۹ - ۳تطبیق الگو روی لیستها
دیدیم که میشه روی دادهسازها تطبیق الگو انجام داد، و دادهسازهای لیست هم مستثنی نیستن. در زیر روی اولین آرگومانِ دادهساز ِ میانوند ِ (:) تطبیقِ الگو میکنیم و مابقی لیست رو نادیده میگیریم:
Prelude> let myHead (x : _) = x
Prelude> :t myHead
myHead :: [t] -> t
Prelude> myHead [1, 2, 3]
1برعکسش هم میشه انجام داد:
Prelude> let myTail (_ : xs) = xs
Prelude> :t myTail
myTail :: [t] -> [t]
Prelude> myTail [1, 2, 3]
[2, 3]برای استفاده از این توابع باید حواسمون رو جمع کنیم. نه myHead و نه myTail حالتی که ورودی یه لیستِ خالی باشه رو در نظر نگرفتن. اگه با لیست خالی امتحانشون کنیم، نمیتونن تطبیقِ الگو کنن:
Prelude> myHead []
*** Exception:
Non-exhaustive patterns
in function myHead
Prelude> myTail []
*** Exception:
Non-exhaustive patterns
in function myTailمشکل اینه که تایپِ [a] -> a برای myHead در واقع فریبندهست، چون تایپِ [a] تضمینی نداره که حتماً یه مقدارِ a داشته باشه. داشتنِ حداقل یک مقدار رو هم تضمین نمیکنه، پس myTail ممکنه شکست بخوره. یک راه، اضافه کردنِ فرمان پایه هست:
myTail :: [a] -> [a]
myTail [] = []
myTail (_ : xs) = xsبا این تعریف چنین جوابهایی میده:
Prelude> myTail [1..5]
[2,3,4,5]
Prelude> myTail []
[]استفاده از Maybe
راه بهتر برای چنین مواردی، استفاده از نوعداده ای به اسمِ Maybe ِه. توضیح کاملِ Maybe رو برای یکی دیگه از فصلها حفظ میکنیم، ولی اینجا یه درک کلی از کاربردش پیدا میکنین. ایده اینه که حالت شکست رو صراحتاً بیان کنیم؛ با طولانی و پیچیدهتر شدنِ برنامهها این روش خیلی کاربردی میشه.
با استفاده از Maybe برای myTail مثال میزنیم. بجای یه فرمانِ پایه که لیست خالی برگردونه، تابعی که با Maybe نوشته شده Nothing برمیگردونه. همونطور که میبینیم، نوعداده ِ Maybe میتونه یکی از دو مقدارِ Nothing یا Just a رو داشته باشه:
Prelude> :info Maybe
data Maybe a = Nothing | Just aبازنویسیِ myTail با Maybe سادهست:
safeTail :: [a] -> Maybe [a]
safeTail [] = Nothing
safeTail (x:[]) = Nothing
safeTail (_:xs) = Just xsدقت کنین که تابع هنوز روی لیست تطبیق الگو انجام میده. فرمان پایه ِ دوم safeTail (x:[]) = Nothing رو اینطور نوشتیم که اگه ورودی یه لیست تک عضوی بود، خالی بودن دُم ِش رو نشون بده. اگه این فرمان رو نمینوشتیم، جواب برای لیستی که فقط یه مقدارِ سَر داره، Just [] میشد. چند دقیقه وقت بذارین و باهاش بازی کنین تا طرز کارش رو ببینین. بعد هم سعی کنین تابعِ myHead رو با استفاده از Maybe بازنویسی کنین.
جلوتر در کتاب نوعداده ِ NonEmpty هم معرفی میکنیم که همیشه حداقل یک مقدار داره و مشکلِ لیست خالی رو نداره.