۱۱ - ۱۲حالت معمولی
تا اینجا مفهومِ جبر در نوعدادههای جبریِ هسکل رو بررسی کردیم و فوایدِ درکِ کاردینالیتی ِ نوعدادهها رو شناختیم. اما جبر به همینجا ختم نمیشه. همهی قواعد جبری که برای ضرب و جمع وجود دارن، برای تایپ سیستم هم صادقاند. خاصیتِ توزیعپذیری هم یکی از این قواعده. با یه مثال نحوهی کارش رو در حساب نشون میدیم:
2 * (3 + 4)
2 * (7)
14
میشه از توزیع ِ ضرب روی جمع هم به همین جواب برسیم:
2 * 3 + 2 * 4
(6) + (8)
14
به این میگیم "جمعِ ضربها." در حساب، حالت معمولی برای چنین بیانیهای، بعد از رسیدن به جوابه (م. یعنی ۱۴ در مثال بالا). اما اگه اون اعداد در مثالِ بالا نشوندهندهی کاردینالیتی ِ چندتا مجموعه باشن، اون موقع همون بیانیهی جمعِ ضربها حالت معمولی محسوب میشه، چون محاسبهای باقی نمونده.
خاصیتِ توزیعپذیری رو میشه تعمیم داد:
a * (b + c) -> (a * b) + (a * c)
و این برای تایپهای هسکل هم صادق ِه! تایپهای ضرب روی تایپهای جمع توزیع میشن. برای اینکه بیشتر با این بازی کنیم، اول چندتا نوعداده تعریف میکنیم:
data Fiction = Fiction deriving Show
data Nonfiction = Nonfiction deriving Show
data BookType = FictionBook Fiction
| NonfictionBook Nonfiction
deriving Show
-- BookType : م. نوعِ کتاب
دو تایپی که هرکدوم فقط یک عضوِ پوچگانه دارن تعریف کردیم: Fiction
و Nonfiction
. دلایل چنین کاری شاید درجا واضح نباشه، اما به خاطر بیارین که گفتیم از یه تایپ، فقط با یکی از مقادیرش نمیشه استفاده کرد. نمیشه یه مقدارِ Bool
بخواین و در تایپِ بیانیه مشخص کنین که باید همیشه True
باشه – مجبورین هر دو مقدارِ Bool
رو در نظر بگیرین. پس با تعریفِ Fiction
و Nonfiction
میتونیم انواع کتاب (که زیرش تعریف کردیم) رو تفکیک کنیم.
یه تایپ جمع به اسمِ BookType
هم تعریف کردیم که سازندههاش تایپهای Fiction
و Nonfiction
رو به عنوان آرگومان میگیرن. به خاطر داشتنِ این مهم ِه، با اینکه اسمِ نوعسازها و دادهسازهای Fiction
و Nonfiction
یکیاند، اما این دو تا یک چیز نیستن. چیزی که به عنوان آرگومان به FictionBook
و NonfictionBook
داده میشه، نوعسازهااند. اسمهاشون رو عوض کنین و خودتون تأثیرش رو ببینین.
حالا یه تایپ مترادف به اسمِ AuthorName
و یه تایپ ضرب به اسمِ Author
تعریف میکنیم. تایپ مترادف واقعاً کارِ خاصی نمیکنه، فقط اینجا کمک میکنه بدونیم از کدوم
String در تایپِ Author
داریم استفاده میکنیم:
type AuthorName = String
data Author = Author (AuthorName, BookType)
این جمعِ ضرب نیست، پس حالت معمولی هم نیست. میشه طوری محاسبهش کرد که مقادیرِ مخفی شده داخلِ تایپِ جمع ِ BookType
از توش بیرون کشیده بشن. با اعمال خاصیت توزیعپذیری تایپِ Author
رو در حالت معمولی بازنویسی میکنیم:
type AuthorName = String
-- اگه هر دوشون رو تو یه
-- فایل نوشتین، باید تعاریف
-- Nonfiction و Fiction قبلی از
-- .کنین یا پاک کنین comment رو
data Author =
Fiction AuthorName
| Nonfiction AuthorName
deriving (Eq, Show)
ضربها روی جمعها توزیع میشن. مثل باز کردن بیانیهی a * (b + c)
(که اعضای BookType
معادلِ b
و c
میشن)، با مجزا کردن مقادیر به جمعِ ضربها رسیدیم. حالا در حالت معمولی ِه، چون تا وقتی عملیات یا محاسبهای با این تایپها انجام نشه، این سازندهها سادهتر نمیشن.
یه مثالِ دیگه از حالت معمولی رو با تایپ Expr
، که در خیلی از مقالات دربارهی تایپ سیستمها و زبانهای برنامهنویسی به کار میره نشون میدیم:
data Expr =
Number Int
| Add Expr Expr
| Minus Expr
| Mult Expr Expr
| Divide Expr Expr
این جمعِ ضربها هست (تایپ جمع از تایپهای ضرب)، پس در حالت معمولی ِه: (Number Int) + Add (Expr Expr) + …
اگه بخوایم سختگیرانهتر به حالت معمولی ِ "جمعِ ضربها" برسیم، باید ضربها رو با توپل، و جمعها رو با Either
نشون بدیم. پس نوعداده ِ بالا مثل زیر میشه:
type Number = Int
type Add = (Expr, Expr)
type Minus = Expr
type Mult = (Expr, Expr)
type Divide = (Expr, Expr)
type Expr =
Either Number
(Either Add
(Either Minus
(Either Mult Divide)))
چنین ارائهای در جاهایی مثل برنامهنویسیِ جِنِریک یا فرابرنامهنویسی که توابع یا فولدها برای نوعدادهها نوشته میشن کاربرد داره. بعضی از این متودها در هسکل هم کاربرد دارن، اما باید با دقت زیاد به کار برن، و همیشه هم استفاده ازشون آسون نیست.
تایپِ Either
رو در فصل بعد کامل توضیح میدیم.
تمرینها: رشد باغچهتون چطوره؟
۱.
با توجه به تایپ زیر
data FlowerType = Gardenia
| Daisy
| Rose
| Lilac
deriving Show
type Gardener = String
data Garden =
Garden Gardener FlowerType
deriving Show
حالت معمولی (جمعِ ضرب) ِ Garden
چی میشه؟