ساختارها
ساختار چند دادهی مرتبط را زیر یک نام جمع میکند و تابعهایی به نام روش دارد که روی همان داده کار میکنند. برنامههای سلام چیزهایی را که با آنها سروکار دارند با ساختار توصیف میکنند: یک حساب بانکی، یک سفارش، یک نقطه روی نقشه.
تعریف ساختار
هر فیلد نام و گونه دارد و میتواند مقدار پیشفرض هم داشته باشد. روش، تابعی است که درون ساختار نوشته شده و درون آن این به مقداری اشاره میکند که روش رویش صدا زده شده. فیلدها و روشها بهطور پیشفرض خصوصیاند و همگانی آنها را بیرون از ساختار در دسترس میگذارد.
ساختار مستطیل:
همگانی پهنا: صحیح = ۱
همگانی بلندی: صحیح = ۱
همگانی تابع مساحت(): صحیح:
برگشت این.پهنا * این.بلندی
پایان
پایان
تابع ریشه:
زمین := مستطیل { پهنا = ۱۲, بلندی = ۸ }
سرچاپ "مساحت زمین:", زمین.مساحت()
پایانساختن و چاپ
در ساختن یک مقدار، نام فیلدهایی را که میخواهید تعیین کنید مینویسید: نقطه { ایکس = ۳، وای = ۴ }. هر فیلدی که ننویسید مقدار پیشفرضش را میگیرد. فیلدی که پیشفرض ندارد باید هر بار داده شود، وگرنه کامپایلر با پیام E028: مقدار ساختاری 'ن' به فیلد 'ایکس' نیاز دارد میایستد. ساختار را میشود مستقیم و با همهی فیلدهایش چاپ کرد:
ساختار نقطه:
همگانی ایکس: صحیح = ۰
همگانی وای: صحیح = ۰
پایان
تابع ریشه:
ن := نقطه { ایکس = ۳، وای = ۴ }
سرچاپ ن
سرچاپ نقطه {}
پایانفیلد اختیاری با T؟
فیلدی که مقدار پیشفرض دارد همیشه مقدار دارد، پس کدی که آن را میخواند نمیتواند «تعیین نشده» را از «صفر تعیین شده» جدا کند. برای این کار به گونهی فیلد ؟ اضافه کنید: مهلت: صحیح؟. چنین فیلدی را میشود در مقدار ساختاری ننوشت و در این حالت چیزی در خود ندارد، مگر آنکه پیشفرض خودش را داشته باشد، مانند تلاشها: صحیح؟ = ۳. مقدار ساده خودکار در آن قرار میگیرد و پوچ دوباره خالیاش میکند. آن را با دارد()، ندارد()، یابرگردان(جایگزین) یا بازکن() بخوانید، یا از فیلدهای موجود و مقدار استفاده کنید.
ساختار مشتری:
همگانی میزبان: رشته
همگانی درگاه: صحیح = ۴۴۳
همگانی مهلت: صحیح؟
پایان
تابع ریشه:
الف := مشتری { میزبان = "a.test" }
سرچاپ الف.درگاه، الف.مهلت.ندارد()
ناپایا ب := مشتری { میزبان = "b.test"، مهلت = ۳۰ }
سرچاپ ب.مهلت.مقدار، ب.مهلت.یابرگردان(۵)
ب.مهلت = پوچ
سرچاپ ب.مهلت.یابرگردان(۵)
پایانصحیح؟ شکل دیگری از هسته.اختیاری<صحیح> است و برای استفاده از آن به هیچ واردسازی نیازی نیست. ورودی و خروجی تابع هم میتوانند اختیاری باشند. برای متغیر محلی بنویسید x := ۵ برگردان صحیح؟، یا ناپایا x: صحیح؟ و پیش از خواندن به آن مقدار بدهید. پیش از استفاده از مقدار آن را با دارد() بررسی کنید: در فیلد خالی معنایی ندارد و در یک اختیاری رشتهای خالی اشارهگر پوچی است که هنگام استفاده برنامه را از کار میاندازد. یابرگردان این بررسی را خودش انجام میدهد.
فیلدهایی که به هم وابستهاند
گاهی فیلدی اختیاری است تا وقتی که فیلد دیگری داده شود: نام کاربر فقط وقتی پذیرفته است که رمز هم باشد. این را با یک قاعده درون ساختار بنویسید تا مترجم هر مقدار ساختاری را با آن بسنجد:
ساختار مشتری:
همگانی میزبان: رشته
همگانی کاربر: رشته = ""
همگانی رمز: رشته = ""
همگانی توکن: رشته = ""
قاعده کاربر نیازمند رمز
قاعده فقط یکی(کاربر، توکن)
پایان
تابع ریشه:
الف := مشتری { میزبان = "db"، کاربر = "علی"، رمز = "راز" }
سرچاپ الف.میزبان، الف.کاربر
ب := مشتری { میزبان = "api"، توکن = "abc" }
سرچاپ ب.میزبان، ب.توکن
پایانقاعده الف نیازمند ب، ج یعنی مقداری که الف را تعیین کند باید ب و ج را هم تعیین کند. شکلهای دیگر روی یک گروه کار میکنند: باهم(الف، ب) برای همه یا هیچکدام، فقط یکی(الف، ب)، دست کم یکی(الف، ب) و حداکثر یکی(الف، ب). شکستن قاعده ساخت را با خطا۱۵۹ متوقف میکند.
قاعدهی نیازمند میتواند مقدار را هم بسنجد: قاعده با کلید برابر درست نیازمند کلید فقط وقتی اعمال میشود که با کلید درست باشد. مقدار سمت راست باید لفظی یا عضو جداشمار ساده باشد. اگر مقدار فیلد ثابت نباشد، مترجم فرض میکند شاید برابر باشد و فیلد را میخواهد.
در غیر این صورت قاعده به چیزی نگاه میکند که در مقدار نوشته شده، نه به خود مقدارها، پس کاربر = "" هم دادهشده به حساب میآید. قاعده فقط همان مقدار ساختاری را میسنجد و تغییر بعدی فیلد بررسی نمیشود. اگر چیزی باید همیشه برقرار بماند، از جداشمار دادهدار یا سازنده استفاده کنید.
متن دلخواه با to_str
شکل چاپی پیشفرض همهی فیلدها را نشان میدهد؛ برای خودتان هنگام نوشتن برنامه مفید است، ولی همیشه چیزی نیست که کاربر باید ببیند. اگر ساختار روشی با امضای همگانی تابع to_str(): رشته داشته باشد، سرچاپ از آن استفاده میکند. وقتی ساختار درون ساختاری دیگر، یک وکتور یا یک آرایه باشد هم همین روش به کار میرود.
ساختار پول:
همگانی ریال: صحیح = ۰
همگانی تابع to_str(): رشته:
تومان := این.ریال / ۱۰
باقی := این.ریال % ۱۰
اگر باقی == ۰:
برگشت "" + تومان + " تومان"
پایان
برگشت "" + تومان + " تومان و " + باقی + " ریال"
پایان
پایان
ساختار کالا:
همگانی نام: رشته = ""
همگانی قیمت: پول = پول {}
پایان
تابع ریشه:
خودکار := کالا { نام = "خودکار"، قیمت = پول { ریال = ۲۵۰۰۰ } }
سرچاپ خودکار.قیمت
سرچاپ خودکار
سرچاپ "جمع: " + پول { ریال = ۱۲۰۵۵ }.ToStr()
پایاننام این روش حتی در برنامهی فارسی باید دقیقاً to_str باشد. کامپایلر با همین نام دنبالش میگردد و روشی به نام «به رشته» را برای چاپ به کار نمیبرد.
کالا روش to_str ندارد و فیلدبهفیلد چاپ میشود، ولی فیلد قیمت آن از روش پول استفاده میکند. اگر متن را بدون چاپ لازم دارید، مثل خط آخر خودتان to_str() را صدا بزنید.
همگانی و خصوصی
فیلد خصوصی را فقط روشهای خود ساختار میتوانند بخوانند و بنویسند. کدی بیرون از ساختار که ح.موجودی را بخواهد، این خطا را میگیرد: E017: فیلد 'موجودی' در ساختار 'حساب' خصوصی است (آن را 'همگانی' کنید). ساختار قاعدههایش را اینطور نگه میدارد: اینجا هیچکس نمیتواند موجودی را منفی کند، چون تنها راه تغییرش واریز و برداشت است و هر دو اول مبلغ را بررسی میکنند.
ساختار حساب:
دارنده: رشته = ""
موجودی: صحیح = ۰
همگانی ایستا تابع باز کن(دارنده: رشته): حساب:
برگشت حساب { دارنده = دارنده }
پایان
همگانی تابع نام دارنده(): رشته:
برگشت این.دارنده
پایان
همگانی تابع مانده(): صحیح:
برگشت این.موجودی
پایان
همگانی ناپایا تابع واریز(مبلغ: صحیح): منطقی:
اگر مبلغ <= ۰:
برگشت نادرست
پایان
این.موجودی += مبلغ
برگشت درست
پایان
همگانی ناپایا تابع برداشت(مبلغ: صحیح): منطقی:
اگر مبلغ <= ۰ یا مبلغ > این.موجودی:
برگشت نادرست
پایان
این.موجودی -= مبلغ
برگشت درست
پایان
پایان
تابع ریشه:
ناپایا ح := حساب.باز کن("سارا")
سرچاپ ح.واریز(۵۰۰)، ح.برداشت(۸۰۰)، ح.برداشت(۱۲۰)
سرچاپ ح.نام دارنده()، ح.مانده()
پایانعادت خوبی است که فیلدها را خصوصی نگه دارید مگر دلیلی برای همگانی کردنشان باشد، و به دنیای بیرون روشهایی بدهید که بگویند چه کاری ممکن است (واریز)، نه اینکه داده چطور نگه داشته شده (موجودی).
روش نمیتواند همنام یکی از فیلدهای ساختار خودش باشد. برای همین روشی که موجودی را برمیگرداند مانده نام گرفته، نه موجودی.
روش ناپایا
روشی که فیلدهای ساختار را تغییر میدهد با ناپایا تابع تعریف میشود. وقتی ساختاری حتی یک روش ناپایا داشته باشد، کامپایلر همهی روشهایش را میسنجد: تابع معمولیای که به فیلدی از این مقدار بدهد خطاست (E105: در متد 'صفر' نمیتوان 'این' را تغییر داد: آن را 'ناپایا تابع' اعلام کنید) و روش ناپایا را فقط روی متغیری میشود صدا زد که با ناپایا تعریف شده (E106). پس با خواندن خط اول هر روش میفهمید که مقدار را تغییر میدهد یا نه.
ساختار شمارنده:
مقدار: صحیح = ۰
همگانی تابع خواندن(): صحیح:
برگشت این.مقدار
پایان
همگانی ناپایا تابع یکیبیشتر():
این.مقدار++
پایان
پایان
تابع ریشه:
ناپایا ش := شمارنده {}
ش.یکیبیشتر()
ش.یکیبیشتر()
سرچاپ ش.خواندن()
پایانعضو ایستا و سازنده
ایستا تابع و پایا درون ساختار به خودِ گونه تعلق دارند، نه به یک مقدار؛ با نام ساختار صدایشان بزنید. تابع ایستا این ندارد، ولی میتواند فیلدهای خصوصی را مقدار بدهد، و همین آن را جای طبیعی سازندهها میکند. حساب.باز کن در مثال قبل یکی از آنها بود؛ این هم یکی دیگر که مقداری آماده میسازد:
ساختار رنگ:
همگانی سرخ: صحیح = ۰
همگانی سبز: صحیح = ۰
همگانی آبی: صحیح = ۰
همگانی پایا بیشینه := ۲۵۵
همگانی ایستا تابع خاکستری(مقدار: صحیح): رنگ:
برگشت رنگ { سرخ = مقدار، سبز = مقدار، آبی = مقدار }
پایان
پایان
تابع ریشه:
سفید := رنگ.خاکستری(رنگ.بیشینه)
سرچاپ سفید
پایانساختار مقدار است
نسبت دادن یک ساختار آن را رونویسی میکند. پس از مقصد := آغاز این دو مقدار جدا هستند و تغییر مقصد به آغاز کاری ندارد. فرستادن ساختار به یک تابع هم آن را رونویسی میکند، پس تابعی که باید ساختارِ صدازننده را تغییر دهد آن را با &: میگیرد. دو ساختار را بی هیچ کد اضافهای میشود با == و != فیلدبهفیلد مقایسه کرد.
ساختار نقطه:
همگانی ایکس: صحیح = ۰
همگانی وای: صحیح = ۰
پایان
تابع صفر رونوشت(ن: نقطه):
ن.ایکس = ۰
پایان
تابع صفر کن(ن &: نقطه):
ن.ایکس = ۰
پایان
تابع ریشه:
آغاز := نقطه { ایکس = ۳، وای = ۴ }
ناپایا مقصد := آغاز
مقصد.وای = ۱۰
سرچاپ آغاز، مقصد
سرچاپ آغاز == نقطه { ایکس = ۳، وای = ۴ }، آغاز == مقصد
صفر رونوشت(مقصد)
سرچاپ مقصد.ایکس
صفر کن(مقصد)
سرچاپ مقصد.ایکس
پایانصفر رونوشت رونوشت خودش را تغییر داد که هنگام بازگشت دور ریخته شد. صفر کن ارجاع گرفت و مقصدِ صدازننده را تغییر داد. ورودی &: برای ساختارهای بزرگ ارزانتر هم هست، چون چیزی رونویسی نمیشود. یک استثنا در «ساختار مقدار است» هست: فیلدی از گونهی وکتور یا نگاشت به شکل یک دستگیره رونویسی میشود و هر دو نسخه عضوهای یکسانی را میبینند (بخش اشتراک و رونوشت را ببینید).
ترکیب با شامل
شامل یک ساختار را درون ساختار دیگر میگذارد و فیلدها و روشهای همگانیاش را مستقیم در دسترس میکند. این ترکیب است، نه ارثبری: سگ از یک جانور بهعلاوهی نژاد ساخته شده.
ساختار جانور:
همگانی نام: رشته = ""
همگانی تابع معرفی(): رشته:
برگشت "من " + این.نام + " هستم"
پایان
پایان
ساختار سگ:
همگانی شامل جانور
همگانی نژاد: رشته = ""
پایان
تابع ریشه:
س := سگ { نام = "رکس"، نژاد = "شکاری" }
سرچاپ س.معرفی()
سرچاپ س.نام، س.نژاد
پایانساختارِ شاملشده خودش هم فیلدی است به نام گونهاش. اگر ساختار بیرونی روشی همنام با روش ساختار درونی تعریف کند، روش خودش برنده است و هنوز میتواند نسخهی درونی را از راه همان فیلد صدا بزند:
ساختار تاریخچه:
همگانی ساخت: رشته = ""
همگانی ویرایش: رشته = ""
همگانی تابع شرح(): رشته:
برگشت "ساخته در " + این.ساخت + "، ویرایش در " + این.ویرایش
پایان
پایان
ساختار نوشته:
همگانی شامل تاریخچه
همگانی عنوان: رشته = ""
همگانی تابع شرح(): رشته:
برگشت این.عنوان + " (" + این.تاریخچه.شرح() + ")"
پایان
پایان
تابع ریشه:
ناپایا ن := نوشته { عنوان = "سلام"، ساخت = "۱۴۰۵/۰۷/۰۹" }
ن.ویرایش = "۱۴۰۵/۰۷/۱۳"
سرچاپ ن.شرح()
سرچاپ ن.ساخت، ن.تاریخچه.ویرایش
سرچاپ ن
پایاندر ساختن مقدار میشود فیلدهای درونی را مستقیم داد (ساخت = ...) و در چاپ، ساختار درونی به شکل مقداری تودرتو نشان داده میشود. همگانی شامل ساختار درونی را بیرون هم در دسترس میگذارد و شامل تنها آن را خصوصی نگه میدارد.
قدمهای بعدی
- ساختار میتواند عملگرهایی مثل
+و==را به شکل روش تعریف کند؛ بازتعریف عملگرها را ببینید. همخوانمیتواند ساختار را بر پایهی فیلدهایش باز کند؛ الگوی ساختار را ببینید.- ساختارهای عام مثل
جعبهساختارهای عام مثلو میانجیهایی که چند ساختار در آن شریکاند در درس تابعهای عام و میانجیها آمدهاند.جعبه<ت>و میانجیهایی که چند ساختار در آن شریکاند در درس گونههای عام و میانجیها آمدهاند.lt;تساختارهای عام مثلجعبه<ت>و میانجیهایی که چند ساختار در آن شریکاند در درس گونههای عام و میانجیها آمدهاند.gt;
چند قاعدهی کاربردی
- به فیلدها پیشفرض معقول بدهید تا در ساختن مقدار فقط آنچه فرق دارد نوشته شود.
- فیلدها را خصوصی نگه دارید و روش در اختیار بگذارید؛ فیلد همگانی را برای دادهی سادهای بگذارید که قاعدهای برای نگهداری ندارد.
- هر روشی را که ساختار را تغییر میدهد
ناپایا تابعکنید و متغیری را که روی آن صدایش میزنید باناپایاتعریف کنید. - سازندهها را در
ایستا تابعبگذارید، بهویژه وقتی مقدار باید بررسی شود یا فیلد خصوصی دارد. - نسبت دادن و ورودی تابع ساختار را رونویسی میکنند. وقتی تابع باید مقدار صدازننده را تغییر دهد، از
&:استفاده کنید.