۲۶ - ۱۰الگوهای انکارپذیر و انکارناپذیر
در تطبیق الگو، دو نوع الگو وجود داره: انکارپذیر و انکارناپذیر. یه الگو ِ انکارناپذیر الگو ایه که هیچ وقت در تطبیق شکست نمیخوره. اما الگو ِ انکارپذیر احتمالِ شکست داره.
refutable :: Bool -> Bool
refutable True = False
refutable False = True
irrefutable :: Bool -> Bool
irrefutable x = not x
oneOfEach :: Bool -> Bool
oneOfEach True = False
oneOfEach _ = True
حواستون باشه که انکارپذیر بودن یا نبودن برای الگو ِه، نه خودِ تابع. تابعِ refutable
انکارپذیر ِه، به خاطرِ اینکه هر حالتش انکارپذیر ِه؛ هر حالت ممکنه مقداری بگیره که نتونه منطبق بشه. در مقابل، irrefutable
الگو ِ انکارناپذیر داره؛ یعنی الگوش به انطباق بر روی یه مقدارِ بخصوص اتکا نمیکنه.
در موردِ oneOfEach
(م. به معنای یکیازهرکدوم)، الگو ِ اول انکارپذیر ِه، چون روی دادهساز ِ True
تطبیق الگو میکنه. irrefutable
و انطباق ِ دومِ oneOfEach
انکارناپذیر اند چون لازم نیست به داخلِ مقداری که بهش اعمال شدن نگاه کنن.
البته اینکه تطبیق الگو ِ دوم در oneOfEach
انکارناپذیر ِه، خیلی مفهومی در هسکل نداره، چون به هر حال برای تطبیق روی حالتِ اول، داده ِ ورودی باید بررسی بشه.
تابعِ irrefutable
برای هر دو مقدارِ Bool
کار میکنه، چون هیچ کدوم از اون مقادیر رو برای تطبیق مشخص نکرده. الگو ِ انکارناپذیر رو میشه الگویی دونست که هیچ وقت در تطبیق شکست نمیخوره. اگه یه الگو ِ انکارناپذیر قبل از یه الگو ِ انکارپذیر بیاد، الگو ِ انکارپذیر هیچ وقت احضار نمیشه.
این تابع کوچولو رو در یکی از فصلهای قبلی دیدیم؛ میخوایم خیلی سریع یه چیزی رو باهاش نشون بدیم:
isItTwo :: Integer -> Bool
isItTwo 2 = True
isItTwo _ = False
با Bool
، ترتیبِ تطبیق روی True
و False
فرقی نداره، اما در مواردی مثلِ isItTwo
که یک حالت بخصوص و مشخصه، و حالتِ دیگه یه حالتِ "در غیر اینصورت" یا otherwise ِه، ترتیب قطعاً مهمه. میتونین ترتیبِ بیانیههای isItTwo
رو عوض کنین تا ببینین چی میشه، ولی احتمالاً واضحه.
الگوهای تنبل
الگوهای تنبل هم انکارناپذیر اند.
strictPattern :: (a, b) -> String
strictPattern (a,b) = const "Cousin It" a
lazyPattern :: (a, b) -> String
lazyPattern ~(a,b) = const "Cousin It" a
با اون مدک یا تیلدا، تطبیق الگو تنبل میشه. البته از اون جایی که الگو رو انکارناپذیر میکنه، دیگه نمیشه ازش برای تمیز دادن حالتهای یه تایپِ جمع استفاده کرد – به دردِ باز کردنِ تایپهای ضربی میخوره که ممکنه استفاده نشن.
Prelude> strictPattern undefined
*** Exception: Prelude.undefined
Prelude> lazyPattern undefined
"Cousin It"
همونطور که اینجا میبینیم، تابعی که الگو ِ تنبل داشت تهی رو اجبار نکرد، چون const
اصلاً a
ِ توپل رو لازم نداشت. بطورِ پیشفرض، تهی قبل از محاسبهی بدنه ِ تابع اجبار میشد، دلیل اصلیش هم مصرف حافظه و عملکرد ِ قابلِ پیشبینیتَرِه.