۲ - ۶عملگرهای میانوند

توابعِ هسکل در حالت پیش‌فرض، گرامر ِ پیشوندی دارن؛ یعنی تابعی که داره اعمال میشه، بجای وسط، اولِ بیانیه‌ست. با تابعِ ‏‎triple‎‏ این رو دیدیم، بقیه‌ی تابع‌های استاندارد هم، مثل تابع همانی یا ‏‎id‎‏، همینطوراند. این تابع فقط آرگومانش رو برمی‌گردونه:

Prelude> id 1
1

این گرامر ِ پیش‌فرض ِ توابع‌ه، با این حال، همه‌ی توابع پیشوندی نیستند. یه گروهی از عملگرها هستن، مثل عملگرهای عددی (جمع، ضرب، و...)، که در حقیقت تابع‌اند (به آرگومان‌ها اعمال میشن و یه خروجی میدن) ولی به طور پیش‌فرض، میانوند نوشته میشن.

عملگرها در واقع توابعی هستن که میشه به سبکِ میانوندی ازشون استفاده کرد. همه‌ی عملگرها تابع اند؛ ولی همه‌ی تابع‌ها عملگر نیستند. ‏‎triple‎‏ و ‏‎id‎‏ توابعِ پیشوندی اند (عملگر نیستن)، ولی تابعِ ‏‎+‎‏ یک عملگر یا اوپراتورِ میانوند ِه:

Prelude> 1 + 1
2

چندتا عملگر ِ عددی دیگه رو امتحان کنیم:

Prelude> 100 + 100
200
Prelude> 768395 * 21356345
16410108716275
Prelude> 123123 / 123
1001.0
Prelude> 476 - 36
440
Prelude> 10 / 4
2.5

با یه کم تغییر گرامری*، میشه از توابع هم به سبک میانوندی استفاده کرد:

Prelude> 10 `div` 4
2
Prelude> div 10 4
2
*

م. با استفاده از اَکسان گراو. این کاراکتر با آپاستروف فرق داره و توی بیشتر کیبوردها کنار کلید ۱ و بالای tab قرار گرفته.

برعکس‌ش هم ممکنه. اگه عملگرها رو توی پرانتز بذارین، تبدیل به تابعِ پیشوندی میشن:

Prelude> (+) 100 100
200
Prelude> (*) 768395 21356345
16410108716275
Prelude> (/) 123123 123
1001.0

اگه اسم تابع فقط متشکل از حروف و اعداد باشه، به طور پیش‌فرض، پیشوندی میشه؛ و همه‌ی توابع پیشوندی رو نمیشه میانوندی کرد. اما اگه اسم تابع یه علامت باشه*، خودبه‌خود میانوندی میشه، و با پرانتز پیشوندی میشه.

*

م. بقیه‌ی کاراکترها به غیر از حروف و اعداد.

برا عزیزانی که دوست دارن گیر بدن! نمیشه یه تابع پیشوندی رو با اکسان گراو میانوندی کنین، بعد اونو بذارین لای پرانتز تا پیشوندی شه... دلیل اینکه یه همچین چیزی رو میخواین برا ما واضح نیست!

شرکت‌پذیری و تقدم

اگه از ریاضیات یادتون باشه، یه شرکت‌پذیری و تقدم ِ پیش‌فرض برای عملگرهای میانوند، ‏‎(*)‎‏، ‏‎(+)‎‏، ‏‎(-)‎‏ و ‏‎(/)‎‏، وجود داره.

با استفاده از دستور ‏‎:info‎‏ در GHCi، میشه اطلاعاتی مثل شرکت‌پذیری و تقدم ِ توابع و عملگرها رو استعلام کرد. وقتی با ‏‎:info‎‏ درباره‌ی یه تابع یا عملگر می‌پرسین، GHCi تایپِ تابع رو بهتون میگه، و اینکه میانوند هست یا نه، و اگه بود، شرکت‌پذیری و تقدم اون رو هم چاپ می‌کنه. فعلاً اطلاعات راجع به تایپِ تابع و بقیه چیزها رو کنار میذاریم، فقط یه کم از شرکت‌پذیری و تقدم صحبت می‌کنیم.

جواب ‏‎:info‎‏ تو GHCi برای توابعِ ‏‎(*)‎‏، ‏‎(+)‎‏، و ‏‎(-)‎‏ از این قراره (تا این لحظه):

:info (*)
infixl  7   *
-- [1] [2] [3]

:info (+) (-)
infixl 6 +

infixl 6 -

۱.

منظور از ‏‎infixl‎‏، میانوند بودنِ اوپراتوره؛ اون ‏‎l‎‏ ِآخرش هم برای شرکت‌پذیری از left یا چپ‌ه.

۲.

عدد ۷، تقدم رو نشون میده: هر چیزی تقدم‌ش بیشتر باشه، اوّل اعمال میشه. بازه‌ی تقدم از ۰ تا ۹ تعریف شده.

۳.

اسمِ تابعِ میانوند: در این مورد، ضرب.

برای جمع و تفریق هم می‌بینیم که هردوشون عملگر ِ میانوند با شرکت‌پذیری از چپ هستن، و تقدم ۶ دارن.

می‌تونیم به کمک پرانتز، مفهوم شرکت‌پذیری از چپ رو بهتر نشون بدیم. به همراهی این کدها با REPL ادامه بدین:

به خاطر شرکت‌پذیری از چپ، این:

2 * 3 * 4

به این ترتیب حساب میشه:

(2 * 3) * 4

حالا یه مثال برای شرکت‌پذیری از راست:

 Prelude> :info (^)
 infixr  8   ^
-- [1]  [2] [3]

۱.

منظور از ‏‎infixr‎‏، میانوند بودنِ اوپراتوره؛ اون ‏‎r‎‏ ِآخرش هم برای شرکت‌پذیری از right یا راست‌ه.

۲.

عدد ۸ برای تقدم ِه، که هر چقدر بزرگتر باشه، اون اوپراتور اول اعمال میشه. می‌بینید که این عملگر تقدم‌ِش از ضرب (۷) و جمع یا تفریق (۶) بیشتره.

۳.

اسمِ تابعِ میانوند: در این مورد، توان.

با ضرب، اهمیتِ شرکت‌پذیری واضح نبود، چون ضرب کلاً شرکت‌پذیر هست، و جابجاییِ پرانتزها تاثیری روی جواب نمیذاشت. ولی توان شرکت‌پذیر نیست، و گزینه‌ی خوبی برای نشون دادنِ فرق بین شرکت‌پذیری از چپ و شرکت‌پذیری از راست‌ه:

Prelude> 2 ^ 3 ^ 4
2417851639229258349412352
Prelude> 2 ^ (3 ^ 4)
2417851639229258349412352
Prelude> (2 ^ 3) ^ 4
4096

همونطور که می‌بینید، وقتی شرکت‌پذیری از راست داریم، اضافه کردن پرانتز از راست (دسته‌بندی از راست) تاثیری روی جواب نداره. ولی اگه از چپ پرانتز بذاریم، جواب نهایی‌مون فرق می‌کنه.

در کل، اون درکی که از تقدم، شرکت‌پذیری، و تاثیرِ پرانتزها توی ریاضیات دارین، توی هسکل هم صادق‌ه:

 2 + 3 * 4

 (2 + 3) * 4

این دو تا چه فرقی با هم دارن؟ چرا فرق دارن؟

تمرین‌ها: پرانتز و شرکت‌پذیری

در زیر، هر جفتِ تابع‌ها فقط در پرانتزگذاری فرق دارن. با دقت هر کدوم رو بخونین و ببینین آیا پرانتز تغییری در نتیجه‌ی نهایی داره یا نه. جواب‌تون رو توی GHCi چک کنین.

۱.

a)

‏‎8 + 7 * 9‎‏

b)

‏‎(8 + 7) * 9‎‏

۲.

a)

‏‎perimeter x y = (x * 2) + (y * 2)‎‏

b)

‏‎perimeter x y = x * 2 + y * 2‎‏

۳.

a)

‏‎f x = x / 2 + 9‎‏

b)

‏‎f x = x / (2 + 9)‎‏