۵ - ۸تمرین‌های فصل

دونستن کافی نیست، باید انجام بدیم.

تستی

۱.

کدوم گزینه برای مقداری با تایپِ ‏‎[a]‎‏ صحت داره؟

a)

لیستی از حروف الفبا

b)

لیستی از لیست‌ها

c)

لیستی که همه‌ی المان‌هاش از یه تایپِ ‏‎a‎‏ اند

d)

لیستی که همه‌ی المان‌هاش تایپ‌های متفاوتی دارن

۲.

یه تابع با تایپِ ‏‎[[a]] -> [a]‎‏ می‌تونه

a)

لیستی از ‏‎String‎‏ به عنوانِ آرگومان بگیره

b)

یک حرف رو به یه ‏‎String‎‏ تبدیل کنه

c)

یه ‏‎String‎‏ رو به یه لیستی از ‏‎String‎‏ تبدیل کنه

d)

دو آرگومان بگیره

۳.

یه تابع با تایپِ ‏‎[a] -> Int -> a‎‏

a)

یک آرگومان می‌گیره

b)

یک المان با تایپ ‏‎a‎‏ از یه لیست برمی‌گردونه

c)

باید یه مقدار ‏‎Int‎‏ برگردونه

d)

کاملاً تَخیُلی‌ه

۴.

یه تابع با تایپِ ‏‎(a, b) -> a‎‏

a)

یه لیست می‌گیره و یه مقدارِ ‏‎Char‎‏ برمی‌گردونه

b)

صفر آرگومان داره

c)

یه مقدارِ توپل می‌گیره و مقدار اولِ‌ش رو برمی‌گردونه

d)

لازم داره که ‏‎a‎‏ و ‏‎b‎‏ تایپ‌های متفاوتی داشته باشن

تایپ رو تشخیص بدین

برای توابع زیر، تایپِ مقدار مشخص‌شده رو تعیین کنید. پیشنهاد می‌کنیم در یه فایل بنویسین و بعد در GHCi بارگذاری کنین. به احتمالِ زیاد وقتی فایل رو بارگذاری می‌کنین، به خاطرِ محدودیت مونومورفیسم، تایپ‌هاتون پلی‌مورفیسم‌ای که انتظار دارین رو نداشته باشن. منظور از محدودیت مونومورفیسم اینه که به طور پیش‌فرض تعاریف سطح بالا، معین‌ترین تایپِ ممکن رو می‌گیرن. اگه فایل‌تون رو مثل زیر شروع کنین، مشکل برطرف میشه:

{-# LANGUAGE NoMonomorphismRestriction #-}

module DetermineTheType where

-- مثال ساده
example = 1

اگه توسعه ِ ‏‎NoMonomorphismRestriction‎‏ رو نمی‌نوشتین، ‏‎example‎‏ تایپِ‌ش، بجای ‏‎Num a => a‎‏، میشد ‏‎Integer‎‏. سعی کنین پلی‌مورفیک‌ترین تایپی که بیانیه‌های زیر می‌تونن داشته باشن رو تشخیص بدین.

۱.

همه‌ی توابعِ زیر جواب میدن. مقدار و تایپِ خروجیِ این اعمالِ توابع رو بدست بیارین.

a)

(* 9) 6

b)

head [(0,"doge"),(1,"kitteh")]

c)

head
  [ (0 :: Integer,"doge")
  , (1,"kitteh")
  ]

d)

if False then True else False

e)

length [1, 2, 3, 4, 5]

f)

(length [1, 2, 3, 4]) > (length "TACOCAT")

۲.

داریم

x = 5
y = x + 5
w = y * 10

تایپِ ‏‎w‎‏ چیه؟

۳.

داریم

x = 5
y = x + 5
z y = y * 10

تایپِ ‏‎z‎‏ چیه؟

۴.

داریم

x = 5
y = x + 5
f = 4 / y

تایپِ ‏‎f‎‏ چیه؟

۵.

داریم

x = "Julie"
y = " <3 "
z = "Haskell"
f = x ++ y ++ z

تایپِ ‏‎f‎‏ چیه؟

کامپایل میشه؟

بگین در صورتِ وجود، کدوم یکی از بیانیه‌های زیر باعث میشن کامپایلر فِف کنه (نکته:‌ یعنی خطا بده). دلیل‌ِش رو پیدا کنید، بعد هم سعی کنین درست‌ش کنین.

۱.

bigNum = (^) 5 $ 10
wahoo = bigNum $ 10

۲.

x = print
y = print "woohoo!"
z = x "hello world"

۳.

a = (+)
b = 5
c = b 10
d = c 200

۴.

a = 12 + b
b = 10000 * c

متغیرِ تایپ یا نوع‌ساز ِ معیَن؟

۱.

در تایپ سیگنچرهای داده شده، هر کدوم از عضوهای اونها رو طبقه‌بندی کنین (از بینِ: متغیر تایپ ِ کاملاً پلی‌مورفیک، متغیر تایپ ِ پلی‌مورفیکِ محدود، یا نوع‌ساز ِ معین).

f :: Num a => a -> b -> Int -> Int
--           [0]  [1]   [2]    [3]

اینجا جواب میشه:‌ پلی‌مورفیک ِ محدود (‏‎Num‎‏) برای ‏‎[0]‎‏، تماماً پلی‌مورفیک برای ‏‎[1]‎‏، و نوع‌ساز ِ معین برای ‏‎[2]‎‏ و ‏‎[3]‎‏.

۲.

اعضای این تایپ سیگنچر رو مثل بالا طبقه‌بندی کنید.

f :: zed -> Zed -> Blah

۳.

اعضای این تایپ سیگنچر رو طبقه‌بندی کنید.

f :: Enum b => a -> b -> C

۴.

اعضای این تایپ سیگنچر رو طبقه‌بندی کنید.

f :: f -> g -> C

تایپ سیگنچر بنویسین

تایپ سیگنچر ِ بیانیه‌های زیر رو بنویسید. می‌تونین از استنتاج تایپ ِ GHCi برای چک کردنِ جواب‌تون استفاده کنید،‌ فقط ممکنه یه ذره جوابِ‌ش فرق کنه (به خاطر پلی‌مورفیسم و غیره).

۱.

هنوز این گرامر رو توضیح ندادیم،‌ ولی هم تو فصلِ ۲ و هم به عنوان جواب یکی از تمرین‌های فصل ۴ این گرامر رو دیدین. با این گرامر میشه به لطفِ تطبیقِ الگو، یکی از المان‌های لیست رو در آورد.

functionH ::
functionH (x:_) = x

۲.

functionC ::
functionC x y =
  if (x > y) then True else False

۳.

functionS ::
functionS (x, y) = y

از روی تایپ، تابع بنویسین

با توجه به اطلاعاتی که تایپ‌ها بِهِتون میدن، یه تابع براشون بنویسین. صورتِ مسئله تعداد تابع‌های ممکن برای هر تایپ رو هم میگه. البته تعاریفی که فقط از لحاظ گرامری فرق دارن (مفهوم یکسان دارن) حساب نمیان. مثلاً اگه یه تعریف تابع رو بار دوم با همون مفهوم، ولی با لاندای بی‌نام بنویسین،‌ دو تعریف حساب نمیشه.

برای درک بهتر،‌ یه نمونه حل می‌کنیم. داریم:

myFunc :: (x -> y)
       -> (y -> z)
       -> c
       -> (a, x)
       -> (a, z)
myFunc xToY yToZ _ (a, x) = undefined

بالا یه تابع داریم که چهارتا آرگومان می‌گیره، جوابش هم از تایپِ ‏‎(a,z)‎‏ ِه. اینطور که معلومه، آرگومانِ ‏‎c‎‏ هیچ‌جا از جواب نیست، کاری هم نمیشه باهاش کرد، پس ما هم با استفاده از یه خطِ تیره (‏‎_‎‏) بی‌خیال‌ِش شدیم. دو آرگومانِ تابعی رو با توجه به تایپِ‌شون نامگذاری کردیم،‌ اون توپل رو هم، تطبیقِ الگو کردیم. تنها راه برای رسیدن به تایپِ ‏‎z‎‏ از روی تایپ ‏‎x‎‏ (المان دوم توپل)،‌ استفاده از هر دو تابعی‌ه که در اختیار داریم. اگه تعریف زیر رو امتحان کنیم:

myFunc xToY yToZ _ (a, x) =
  (a, (xToY x))

پیغام خطا می‌گیریم که تایپِ مورد انتظار ‏‎z‎‏ بوده، ولی تایپ واقعی ‏‎y‎‏ شده. راهِ‌مون درسته،‌ فقط ناقص رفتیم! نتیجتاً، تابع زیر باید کار می‌کنه:

myFunc :: (x -> y)
       -> (y -> z)
       -> c
       -> (a, x)
       -> (a, z)
myFunc xToY yToZ _ (a, x) =
  (a, (yToz (xToY x)))

۱.

فقط یه تابع برای تایپ زیر میشه تعریف کرد که بدون گیر کردن تو یه حلقه‌ی بینهایت جواب بده.

i :: a -> a
i = undefined

۲.

فقط یه تعریف وجود داره که کار کنه.

c :: a -> b -> a
c = undefined

۳.

با توجه به تعادل آلفا، آیا ‏‎c''‎‏ و ‏‎c‎‏ ِ بالا یکی اند؟

c'' :: b -> a -> b
c'' = ?

۴.

فقط یه تعریف وجود داره که کار کنه.

c' :: a -> b -> b
c' = undefined

۵.

جوابهای زیادی داره،‌ که حداقل دو تا رو تو فصل‌های قبلی دیدین.

r :: [a] -> [a]
r = undefined

۶.

فقط یه تعریف.

co :: (b -> c) -> (a -> b) -> a -> c
co = undefined

۷.

فقط یه تعریف.

a :: (a -> c) -> a -> a
a = undefined

۸.

فقط یه تعریف.

a' :: (a -> b) -> a -> b
a' = undefined

درست کن

یکی پیدا نمیشه این کُدِ بیچاره رو درست کنه؟ حواس‌ِتون به کوچیک/بزرگ بودنِ حروف، پرانتزها، و توگذاری باشه.

۱.

module sing where

fstString :: [Char] ++ [Char]
fstString x = x ++ " in the rain"

sndString :: [Char] -> Char
sndString x = x ++ " over the rainbow"

sing = if (x > y)
       then fstString x
       or sndString y
where x = "Singin"
      x = "Somewhere"

۲.

حالا که درست شد،‌ با یه تغییر کوچک کاری کنین که اون یکی آواز رو بخونه*. خوش‌شانس باشین جفت آهنگها رو زبون‌تون گیر می‌کنن!

*

م. اسم انتخابیِ تابع (‏‎sing‎‏) به معنای آواز خوندن‌ِه، منظور از دو آهنگ هم یکی آهنگِ Singin’ in the Rain و یکی آهنگ Somewhere Over the Rainbow ِه.

۳.

-- arith3broken.hs
module Arith3Broke where

main :: IO ()
Main = do
  print 1 + 2
  putStrLn 10
  print (negate -1)
  print ((+) 0 blah)
  where blah = negate 1

تایپ-وان-دو

این لغت* ایده‌ی فیلیپ رایت ِه. دستت درد نکنه!

اینجا هدفِ اصلی جور کردن جملات با تایپ‌هاست. اینجور تمرین مشابه کار کردن با کد هسکلِ واقعی می‌مونه، برای تقویت کدنویسیِ روزمره خوبه.

*

م. بازی با لغت تکواندو (Taekwondo و Type-Kwon-Do)...

ما تایپ رو با جملات تهی (تعریف شده با ‏‎undefined‎‏) تأمین می‌کنیم. تهی و تعریف نشده رو بعداً‌ کامل توضیح میدیم. تو این تمرین محتویاتِ جملات بی‌اهمیت اند. فقط تعاریفِ ارائه شده و اونهایی که ‏‎Prelude‎‏ به صورت پیش‌فرض در اختیار میذاره رو لازم دارین (مگه غیرش گفته شده باشه). باید تنها با ویرایشِ ‏‎???‎‏ ها، تایپچکر رو پاس کنید.

برای واضح‌تر شدنِ صورت سؤال، اینجا یه مثالِ حل‌شده آوردیم. اگه داده‌ی مسئله این باشه:

data Woot

data Blah

f :: Woot -> Blah
f = undefined

g :: (Blah, Woot) -> (Blah, Blah)
g = ???

فقط باید ‏‎g‎‏ رو بنویسین؛ آخر کار هم چیزی قابل محاسبه نمیشه. ولی مهم نیست،‌ اینجا فقط کافیه کُدتون تایپچک بشه. جواب‌تون رو می تونین با استعلام و استنتاج تایپ بررسی کنید. اگر هم دقت کنین، اینجا یه کَلَک زدیم تا تایپ‌های بدون مقدار و‌ فقط برای تایپ سیگنچرها تعریف کنیم. یه جوابِ درست برای این مثال:

g :: (Blah, Woot) -> (Blah, Blah)
g (b, w) = (b, f w)

پس فقط کافیه ‏‎???‎‏ ها رو پر کنین.

لزوماً همه‌ی جملاتِ کد برای جواب لازم نیستن.

۱.

f :: Int -> String
f = undefined

g :: String -> Char
g = undefined

h :: Int -> Char
h = ???

۲.

data A
data B
data C

q :: A -> B
q = undefined

w :: B -> C
w = undefined

e :: A -> C
e = ???

۳.

data X
data Y
data Z

xz :: X -> Z
xz = undefined

yz :: Y -> Z
yz = undefined

xform :: (X, Y) -> (Z, Z)
xform = ???

۴.

munge :: (x -> y)
      -> (y -> (w, z))
      -> x
      -> w
munge = ???