۲ - ۱۰کلیدواژههای 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
داشته باشه.