درس ۲ از ۴۹ · آغاز راه

متغیرها و پایاها

متغیرها نام‌هایی برای نگه‌داشتن مقدارها هستند. سلام به‌طور پیش‌فرض متغیرها را تغییرناپذیر می‌سازد تا خطاهای پنهان کمتر شوند.

تعریف متغیر

با := یک نام تازه می‌سازید و مقداری در آن می‌گذارید. گونه‌ی متغیر از مقدارش فهمیده می‌شود و نیازی به نوشتنش نیست: "مریم" یک رشته می‌سازد و ۸۵ یک صحیح. از آن پس گونه ثابت می‌ماند و اگر بعداً بخواهید متن را در متغیری عددی بگذارید، خطا می‌گیرید. نام متغیرها می‌تواند چندواژه‌ای باشد، مثل قیمت کتاب.

variables.salam
پایا بیشینه := ۱۰۰

تابع ریشه:
    نام := "مریم"
    ناپایا امتیاز := ۸۵
    امتیاز += ۱۰
    سرچاپ نام, "امتیاز گرفت:", امتیاز, "از", بیشینه
پایان
خروجیمریم امتیاز گرفت: 95 از 100

هر متغیر از همان لحظه‌ی تعریف مقدار دارد. راهی نیست که نامی را حالا تعریف کنید و بعداً پرش کنید، پس هیچ‌وقت متغیری را نمی‌خوانید که مقدار بی‌معنایی در آن مانده باشد.

متغیر تغییرپذیر

متغیری که با := تعریف شود دیگر عوض نمی‌شود. اگر می‌خواهید مقدارش را بعداً تغییر دهید، پیش از نامش ناپایا بنویسید و سپس با = مقدار تازه بدهید. عملگرهای کوتاه +=، -= و ++ هم هستند.

var-mut.salam
تابع ریشه:
    ناپایا شمارنده := ۰
    شمارنده++
    شمارنده += ۵
    شمارنده++
    سرچاپ "شمارنده:", شمارنده
پایان
خروجیشمارنده: 7

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

محدوده‌ی متغیر

هر متغیر از خطی که تعریف می‌شود تا پایان همان بلوکی که در آن تعریف شده زنده است. نامی که درون یک اگر ساخته شود، با بسته‌شدن اگر از بین می‌رود و استفاده از آن پس از بلوک خطای «شناسه‌ی ناشناخته» می‌دهد. هر متغیر را در کوچک‌ترین بلوکی تعریف کنید که به آن نیاز دارد.

var-scope.salam
تابع ریشه:
    جمع خرید := ۱۴۰
    اگر جمع خرید >= ۱۰۰:
        امتیاز := جمع خرید / ۲۰
        سرچاپ "امتیاز این خرید:"، امتیاز
    پایان
    برچسب := "سفارش"
    اگر جمع خرید > ۱۲۰:
        برچسب := "سفارش بزرگ"
        سرچاپ "درون بلوک:"، برچسب
    پایان
    سرچاپ "پس از بلوک:"، برچسب
پایان
خروجیامتیاز این خرید: 7 درون بلوک: سفارش بزرگ پس از بلوک: سفارش

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

انتخاب مقدار بدون ناپایا

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

var-choose.salam
تابع ریشه:
    وزن := ۳
    فوری := درست
    هزینه ارسال := وزن > ۲ ? ۹۰۰۰۰ : ۵۰۰۰۰
    نوع ارسال := فوری ? "پست پیشتاز" : "پست عادی"
    سرچاپ "هزینه‌ی"، نوع ارسال، هزینه ارسال، "تومان"
پایان
خروجیهزینه‌ی پست پیشتاز 90000 تومان

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

گونه‌ی دلخواه

اگر گونه‌ای غیر از گونه‌ی پیش‌فرض می‌خواهید، مقدار را با برگردان به آن گونه تبدیل کنید. عدد صحیح به‌طور پیش‌فرض صحیح (۳۲ بیتی) و عدد اعشاری اعشار۶۴ است.

var-typed.salam
تابع ریشه:
    سن := ۱۷ برگردان صحیح۶۴
    قد := ۱.۷۵
    نام := "نگار"
    دانشجو := درست
    سرچاپ نام, سن, قد, دانشجو
پایان
خروجینگار 17 1.75 true

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

متغیرهای سراسری

بیرون از تابع‌ها می‌توانید با ناپایا متغیرهای سراسری و با پایا مقدارهای ثابت تعریف کنید؛ همه‌ی تابع‌های برنامه به آن‌ها دسترسی دارند. در این نمونه یک متغیر سراسری می‌شمارد که یک تابع چند بار صدا زده شده است:

var-global.salam
پایا نرخ := ۱۰

ناپایا فراخوانی‌ها := ۰

تابع مالیات(قیمت: صحیح): صحیح:
    فراخوانی‌ها++
    برگشت قیمت * نرخ / ۱۰۰
پایان

تابع ریشه:
    سرچاپ مالیات(۲۰۰۰)
    سرچاپ مالیات(۵۰۰)
    سرچاپ "فراخوانی‌ها:", فراخوانی‌ها
پایان
خروجی200 50 فراخوانی‌ها: 2

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

پایاها

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

var-const.salam
پایا دقیقه := ۶۰
پایا ساعت := ۶۰ * دقیقه
پایا شبانه‌روز := ۲۴ * ساعت
پایا برنامه := "پشتیبان‌گیر"

تابع ریشه:
    پایا تلاش‌ها := ۳
    سرچاپ برنامه، "پرونده‌ها را"، ۷ * شبانه‌روز، "ثانیه نگه می‌دارد"
    سرچاپ "بارگذاری ناموفق"، تلاش‌ها، "بار دوباره امتحان می‌شود"
پایان
خروجیپشتیبان‌گیر پرونده‌ها را 604800 ثانیه نگه می‌دارد بارگذاری ناموفق 3 بار دوباره امتحان می‌شود

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

وقتی متغیر چند مقدار را با هم نگه می‌دارد، مثل یک آرایه، تفاوت := و پایا مهم می‌شود. := نام را ثابت می‌کند: نمی‌توان آن را به مقدار کاملاً تازه‌ای سپرد، اما عضوهای درونش همچنان تغییرپذیرند. پایا همه چیز را، عضوها را هم، ثابت نگه می‌دارد.

var-contents.salam
پایا روزها := ["شنبه"، "یکشنبه"، "دوشنبه"، "سه‌شنبه"، "چهارشنبه"]

تابع ریشه:
    دماها := [۲۱، ۲۳، ۹۱، ۲۴، ۲۲]
    دماها[۲] = ۱۹
    سرچاپ دماها
    سرچاپ "دمای"، روزها[۲]، "به"، دماها[۲]، "اصلاح شد"
پایان
خروجی[21, 23, 19, 24, 22] دمای دوشنبه به 19 اصلاح شد

اینجا یک دمای اشتباه سر جایش اصلاح می‌شود، با این‌که دماها ناپایا نیست. اما نوشتن روزها[۲] = "جمعه" خطاست، چون پایا به‌طور کامل تغییرناپذیر است.

قواعد نام‌گذاری

  • نام‌ها می‌توانند از حرف‌های فارسی و انگلیسی، رقم و _ ساخته شوند، اما نباید با رقم شروع شوند.
  • فاصله و نیم‌فاصله درون نام یکی‌اند: قیمت کتاب و قیمت‌کتاب یک متغیرند. «ی» و «ک» عربی هم با فارسی‌شان یکی شمرده می‌شوند.
  • واژه‌های کلیدی زبان، مثل اگر، و، تا یا در، نام نمی‌شوند، حتی به شکل یکی از واژه‌های یک نام چندواژه‌ای: تعداد در انبار پذیرفته نمی‌شود، اما موجودی انبار درست است.
  • متغیر نمی‌تواند هم‌نام تابعی در همان پرونده باشد، تا هر نام فقط یک معنا داشته باشد.
  • آغاز کردن نام با _ خطای متغیر استفاده‌نشده را خاموش می‌کند. خود مترجم این کار را برای کد مرده توصیه نمی‌کند، چون کد در برنامه می‌ماند؛ آن را برای مقدارهایی نگه دارید که واقعاً می‌خواهید نادیده بگیرید.

این‌ها خطاهایی هستند که هنگام کار با متغیرها بیشتر از همه می‌بینید:

کدخطامعنی
ع := ۵ و سپس ع = ۶E013نمی‌توان متغیر تغییرناپذیر 'ع' را دوباره مقداردهی کرد؛ ناپایا اضافه کنید
ناپایا ع := ۵ که هرگز تغییر نمی‌کندE069با 'ناپایا' تعریف شده اما هرگز تغییر نکرده است؛ ناپایا را بردارید
ع := ۵ که به کار نرفتهE059متغیر استفاده‌نشده 'ع'
دو بار ع := ۵ در یک بلوکE001تعریف دوباره‌ی 'ع'
ناپایا ع := ۵ و سپس ع = "ده"E002نمی‌توان 'رشته' را به 'صحیح' نسبت داد
جمع := ۵ کنار تابع جمعE090'جمع' از قبل نام یک تابع در این فایل است