۹ - ۳تطبیق الگو روی لیستها
دیدیم که میشه روی دادهسازها تطبیق الگو انجام داد، و دادهسازهای لیست هم مستثنی نیستن. در زیر روی اولین آرگومانِ دادهساز ِ میانوند ِ (:)
تطبیقِ الگو میکنیم و مابقی لیست رو نادیده میگیریم:
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
هم معرفی میکنیم که همیشه حداقل یک مقدار داره و مشکلِ لیست خالی رو نداره.