۲۶ - ۱۰الگوهای انکارپذیر و انکارناپذیر
در تطبیق الگو، دو نوع الگو وجود داره: انکارپذیر و انکارناپذیر. یه الگو ِ انکارناپذیر الگو ایه که هیچ وقت در تطبیق شکست نمیخوره. اما الگو ِ انکارپذیر احتمالِ شکست داره.
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 ِ توپل رو لازم نداشت. بطورِ پیشفرض، تهی قبل از محاسبهی بدنه ِ تابع اجبار میشد، دلیل اصلیش هم مصرف حافظه و عملکرد ِ قابلِ پیشبینیتَرِه.