۴ - ۳آناتومی تعریف داده
تعریفِ داده راهی برای تعریفِ تایپهاست.
نوعساز، اسمِ تایپه و با حروفِ بزرگ شروع میشه. وقتی تایپ سیگنچرها رو میخونین یا مینویسین (در سطحِ نوعی ِ کُد هستین)، از اسمِ تایپها، یا نوعسازها استفاده میکنین.
دادهسازها مقادیریاند که داخلِ یک تایپ تعریف میشن. همون مقادیریاند که در سطح جملهای ِ کُد ظاهر میشن، و نه سطح نوعی. منظور از سطح جملهای، مقادیریاند که در خودِ کُد ظاهر میشن، و یا کُد به اونها حساب میشه.
برای آشنایی، و اینکه چطور نوعدادهها درست میشن، با یه تایپِ پایه مثل Bool
شروع میکنیم. تایپِ Bool
، که به نامِ ریاضیدانِ بزرگ، جورج بول و سیستمِ منطق او نامگذاری شده، مقادیر واقعی رو تعریف میکنه. از اونجا که فقط دو مقدارِ واقعی وجود دارن، Bool
هم فقط دو دادهساز داره:
-- Bool تعریفِ
data Bool = False | True
-- [1] [2] [3] [4]
۱.
نوعساز برای تایپِ Bool
. اسمِ تایپ که در تایپ سیگنچرها هم دیده میشه.
۲.
دادهساز برای مقدار False
.
۳.
خط عمودی |
که تعیین کنندهی نوعِ جمع یا فصل منطقی (یا) ِه.
۴.
دادهساز برای مقدارِ True
.
به کلِ اون عبارت میگیم تعریفِ داده. تعریف دادهها میتونن شکلهای دیگه هم داشته باشن – بعضی تایپها عطفِ منطقی (و) دارن (برخلافِ این مثال که فصلِ منطقی ِه)، بعضی نوعسازها و دادهسازها هم آرگومان دارن. با وجودِ این تفاوتها، همهشون نقاطِ مشترکی هم دارن: یکی کلیدواژه ِ data
در اولِ تعریفِه که بعد از اون، نوعساز (یا همون اسمِ تایپ) نوشته میشه، علامت مساوی که نشون میده این یه تعریفه، و آخر هم دادهسازها (یا اسم مقادیری که در سطحِ جملهای ِ کُد قرار میگیرن) که بعد از مساوی نوشته میشن.
شما میتونین تعریف یک نوعداده ِ از پیش تعریف شده رو با دستورِ :info
در GHCi ببینین:
Prelude> :info Bool
data Bool = False | True
حالا ببینیم بخشهای مختلفِ یه نوعداده در کجاهای کُد دیده میشن. اگه تایپِ تابعِ not
رو استعلام کنیم، میبینیم که یه مقدارِ Bool
میگیره و یه مقدار Bool
ِ دیگه برمیگردونه. پس تایپ سیگنچر، به نوعساز یا اسمِ تایپ اشاره میکنه:
Prelude> :t not
not :: Bool -> Bool
ولی موقعِ استفاده از این تابع، از دادهسازها (یا خودِ مقادیر) استفاده میکنیم:
Prelude> not True
False
بیانیهمون هم به یه دادهساز یا مقدارِ دیگه ساده میشه – که اینجا تنها دادهساز ِ دیگهی همون تایپ رو نتیجه میده.
تمرینها: نوسان خُلقی
با نوعداده ِ زیر، سؤالهای زیر رو جواب بدین:
data Mood = Blah | Woot deriving Show
هنوز اون تیکهی deriving Show
رو توضیح ندادیم. فعلاً فقط در همین حد توضیح میدیم که مشتق کردنِ Show
باعث میشه مقادیرِ تایپهایی که خودتون تعریف میکنین قابلِ چاپ بشن. به تایپکلاسها که برسیم، بیشتر توضیح میدیم.
۱.
نوعساز، یا اسمِ این تایپ چیه؟
۲.
اگه تابعی ورودیش یه مقدارِ Mood
باشه، از چه مقادیری میشه استفاده کرد؟
۳.
میخوایم یه تابعِ changeMood
بنویسیم که خُلق ِ کریس رو فوراً عوض کنه*. باید کاری شبیهِ تابعِ not
انجام بده: با یک مقدارِ ورودی، تنها ورودی دیگهی همون تایپ رو برگردونه. ما این تایپ سیگنچر رو براش نوشتیم:
changeMood :: Mood -> Woot
مشکلش چیه؟
۴.
حالا خودِ تابع رو مینویسیم. با یه خُلق ِ ورودی، خُلق ِ دیگه رو برمیگردونه. هر اشکالی هست برطرف و تابع رو تکمیل کنین:
changeMood Mood = Woot
changeMood _ = Blah
اسمِ کاری که اینجا کردیم، تطبیقِ الگو ِه. میشه در تعریفِ یه تابع، اون رو با دادهسازها، یا مقادیرِ مختلف تطبیق داد، و رفتارِ اون رو بر اساس مقداری که باهاش منطبق میشه توصیف کرد. خطِ تیرهای که در خط دوم نوشتیم، نشونهی "بقیهی حالتها"ست. بنابراین، در خطِ اول، تعریف کردیم که به ازای یه ورودیِ مشخص، تابع باید چه کاری انجام بده. و در خط دوم، رفتار تابع به ازای همهی ورودیهای ممکنِ دیگه رو توصیف کردیم. تو این مثال که تایپ ورودی فقط دو مقدار داره، استفاده از خطِ تیره لازم نیست، ولی برای توابعِ پیچیدهتر، ضروری میشه.
۵.
همهی چیزهایی که بالاتر نوشتین رو تو یه فایل هم بنویسین – نوعداده (به همراهِ اون deriving Show
)، تایپ سیگنچر ِ اصلاح شده، و تابع تکمیل شده. فایل رو در GHCi بارگذاری کنین تا از درست بودنِ همه چیز مطمئن بشین.
م. کریس اسمِ یکی از نویسندههای این کتابه. اسم انتخابی تابع هم به معنی تغییرِ خلق
ِه.