➡ فهرست
چهار identifier از پیش تعریف شدهی مربوط به نوع دادهای ساده را در فصل قبل بررسی کردیم. این چهار predefined, simple type identifier عبارتند از:
با استفاده از این identifier ها برنامهنویس میتواند به type های موجود که این identifier ها آنها را نمایندگی میکنند، رجوع کند.
💡 type identifier ها نوع نیستند، بلکه شناسههایی هستند که نمایندهی یک نوعاند و به آن ارجاع میدهند.
حال به بررسی دو روش میپردازیم که با استفاده از آنها میتوانیم انواع ordinal جدید، enumerated type و subrange type، بسازیم.
نوع شمارشی یا enumerated، نوع دادهی جدیدی ایجاد میکند که به هیچ نوع دادهی دیگری مرتبط نیست، در حالی که نوع subrange یا زیربازه، نوع دادهی جدیدی میسازد که زیر مجموعهای از مقادیر یک نوع oridinal موجود دیگر را میپذیرد.
تعریف یک نوع enumerated مشخص کنندهی یک مجموعهی مرتب یا ordered از مقادیر است که هر مقدار با یک constant identifier بیان میشود. ordonal number اولین constant لیست شده، صفر، دومی یک و همین طور الی آخر است. سینتکس دیاگرام enumerated type به شکل زیر است:

مثال :
TYPE
Color = (White, Red, Orange, Yellow, Green, Blue, Purple, Black);
Sex = (Male, Female);
Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
مثال زیر نادرست است. چون نوع Sat مبهم است:
TYPE
Workday = (Mon, Tue, Wed, Thu, Fri, Sat);
Free = (Sat, Sun); { ❌ }
در Free Pascal میتوانید یک مقدار از نوع enumerated را مستقیما با Write و WriteLn چاپ کنید. این کار در پاسکال استاندارد ممکن نیست و باید از تابع Ord برای پیدا کردن ordinal number آن مقدار، استفاده کنید.
⬅ مثال . فقط در Free Pascal
⬅ مثال . هم در Free Pascal و هم در پاسکال استاندارد
⚪ حالا به سادگی درک میکنید که نوع از پیش تعریف شدهی Boolean به صورت زیر ساخته شده است:
TYPE
Boolean = (False, True);
تعریف بالا دو constant identifier به نامهای False و True ایجاد میکند
و مشخص میسازد که False < True.
⚪
عملگرهای رابطهای
= ،
<> ،
< ،
> ،
<= و
>=
میتوانند روی انواع enumerated استفاده شوند مشروط به این که هر دو عملوند
از یک نوع باشند. ترتیب آنها بر مبنای قرار گرفتن constant ها در لیست
مشخص میشود.
⛔ constant identifier های موجود در یک نوع شمارشی به هیچ وجه معادل اعداد 0 و 1 و ... نیستند.
⚪ توابع از پیش تعریف شده که آرگومانی از انواع ordinal میگیرند، عبارتند از: Succ(X) و Pred(X) و Ord(X).
Succ(X) e.g. Succ(Orange) = Yellow
Pred(X) Pred(Orange) = Red
Ord(X) Ord(Orange) = 2
⬅ مثال
⚪ فرض کنید C1 و C از نوع Color (تعریف شده در بالا) باشند و همچنین B از نوع Boolean و S1...Sn هم statement های دلخواه هستند. در این صورت statement های زیر همگی دارای معنا و صحیح هستند:
FOR C := Black DOWNTO Red DO
S1;
WHILE (C1 <> C) AND B DO
S1;
IF C > White THEN
C := Pred(C);
Case C OF
Red, Blue, Yellow:
S1;
Purple:
S2;
Green, Orange:
S3;
White, Black:
S4
END
⬅ مثال
⬅ مثال 5.1 #Think
💡 در پاسکال استاندارد نه میتوان یک مقدار از نوع enumerated را از ورودی خواند و نه آن را مستقیما در خروجی نوشت.
یک نوع جدید میتواند به عنوان یک زیربازه یا subrange از یک نوع ordinal، که قبلا تعریف شده است، تعریف شود. به نوع از قبل تعریف شده host type یا نوع میزبان میگویند. تعریف یک subrange، به سادگی، کوچکترین و بزرگترین مقدار constant در آن زیربازه را مشخص میکند و حد پایین نباید بزرگتر از حد بالا باشد. ایجاد subrange از نوع Real امکانپذیر نیست، چون Real از انواع ordinal نیست. سینتکس دیاگرام نوع زیربازه را در زیر ملاحظه میکنید:

⚪ نوع میزبان یک subrange صحت عملیات روی مقادیر آن subrange را تعیین میکند. به خاطر بیاورید که قانونِ سازگاریِ ordinal-type assignment فرض میکند که variable و expression یا از یک نوع ordinal هستند و یا subrange هایی از یک نوع ordinal اند، و مقدار expression در بازهی بستهای که نوع variable تعیین کرده است قرار میگیرد.
به عنوان مثال ۳ متغیر A و B و C به صورت زیر تعریف شدهاند:
VAR
A: 1..10;
B: 0..30;
C: 20..30;
Integer نوع میزبان A و B و C است. از این رو انتسابهای:
A := B;
C := B;
B := C;
همگی statement های درستی هستند، اگر چه اجرای آنها در برخی مواقع ممکن است با خطا همراه شود. در سراسر این کتاب هرگاه در مورد انواع ordinal بحث میکنیم، منظور «یا هر subrange ای از آن» نیز هست حتی اگر صراحتا ذکر نشود.
چند مثال:
TYPE
Days = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); { enumerated type }
Workdays = Mon..Fri; { subrange of days }
Index = 0..63; { subrange of Integer }
Letter = 'A'..'Z'; { subrange of char }
Natural = 0..MaxInt;
Positive = 1..MaxInt;
⚪ انواع دادهای subrange وسیلهای برای بیانِ توضیحی مساله فراهم میکنند. به عنوان مثال فرض کنید میخواهیم متغیر Age را در برنامه تعریف کنیم. دو راه پیش رو داریم:
Age: Integer;
or
Age: 0..120; { ✅ }
واضح است که تعریف دوم حتی به عنوان یک مستندسازی در کد برنامه عمل میکند و بهتر از روش اول است. برای نویسندهی کامپایلر هم فرصتی برای اختصاص کمتر حافظه و وارد کردن validity check های انتساب در زمان اجرا ایجاد میکند. برای مثال متغیر Age، تعریف شده به صورت subrange در کادر بالا، در بسیاری از پیادهسازیها ممکن است فقط یک بایت یا ۸ بیت اشغال کند؛ در حالیکه متغیری از نوع Integer احتیاج به بایتهای زیادی دارد.