۴ - ۶من رو بول بزن
تایپ Bool از Prelude میاد، و همونطور که دیدیم، یک تایپِ جمع با دو دادهساز ِه:
data Bool = False | Trueاین تعریف، یک نوعداده با نوعسازو ِBool درست میکنه. نوعسازها در تایپ سیگنچرها نوشته میشن، و نه تویِ بیانیههای سطحِ جملهای.ونوعسازو ِBool هیچ آرگومانی نمیگیره (بعضی نوعسازها میگیرن). تعریفِ بالا دو تا دادهساز هم ایجاد میکنه، True و False. هر تابعی که مقادیرِ تایپ Bool قبول میکنه، حتماً باید هر دو حالتِ True یا False رو در نظر بگیره؛ در تایپِ تابع نمیشه تعیین کرد که فقط یک مقدار رو قبول کنه. اگه بخوایم تعریفِ بالا رو به کلام بگیم، اینطور میشه: تایپ Bool با دو مقدارِ True یا False ارائه میشه.
یادتون باشه که تایپ هر مقداری رو میشه از GHCi پرسید، مثلِ توابع:
Prelude> :t True
True :: Bool
Prelude> :t "Julie"
"Julie" :: [Char]یه کم با Bool سرگرم شیم. اول not رو دوره کنیم:
Prelude> :t not
not :: Bool -> Bool
Prelude> not True
FalseTrue و False رو با حرفِ اولِ بزرگ مینویسیم، چون دادهساز اند. اگه not رو به اونها بدونِ حرفِ بزرگ اعمال کنین چی میشه؟
یه چیز دیگه رو امتحان کنیم، یه ذره پیچیدهتر:
Prelude> let x = 5
Prelude> not (x == 5)
False
Prelude> not (x > 7)
Trueمیدونیم که توابعِ قیاس به مقادیر Bool ساده میشن، پس میتونن آرگومانِ not بشن.
حالا عملگرهای میانوند ِمنطق بولی رو نگاه کنیم، و ببینیم چطور میتونیم از Bool و این توابع استفاده کنیم.
اول از همه، (&&) اوپراتورِ میانوندو ِعطف ِبولیه. مثال اول رو اینطور میخونیم، true و true.
Prelude> True && True
True
Prelude> (8 > 4) && (4 > 5)
False
Prelude> not (True && True)
Falseعملگر میانوند برای فصل ِبولی، (||) ِه. پس خطِ اول false یا true خونده میشه:
Prelude> False || True
True
Prelude> (8 > 4) || (4 > 5)
True
Prelude> not ((8 > 4) || (4 > 5))
Falseبا دستور i: در GHCi، میتونیم اطلاعاتِ نوعدادههایی که در گستره هستن رو نگاه کنیم (اگه در گستره نباشن، باید ماژولشون رو وارد کنیم). گستره یعنی جاهایی که یک اسمِ تعریف شده برای یه بیانیه اعتبار داره. وقتی یه چیزی در گستره هست، یعنی میشه از اون بیانیه به واسطهی اسمش استفاده کرد؛ حالا یا به خاطرِ اینکه توی تابع یا ماژولِمون تعریف شده، یا به خاطر اینکه وارد شده. پس در برنامهمون مرئیِه. همهی چیزهایی که در Prelude ِهسکل درست شدن، خودبهخود وارد میشن و در گستره قرار میگیرن. فعلاً فقط همینها رو لازم داریم و لازم نیست همهی تابعهامون رو از اول بنویسیم.
تمرینها: مشکلات رو پیدا کن
بعضی از کدهای زیر ایراد دارن – اصلاً کامپایل نمیشن! میدونین چی کار کنین.
۱.
not True && true۲.
not (x = 6)۳.
(1 * 2) > 5۴.
[Merry] > [Happy]۵.
[1, 2, 3] ++ "look at me!"شروط با if-then-else
هسکل، دستورِ if نداره (if statement)، ولی بیانیهی if داره (if expression). یه گرامر داخلیه، که با تایپِ Bool کار میکنه.
Prelude> let t = "Truthin'"
Prelude> let f = "Falsin'"
Prelude> if True then t else f
"Truthin'"بیانیهی if True به True محاسبه میشه، پس t رو برمیگردونیم.
Prelude> if False then t else f
"Falsin'"
Prelude> :t if True then t else f
if True then "Truthin'" else "Falsin'"
:: [Char]و if False به False ساده میشه، پس مقدار else رو برمیگردونیم. تایپِ کلِ بیانیه String (یا [Char]) ِه، همون تایپی که تایپِ مقدارِ نهاییِ بیانیهست.
ساختار اینطوریه:
If CONDITION
then EXPRESSION_A
else EXPRESSION_Bاگه شرط یا CONDITION (که حتماً باید به یه Bool ساده بشه) مقدارِ True داشته باشه، EXPRESSION_A نتیجه میشه، در غیر اینصورت، EXPRESSION_B.
میشه به بیانیههای if به چشمِ یه راهی برای انتخابِ بین دو مقدار نگاه کرد. تنها محدودیتی که برای بیانیههای جلوی if وجود داره، اینه که باید به یه مقدارِ Bool ساده بشن. تایپِ بیانیههای جلوی then و else هم باید منطبق باشن، مثل زیر:
Prelude> let x = 0
Prelude> let a = "AWESOME"
Prelude> let w = "wut"
Prelude> if (x + 1 == 1) then a else w
"AWESOME"مراحل ساده شدنش از این قراره:
-- داریم:
x = 0
if (x + 1 == 1) then "AWESOME" else "wut"
-- صفر است x
if (0 + 1 == 1) then "AWESOME" else "wut"
-- اون جمع رو ساده میکتیم تا ببینیم
-- آیا برابر با 1 هست یا نه
if (1 == 1) then "AWESOME" else "wut"
-- آیا 1 برابر 1 هست؟
if True then "AWESOME" else "wut"
-- انتخاب کن Bool یکی رو براساس مقدار
"AWESOME"
-- !حلهولی این کار نمیکنه:
Prelude> let dog = "adopt a dog"
Prelude> let cat = "or a cat"
Prelude> let x = 0
Prelude> if x * 100 then dog else cat
<interactive>:15:7:
No instance for (Num Bool) arising
from a use of ‘*’
In the expression: (x * 100)
In the expression:
if (x * 100)
then "adopt a dog"
else "or a cat"
In an equation for ‘it’:
it = if (x * 100)
then "adopt a dog"
else "or a cat"دایل این خطا، تایپِ شرطیِه که به بیانیهی if دادیم، Num a => a، که Bool نیست و تایپکلاسِ Num هم برای Bool تعریف نشده. سادهتر بگیم، (x * 100) به یه عدد ساده میشه و اعداد مقادیر واقعی نیستن. اگه یه چیزی مثل x * 100 == 0 یا x * 100 == 9001 بود، اون موقع کار میکرد، چون بررسیِ یه تساوی بود و به یه Bool ساده میشد.
مثال زیر یه تابعه که از یه مقدار Bool در بیانیهی if استفاده میکنه:
-- greetIfCool1.hs
module GreetIfCool1 where
greetIfCool :: String -> IO ()
greetIfCool coolness =
if cool
then putStrLn "eyyyyy. What's shakin'?"
else
putStrLn "pshhhh."
where cool = coolness == "downright frosty yo"اگه تو REPL چک کنین، چنین چیزی میشه:
Prelude> :l greetIfCool1.hs
[1 of 1] Compiling GreetIfCool1
Ok, modules loaded: GreetIfCool1.
Prelude> greetIfCool "downright frosty yo"
eyyyyy. What's shakin'?
Prelude> greetIfCool "please love me"
pshhhh.اگه میخواستین، میشد cool ِداخل greetIfCool رو، بجای یه مقدار که مستقیماً با آرگومانِ تابع تعریف شده، به عنوان یه تابع بنویسین. اینطوری:
-- greetIfCool2.hs
module GreetIfCool2 where
greetIfCool :: String -> IO ()
greetIfCool coolness =
if cool coolness
then putStrLn "eyyyyy. What's shakin'?"
else
putStrLn "pshhhh."
where cool v = v == "downright frosty yo"