۲۱ - ۱۰تمرینهای فصل
کمی کشش و نرمش
این تمرینها به عنوان دورهای از چیزهایی که تو چند فصلِ آخر یاد گرفتین طراحی شدن. این تمرینها اکثراً با کُدهای واقعی درست شدن، ولی یه کم ساده شدن تا تمرینهای مجزایی باشن. اینطوری میشه تمرینهای متمرکز روی 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 لازمتون میشه.