۴ - ۶من رو بول بزن
تایپ 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
False
True
و 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"