۲۱ - ۱۰تمرینهای فصل
کمی کشش و نرمش
این تمرینها به عنوان دورهای از چیزهایی که تو چند فصلِ آخر یاد گرفتین طراحی شدن. این تمرینها اکثراً با کُدهای واقعی درست شدن، ولی یه کم ساده شدن تا تمرینهای مجزایی باشن. اینطوری میشه تمرینهای متمرکز روی Traversable
و Reader
(که هردوشون هم یه کم سختاند) داشته باشیم.
اول چندتا داده ِ تمرینی درست میکنیم؛ توی اون برنامهی اصلی که این کُد رو ازش گرفتیم، دادهها از جای دیگه میومدند – مثلاً یه پایگاه داده. فقط چندتا لیست عدد لازم داریم. چندتا تابع هم از Data.Maybe
و Control.Applicative
لازم داریم، که اولِ فایل واردِشون میکنیم. برای سادگی، دادهها ِ لیستمون رو با حروف رایج برای متغیرها تعریف میکنیم.
module ReaderPractice where
import Control.Applicative
import Data.Maybe
x = [1, 2, 3]
y = [4, 5, 6]
z = [7, 8, 9]
مرحلهی بعدی نوشتنِ تابعهاییه که اون لیستها رو به هم زیپ میکنن، و با استفاده از lookup
، مقدارِ منسوب به یه کلید در لیستهای زیپشده رو پیدا میکنن. برای تمرین خوبه که خروجیها قابل پیشبینی باشن، پس پیشنهاد میکنیم اون توابع رو هم بصورت مقادیرِ معین تعریف کنین، و هم بصورت توابعی که میشه به یه متغیر اعمال بشن:
lookup :: Eq => a -> [(a, b)] -> Maybe b
-- رو با هم زیپ کنین، و با y و x
-- .استفاده کنین lookup از تابع 3 کلید
xs :: Maybe Integer
xs = undefined
-- رو با هم زیپ کنین، و با z و y
-- .استفاده کنین lookup از تابع 6 کلید
ys :: Maybe Integer
ys = undefined
-- .برگردونه Nothing خوبه یکیشون مثل این
-- رو با هم زیپ کنین، و با y و x
-- .استفاده کنین lookup از تابع 4 کلید
zs :: Maybe Integer
zs = undefined
-- رو با هم زیپ کنین، و از کلید z و x
-- .استفاده کنین lookup متغیر برای تابع
z' :: Integer -> Maybe Integer
z' n = undefined
حالا میخوایم با استفاده از Applicative
، یه Maybe (,)
از مقادیر درست کنیم. با x1
یه توپل از xs
و ys
، و با x2
یه توپل از ys
و zs
درست کنین. x3
هم طوری تعریف کنین که یه ورودی بگیره و از نتیجهی دو بار اعمال ِ z'
یه توپل بسازه.
x1 :: Maybe (Integer, Integer)
x1 = undefined
x2 :: Maybe (Integer, Integer)
x2 = undefined
x3 :: Integer
-> (Maybe Integer, Maybe Integer)
x3 = undefined
خروجیهاتون باید این شکلی باشن:
*ReaderPractice> x1
Just (6,9)
*ReaderPractice> x2
Nothing
*ReaderPractice> x3 3
(Just 9,Just 9)
بعد چندتا تابع کمکی تعریف میکنیم. با استفاده از uncurry
میشه از مقادیرِ توی یه توپل به عنوانِ آرگومانهای یه تابع استفاده کنیم:
uncurry :: (a -> b -> c) -> (a, b) -> c
-- اون آرگومانِ اول یه تابعه، که
-- .در این مورد میخوایم جمع باشه
-- با تابعِ uncurry میشه summed تابع
-- .جمع به عنوان اولین آرگومانش
summed :: Num c => (c, c) -> c
summed = undefined
حالا یه تابع که یه تابعِ بولیَن رو از روی دوتا تابعِ نیمه اعمالشده لیفت میکنه تعریف میکنیم (شبیهش رو قبلاً دیدیم):
bolt :: Integer -> Bool
-- استفاده کنین. && ، و >3 ، <8 از
bolt = undefined
میخوایم از تابعِ fromMaybe
هم استفاده کنیم، پس یه نگاه بهش بندازیم:
fromMaybe :: a -> Maybe a -> a
یه مقدارِ پیشفرض و یه مقدار Maybe
میگیره. اگه مقدار Maybe
یه Just a
باشه، مقدار a
رو برمیگردونه. اما اگه Nothing
باشه، اون مقدار پیشفرض رو برمیگردونه:
*ReaderPractice> fromMaybe 0 xs
6
*ReaderPractice> fromMaybe 0 zs
0
حالا یه main
سَرِهم میکنیم تا با یه بار صدا زدن، چندتا چیز رو اجرا کنیم:
main :: IO ()
main = do
print $
sequenceA [Just 3, Just 2, Just 1]
print $ sequenceA [x, y]
print $ sequenceA [xs, ys]
print $ summed <$> ((,) <$> xs <*> ys)
print $ fmap summed ((,) <$> xs <*> zs)
print $ bolt 7
print $ fmap bolt z
با اجرای این در REPL باید چنین جوابی بگیرین:
*ReaderPractice> main
Just [3,2,1]
[ [1,4], [1,5], [1,6]
, [2,4], [2,5], [2,6]
, [3,4], [3,5], [3,6] ]
Just [6,9]
Just 15
Nothing
True
[True,False,False]
یه خط دیگه اضافه میکنیم که sequenceA
و Reader
رو به نحو غیرمنتظرهای با هم ترکیب میکنه (این رو به main
اضافه کنین):
print $ sequenceA [(>3), (<8), even] 7
تایپِ sequenceA
اینه:
sequenceA :: (Applicative f, Traversable t)
=> t (f a) -> f (t a)
-- :پس اینجا
sequenceA [(>3), (<8), even] 7
-- t ~ [] و f ~ (->) r
برای Applicative
(منظور همون تابعهاست) یه Reader
داریم، و برای اون لیست یه Traversable
. خیلی کاربردیه. اسمش رو میذاریم sequA
تا باز هم ازش استفاده کنیم:
sequA :: Integral a => a -> [Bool]
sequA m = sequenceA [(>3), (<8), even] m
از اینجا به بعد هم این بیانیه رو:
summed <$> ((,) <$> xs <*> ys)
به اسمِ s'
انقیاد بدین.
خیلی خوب، حالا نوبت شماست. اینها رو توی main
تعریف کنین (اگه بخواین میتونین هرچی بعد از do
نوشته بودین رو پاک کنین – فقط حواستون باشه از print
استفاده کنین تا نتایجِ چیزهایی که دارین اضافه میکنین رو چاپ کنین):
۱.
عملگر ِ عطف منطقی بولین رو روی لیست حاصل از sequA
(اعمال شده به یه مقدارِ دلخواه) فولد کنین.
۲.
sequA
رو به s'
اعمال کنین؛ fromMaybe
لازمتون میشه.
۳.
bolt
رو به ys
اعمال کنین؛ fromMaybe
لازمتون میشه.