درس ۱۶ از ۴۹ · داده‌ها و گونه‌ها

گونه‌های تازه و عملگرها

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

نام تازه و گونه‌ی متمایز

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

type-alias.salam
گونه شناسه = صحیح

گونه متر: صحیح
    تابع +(دیگر: متر): متر:
        برگشت ((این برگردان صحیح) + (دیگر برگردان صحیح)) برگردان متر
    پایان
    تابع کیلومتر(): اعشار۶۴:
        برگشت (این برگردان اعشار۶۴) / ۱۰۰۰.۰
    پایان
پایان

تابع ریشه:
    ش := ۴۲ برگردان شناسه
    سرچاپ ش + ۱
    راه := ۱۲۰۰ برگردان متر
    سرچاپ (راه + راه).کیلومتر()
پایان
خروجی43 2.4

نام دیگر برای گونه‌ی بلندی که زیاد می‌نویسید به کار می‌آید، یا برای نام دادن به جداشماری از جای دیگر: پس از گونه رنگ‌مایه = رنگ، رنگ‌مایه.قرمز همان حالت رنگ.قرمز است. در خطاهای گونه‌ای هم این نام دیده می‌شود، مثل نمی‌توان 'رشته' را به 'صحیح' پاس داد ('سن' نام دیگری برای 'صحیح' است).

گونه‌ی متمایز برای معنا

گونه‌ی متمایز همه‌ی عملگرهای گونه‌ی پایه‌اش را نگه می‌دارد و نتیجه هم از گونه‌ی تازه است: جمع دو ریال باز ریال است و < و > مثل قبل کار می‌کنند. کاری که نمی‌کند قاطی شدن با عدد ساده است. قیمت + ن وقتی ن یک متغیر صحیح است با این خطا می‌ایستد: E021: این عملگر روی 'ریال' و 'صحیح' قابل اعمال نیست. تبدیل با برگردان باید نوشته شود، پس همیشه دیده می‌شود. هر جا عملی با گونه‌ای دیگر معنا دارد، آن را تعریف کنید، مثل * در یک تعداد در این مثال:

type-money.salam
گونه ریال: صحیح
    تابع *(تعداد: صحیح): ریال:
        برگشت ((این برگردان صحیح) * تعداد) برگردان ریال
    پایان
    تابع متن(): رشته:
        برگشت "" + ((این برگردان صحیح) / ۱۰) + " تومان"
    پایان
پایان

تابع ریشه:
    قیمت := ۴۹۹۰۰ برگردان ریال
    ارسال := ۲۵۰۰۰ برگردان ریال
    جمع := قیمت * ۳ + ارسال
    سرچاپ جمع.متن()، جمع > قیمت
    سرچاپ جمع
    نفرها := ۲
    سهم := ((جمع برگردان صحیح) / نفرها) برگردان ریال
    سرچاپ "سهم هر نفر"، سهم.متن()
پایان
خروجی17470 تومان true 174700 سهم هر نفر 8735 تومان

سرچاپ جمع عدد زیرین را چاپ می‌کند، برای همین مثال متن قالب‌بندی‌شده را از روش خودش، متن()، می‌گیرد. عدد خام گونه‌ی طرف مقابلش را می‌پذیرد، پس قیمت + ۱۰۰ برگردان لازم ندارد، ولی قیمت * ۳ از *ِ تعریف‌شده در خود گونه استفاده می‌کند.

پایه می‌تواند رشته، منطقی، نویسه یا عدد اعشاری هم باشد. رایانامه هنوز متن است، ولی دیگر نمی‌شود هر رشته‌ای را به اشتباه به تابعی داد که رایانامه می‌خواهد (E012: آرگومان ۱: نمی‌توان 'رشته' را به 'رایانامه' پاس داد):

type-email.salam
گونه رایانامه: رشته
    تابع دامنه(): رشته:
        م := این برگردان رشته
        جا := م.بیاب("@")
        برگشت م.زیررشته(جا + ۱، م.طول() - جا - ۱)
    پایان
پایان

تابع آگاه کن(گیرنده: رایانامه، موضوع: رشته):
    سرچاپ "به"، گیرنده برگردان رشته، "در"، گیرنده.دامنه()، "درباره‌ی"، موضوع
پایان

تابع ریشه:
    کاربر := "sara@example.com" برگردان رایانامه
    آگاه کن(کاربر، "سفارش شما")
پایان
خروجیبه sara@example.com در example.com درباره‌ی سفارش شما

بازتعریف عملگرها

روشی که نامش خودِ عملگر است، مثل تابع +(دیگر: بردار): بردار، آن عملگر را برای ساختار تعریف می‌کند و وقتی صدا زده می‌شود که ساختار عملوند سمت چپ باشد. عملگرهای قابل بازتعریف این‌ها هستند: + - * / % ** == != < > <= >= وارونه [] []=؛ ساختن نماد تازه خطاست.

operator.salam
ساختار بردار:
    همگانی ایکس: صحیح = ۰
    همگانی وای: صحیح = ۰
    همگانی تابع +(دیگر: بردار): بردار:
        برگشت بردار { ایکس = این.ایکس + دیگر.ایکس، وای = این.وای + دیگر.وای }
    پایان
    همگانی تابع ==(دیگر: بردار): منطقی:
        برگشت این.ایکس == دیگر.ایکس و این.وای == دیگر.وای
    پایان
پایان

تابع ریشه:
    الف := بردار { ایکس = ۱، وای = ۲ }
    ب := بردار { ایکس = ۳، وای = ۴ }
    سرچاپ الف + ب
    سرچاپ الف == ب، (الف + ب) == بردار { ایکس = ۴، وای = ۶ }
پایان
خروجیبردار {ایکس = 4, وای = 6} false true

اندیس، منفی و بقیه

تعداد پارامترها نوع عملگر را تعیین می‌کند. تابع -: بردار بدون پارامتر منفی کردن است (-جا) و تابع -(دیگر: بردار) تفریق؛ اگر هر دو را لازم دارید، هر دو را تعریف کنید. [] اندیس را می‌گیرد و []= اندیس و مقدار تازه را، و با آن‌ها می‌شود ساختار را مثل آرایه با کروشه به کار برد:

operator-more.salam
ساختار صفحه:
    خانه‌ها: صحیح[۹] = [۰، ۰، ۰، ۰، ۰، ۰، ۰، ۰، ۰]
    همگانی تابع [](ش: صحیح): صحیح:
        برگشت این.خانه‌ها[ش]
    پایان
    همگانی ناپایا تابع []=(ش: صحیح، نشان: صحیح):
        این.خانه‌ها[ش] = نشان
    پایان
پایان

ساختار بردار:
    همگانی ایکس: صحیح = ۰
    همگانی وای: صحیح = ۰
    همگانی تابع +(دیگر: بردار): بردار:
        برگشت بردار { ایکس = این.ایکس + دیگر.ایکس، وای = این.وای + دیگر.وای }
    پایان
    همگانی تابع *(ضریب: صحیح): بردار:
        برگشت بردار { ایکس = این.ایکس * ضریب، وای = این.وای * ضریب }
    پایان
    همگانی تابع -: بردار:
        برگشت بردار { ایکس = -این.ایکس، وای = -این.وای }
    پایان
    همگانی تابع ==(دیگر: بردار): منطقی:
        برگشت این.ایکس == دیگر.ایکس و این.وای == دیگر.وای
    پایان
پایان

تابع ریشه:
    ناپایا ص := صفحه {}
    ص[۴] = ۱
    ص[۰] = ۲
    سرچاپ ص[۴]، ص[۰]، ص[۸]

    ناپایا جا := بردار { ایکس = ۱، وای = ۲ }
    جا += بردار { ایکس = ۱۰، وای = ۰ }
    سرچاپ جا، جا * ۳، -جا
    سرچاپ جا != بردار {}
پایان
خروجی1 2 0 بردار {ایکس = 11, وای = 2} بردار {ایکس = 33, وای = 6} بردار {ایکس = -11, وای = -2} true

بعضی عملگرها رایگان به دست می‌آیند. الف += ب از +ِ شما استفاده می‌کند و اگر != ننوشته باشید، != از ==ِ شما. لازم نیست عملوند سمت راست * از همان گونه باشد: این‌جا یک بردار در یک صحیح ضرب می‌شود.

عملگری را تعریف کنید که معنایش برای خواننده روشن باشد. + روی دو بردار روشن است؛ +ی که رکوردی را روی دیسک ذخیره کند روشن نیست و روشی با نام مناسب انتخاب بهتری است.

گونه‌های یا

صحیح یا رشته مقداری است که می‌تواند یکی از چند گونه باشد. گونه‌های عضو را هرجا که گونه‌ای لازم است، با یا میان آن‌ها بنویسید: پارامتر، گونه‌ی برگشتی، فیلد ساختار، یا گونه‌ی اعلام‌شده‌ی یک متغیر محلی. با همخوان روی نام گونه‌ها، مقدار را با گونه‌ی واقعی‌اش بیرون بکشید؛ همخوان باید همه‌ی گونه‌های فهرست را پوشش دهد.

variant.salam
تابع شرح(م: صحیح یا رشته): رشته:
    برگشت همخوان م:
        صحیح ع: "عدد " + ع پایان
        رشته ر: "متن " + ر پایان
    پایان
پایان

تابع ریشه:
    ناپایا مقدار := ۲۱ برگردان (صحیح یا رشته)
    سرچاپ شرح(مقدار)
    مقدار = "خاموش"
    سرچاپ شرح(مقدار)
پایان
خروجیعدد 21 متن خاموش

یک گونه‌ی یا اغلب یک فیلد است: تنظیمی که مقدارش ممکن است عدد، کلید روشن و خاموش یا یک نام باشد. مقداری از یکی از گونه‌های فهرست را بی برگردان، مستقیم نسبت دهید:

variant-settings.salam
ساختار تنظیم:
    همگانی کلید: رشته = ""
    همگانی مقدار: صحیح یا منطقی یا رشته = ۰
پایان

تابع نمایش(ت: تنظیم): رشته:
    برگشت ت.کلید + " = " + همخوان ت.مقدار:
        صحیح ع: "" + ع پایان
        منطقی پرچم: پرچم ? "بله" : "نه" پایان
        رشته م: "«" + م + "»" پایان
    پایان
پایان

تابع ریشه:
    ناپایا تنظیم‌ها := وکتور {} برگردان وکتور<تنظیم>
    دیرکن تنظیم‌ها.آزادکن()
    تنظیم‌ها.بیفزا(تنظیم { کلید = "درگاه"، مقدار = ۸۰۸۰ })
    تنظیم‌ها.بیفزا(تنظیم { کلید = "اشکال‌زدایی"، مقدار = درست })
    تنظیم‌ها.بیفزا(تنظیم { کلید = "نام"، مقدار = "فروشگاه" })
    هر ت در تنظیم‌ها:
        سرچاپ نمایش(ت)
    پایان
پایان
خروجیدرگاه = 8080 اشکال‌زدایی = بله نام = «فروشگاه»

مقدار یک گونه‌ی یا را نمی‌شود همان‌طور که هست چاپ کرد (E002: نمی‌توان مقدار گونه‌ی 'گوناگون<صحیح, رشته>' را مستقیم چاپ کرد - خطا شکل درونی گونه را نام می‌برد)؛ رویش همخوان بزنید و بخشی را که به دست آمده چاپ کنید، همان‌طور که نمایش این کار را می‌کند. وقتی گزینه‌ها نام خودشان را لازم دارند یا چند مقدار با خود دارند، معمولاً جداشمار داده‌دار ابزار روشن‌تری است.

enum-or-types.salam
واردسازی رشته

تابع تنظیم(متن: رشته): صحیح یا منطقی یا رشته:
    اگر متن == "درست" یا متن == "نادرست":
        برگشت متن == "درست"
    پایان
    ع := متن.به صحیح()
    اگر رشته.از عدد(ع برگردان صحیح۶۴) == متن:
        برگشت ع
    پایان
    برگشت متن
پایان

تابع توصیف(م: صحیح یا منطقی یا رشته): رشته:
    برگشت همخوان م:
        صحیح ع: "عدد " + رشته.از عدد(ع برگردان صحیح۶۴) پایان
        منطقی ب: "کلید " + (ب ? "روشن" : "خاموش") پایان
        رشته س: "متن " + س پایان
    پایان
پایان

تابع ریشه:
    هر متن در ["8080"، "درست"، "لوکال‌هاست"]:
        سرچاپ توصیف(تنظیم(متن))
    پایان
پایان
خروجیعدد 8080 کلید روشن متن لوکال‌هاست
  • در جای گونه، یک پارامتر، یک گونه‌ی برگشتی، یک فیلد ساختار یا گونه‌ی اعلام‌شده‌ی یک متغیر محلی، نیازی به پرانتز نیست: تابع ف(م: صحیح یا رشته): ... همین‌طور درست است.
  • بعد از برگردان، ترکیب را پرانتز بگذارید: x برگردان (صحیح یا رشته). بدون پرانتز، x برگردان صحیح یا رشته به شکل (x برگردان صحیح) یا رشته خوانده می‌شود، یک عبارت منطقی ساخته از یک تبدیل و یک نام گونه، که خطا نیست و بی‌صدا کار نادرستی انجام می‌دهد.
  • یک گونه‌ی یا به پس‌زمینه‌ی کامپایل‌شده نیاز دارد: salam exec، مفسر درون‌خطی، هنوز همخوان روی آن را پشتیبانی نمی‌کند.
  • تا ۶۴ عضو مجازند.

کدام را انتخاب کنیم

چه می‌خواهیدچه به کار ببرید
نامی کوتاه‌تر یا روشن‌تر برای گونه‌ای که هستگونه نام = گ
عدد یا رشته‌ای که نباید با بقیه قاطی شودگونه نام: گ ... پایان
چند فیلد نام‌دار کنار همساختار
یکی از فهرستی ثابت از حالت‌ها، شاید با دادهجداشمار
مقداری از یکی از چند گونه‌ی موجودالف یا ب
یک تابع یا ساختار که برای گونه‌های زیادی کار کندگونه‌های عام

چند قاعده‌ی کاربردی

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