۲ - ۱۰کلیدواژههای let و where
در کد هسکل از let و where برای معرفیِ بخشهایی از یک بیانیه، زیاد استفاده میشن، و خیلی هم شبیهِ هم هستن. برای استفادهی بهجا از هر کدوم، کمی تمرین لازمه.
تفاوتشون اینجاست که let یک بیانیه یا expression رو معرفی میکنه، پس هر جایی که بشه بیانیه نوشت قابل جاگذاریه. ولی where یک تعریف یا declaration ِه، و به سازهیِ گرامری ِاَطرافش مقیّده.
با یه مثال از where شروع میکنیم:
-- FunctionWithWhere.hs
module FunctionWithWhere where
printInc n = print plusTwo
where plusTwo = n + 2و اگه بیاریمِش تو REPL:
Prelude> :l FunctionWithWhere.hs
[1 of 1] Compiling FunctionWithWhere ...
Ok, modules loaded: FunctionWithWhere.
Prelude> printInc 1
3
Prelude>و حالا همون تابع رو مینویسیم، اما این دفعه با let:
-- FunctionWithLet.hs
module FunctionWithLet where
printInc2 n = let plusTwo = n + 2
in print plusTwoهر وقت let بعدِش in بیاد، میشه یک بیانیهی let یا let expression. تابعِ دوم در REPL:
Prelude> :l FunctionWithLet.hs
[1 of 1] Compiling FunctionWithLet ...
Ok, modules loaded: FunctionWithLet.
Prelude> printInc2 3
5اگه FunctionWithLet رو بعد از FunctionWithWhere بارگذاری کردین، REPL قبلی رو تخلیه کرده:
Prelude> :l FunctionWithWhere.hs
[1 of 1] Compiling FunctionWithWhere ...
Ok, modules loaded: FunctionWithWhere.
Prelude> printInc 1
3
Prelude> :l FunctionWithLet.hs
[1 of 1] Compiling FunctionWithLet ...
Ok, modules loaded: FunctionWithLet.
Prelude> printInc2 10
12
Prelude> printInc 10
<interactive>:6:1:
Not in scope: ‘printInc’
Perhaps you meant ‘printInc2’ (line 4)
-- ^---------------------------^
-- بوده ‘printInc2’ م. شاید منظورتونوقتی با دستورِ :load فایل FunctionWithLet.hs رو بارگذاری کردین، GHCi هر چیزی که قبل از اون تعریف یا بارگذاری کرده بودین رو تخلیه کرد. به همین خاطر تابعِ printInc از گسترهش خارج شده بود. منظور از گستره حوزهای از کده، که انقیاد ِ یک متغیر اونجا اعمال میشه.
این یکی از محدودیتهای دستور :load در GHCi ِه. وقتی شروع به ساخت پروژههای بزرگتر کنیم که لازم باشه چندتا ماژول رو در گستره داشته باشین، به جای GHCi از یک ابزار مدیریتِ پروژه به نام Stack استفاده میکنیم.
تمرینها: کد ذهنی
وقتِ تمرینه. اول جواب بیانیههای زیر رو ذهنی پیدا کنید، بعد با REPL چک کنید:
۱.
let x = 5 in x۲.
let x = 5 in x * x۳.
let x = 5; y = 6 in x * y۴.
let x = 3; y = 1000 in x + 3الان این چندتا بیانیهی let رو تو REPL تایپ کردین. حالا میخوایم یه فایل باز کنیم و چندتا بیانیهی let رو با استفاده از تعاریف where بازنویسی کنیم. برای مقادیری که قید میکنین، یه اسم هم باید اختصاص بدین (که اگه بخواین میشه تک حرفی باشن). برای مثال:
-- کار میکنه GHCi این تو
let x = 5; y = 2 in x * yمیشه با where بازنویسی کرد:
-- این رو توی فایل بنویسین
mult1 = x * y
where x = 5
y = 6اونطوری تساویها رو ردیف کردن، یه انتخاب سلیقهایه. فقط کافیه چنین توگذاری ای رعایت بشه، لازم نیست مساویها زیر هم باشن. دقت کنین که ما یه اسم تعریف کردیم (mult1)، و با استفاده از اون اسم در REPL به مقدارش دسترسی پیدا میکنیم:
Prelude> :l practice.hs
[1 of 1] Compiling Main
Ok, modules loaded: Main.
*Main> mult1
30توجه کنید که prompt از Prelude به *Main تغییر میکنه، این نشون میده یه ماژول به اسم Main رو بارگذاری کردین.
با where بازنویسی کنید:
۱.
let x = 3; y = 1000 in x * 3 + y۲.
let y = 10; x = 10 * 5 + y in x * 5۳.
let x = 7
y = negate x
z = y * 10
in z / x + yاسم فایلی که انتخاب میکنین مهم نیست، فقط باید پسوندِ .hs داشته باشه.