۷ - ۱۱تمرین‌های فصل

تستی

۱.

یک تابعِ پلی‌مورفیک

a)

وقتی صدا زده میشه، همه چیز رو تبدیل به گوسفند می‌کنه

b)

چند آرگومان داره

c)

یک تایپِ معین داره

d)

بسته به ورودی‌ش، ممکنه به مقادیر با تایپ‌های متنوعی مقرر بشه

۲.

دو تابع به اسم‌های ‏‎f‎‏ و ‏‎g‎‏ به ترتیب با تایپ‌های ‏‎Char -> String‎‏ و ‏‎String -> [String]‎‏ رو در نظر بگیرین. تابع ترکیبی ِ ‏‎g . f‎‏ چه تایپی داره؟

a)

‏‎Char -> String‎‏

b)

‏‎Char -> [String]‎‏

c)

‏‎[[String]]‎‏

d)

‏‎Char -> String -> [String]‎‏

۳.

یک تابعِ ‏‎f‎‏ با تایپِ ‏‎Ord a => a -> a -> Bool‎‏ رو به یه مقدار عددی اعمال می‌کنیم. حالا تایپ‌ش چیه؟

a)

‏‎Ord a => a -> Bool‎‏

b)

‏‎Num -> Num -> Bool‎‏

c)

‏‎Ord a => a -> a -> Integer‎‏

d)

‏‎(Ord a, Num a) => a -> Bool‎‏

۴.

یک تابع با تایپِ ‏‎(a -> b) -> c‎‏:

a)

نیاز به مقادیر با سه تایپِ مختلف داره

b)

یک تابع سطح بالا هست

c)

برای آرگومان اولش باید یه توپل بگیره

d)

پارامترهاش به ترتیبِ حروف الفبا اند

۵.

با تعریفِ زیر برای ‏‎f‎‏، تایپِ ‏‎f True‎‏ چیه؟

f :: a -> a
f x = x

a)

‏‎f True :: Bool‎‏

b)

‏‎f True :: String‎‏

c)

‏‎f True :: Bool -> Bool‎‏

d)

‏‎f True :: a‎‏

کُد بنویسیم

۱.

تابعِ زیر دهگانِ یه آرگومانِ ‏‎Integral‎‏ رو برمی‌گردونه.

tensDigit :: Integral a => a -> a
tensDigit x = d
   where xLast = x `div` 10
         d     = xLast `mod` 10

a)

اول با ‏‎divMod‎‏ بازنویسی‌ش کنین.

b)

آیا تایپِ نسخه‌ی ‏‎divMod‎‏ ِ این تابع با نسخه‌ی اصلی‌ش یکسانه؟

c)

حالا تغییرش بدین تا بجای دهگان، صدگان رو خروجی بده. می‌تونین اینطور شروع کنین (التبه شاید تنها راه نباشه):

hunsD x = d2
    where d = undefined
    ...

۲.

تابع با تایپِ ‏‎a -> a -> Bool -> a‎‏ رو یکبار با بیانیه‌ی case و یکبار هم با گارد تعریف کنین.

foldBool :: a -> a -> Bool -> a
foldBool =
  error
  "Error: Need to implement foldBool!"

۳.

تعریفِ تابع رو بنویسین. دقت کنین که آرگومانِ اول یه تابعِ دیگه‌ست که میشه به مقادیر اعمال بشه. آرگومان دوم هم یه توپل ِه، که میشه ازش برای تطبیق الگو استفاده کرد:

g :: (a -> b) -> (a, c) -> (b, c)
g = undefined

۴.

برای این تمرین، نوشتن نسخه‌ی بی‌نقطه برای کُدِ موجود رو آزمایش می‌کنین؛ که مستلزمِ کمی اطلاعات جدیده. پس توضیحات زیر رو به دقت بخونین.

تایپکلاس‌ها بر مبنای تایپ‌ها خبر میشن. ‏‎Read‎‏ یه تایپکلاس مشابه ‏‎Show‎‏ هست، اما دوگان یا متضاد ِ اونه. در کل، تایپکلاسِ ‏‎‎‏Read چیزِ خوبی نیست که ازش استفاده کنین، ولی این تمرین برای آموزشِ چیزی درباره‌ی تعامل بین تایپکلاس‌ها و تایپ‌ها طراحی شده.

تایپِ تابعِ ‏‎read‎‏ از تایپکلاسِ ‏‎Read‎‏ از این قراره:

read :: Read a => String -> a

متوجه الگویی شدین؟

read :: Read a => String -> a
show :: Show a => a -> String

کد زیر رو تو یه فایلِ منبع بنویسین. بعد در GHCi بارگذاری و اجراش کنین تا مطمئن بشین دلیل هر جواب رو درک کردین.

-- arith4.hs
module Arith4 where

-- id :: a -> a
-- id x = x

roundTrip :: (Show a, Read a) => a -> a
roundTrip a = read (show a)

main = do
  print (roundTrip 4)
  print (id 4)

۵.

بعد نسخه‌ی بی‌نقطه از ‏‎roundTrip‎‏ رو بنویسین (نکته: منظور تعریفِ تابع‌ست، و نه اعمال ِ اون در ‏‎main‎‏).

۶.

برای این تمرین هم به استفاده از ماژول ِ ‏‎Arith4‎‏ ادامه میدیم.

وقتی ‏‎show‎‏ رو به یه مقدار مثلِ ‏‎(1 :: Int)‎‏ اعمال می‌کنیم، تایپِ ‏‎a‎‏ ای که باید یه نمونه از تایپکلاسِ ‏‎Show‎‏ داشته باشه ‏‎Int‎‏ میشه، در نتیجه GHCi هم برای تبدیل عدد ۱ ِما به نوشته، از نمونه ِ ‏‎Show‎‏ که برای ‏‎Int‎‏ تعریف شده استفاده می‌کنه.

از طرف دیگه، ‏‎read‎‏ انتظارِ یک آرگومانِ ‏‎String‎‏ داره تا یه ‏‎a‎‏ برگردونه. اون ‏‎String‎‏ که آرگومانِ اولِ ‏‎read‎‏ هست، هیچ اطلاعاتی درباره‌ی تایپِ نتیجه‌ی تابع نمیده. ولی تابعِ ‏‎roundTrip‎‏ (با تایپ سیگنچر ای که الان داره) تایپِ خروجی‌ش رو میدونه، چون با ورودی‌ش یکسانه. یعنی تایپی که ورودی به ‏‎show‎‏ هست باید خروجیِ ‏‎read‎‏ هم باشه.

کاری که شما باید انجام بدین اینه که تایپِ ‏‎roundTrip‎‏ در ‏‎Arith4‎‏ رو به ‏‎(Show a, Read b) => a -> b‎‏ تغییر بدین. حالا چطور میشه به GHCi گفت که کدوم نمونه از ‏‎Read‎‏ رو برای ‏‎String‎‏ خبر کنه؟ کاری کنین که بیانیه‌ی ‏‎print (roundTrip 4)‎‏ جواب بده. فقط گرامر ِ تعیین تایپ (‏‎::‎‏) و پرانتز (برای تعیین گستره) لازم دارین.