مسیرها
/
Swift
Swift
/
تمرین‌ها
/
استاد لازانیا
استاد لازانیا

استاد لازانیا

تمرین یادگیری

مقدمه

مقادیر پیش‌فرض پارامتر

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

func greeting(name: String = "guest", duration: Int = 2) -> String {
  "Welcome, \(name). Enjoy your \(duration) night stay."
}

greeting(name: "Bobo", duration: 7)
// Returns "Welcome, Bobo. Enjoy your 7 night stay."
greeting(duration: 3)
// Returns "Welcome, guest. Enjoy your 3 night stay."
greeting(name: "Wynona")
// Returns "Welcome, Wynona. Enjoy your 2 night stay."
greeting()
// Returns "Welcome, guest. Enjoy your 2 night stay."

پارامترهای چندمقداری

«پارامترهای چندمقداری» به یک تابع اجازه می‌دهند برای یک پارامتر واحد، صفر یا چند مقدار از یک نوع مشخص را بپذیرد. برای تعریف یک پارامتر چندمقداری، سه نقطه (...) را به انتهای حاشیه‌نویسی نوع آن پارامتر اضافه می‌کنید.

در بدنه‌ی تابع، Swift این مقادیر را به‌صورت یک آرایه از نوع مشخص‌شده در دسترس قرار می‌دهد:

import Foundation

func geometricMean(_ numbers: Double...) -> Double {
    var total = 1.0
    for number in numbers {
        total *= number
    }
    return pow(total, 1.0 / Double(numbers.count))
}

let result = geometricMean(1, 2, 3, 4, 5)
// result is 2.605171084697352
Note

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

پارامترهای ورودی-خروجی

در بدنه‌ی یک تابع، پارامترها ثابت در نظر گرفته می‌شوند، نه متغیر؛ تلاش برای تغییر مقدار یک پارامتر، خطای زمان کامپایل ایجاد می‌کند. اگر تابعی بخواهد مقدار یک پارامتر را تغییر دهد، باید از «پارامتر ورودی-خروجی» استفاده کند تا این تغییرپذیری را صریح کند.

برای استفاده از پارامترهای ورودی-خروجی، برنامه‌نویس باید به سه نکته توجه کند:

  1. تعریف تابع باید پارامترهای ورودی-خروجی را به‌صراحت مشخص کند؛ به این صورت که کلیدواژه‌ی inout را بین : و نام نوع، در حاشیه‌نویسی نوع پارامتر بنویسید.
  2. مقداری که به پارامتر ورودی-خروجی فرستاده می‌شود باید به‌صورت متغیر تعریف شده باشد، نه ثابت. به همین ترتیب، نمی‌تواند یک لیترال باشد.
  3. در فراخوانی تابع باید پیش از نام متغیر یک & بیاید تا به‌صراحت به‌عنوان قابل‌تغییر علامت‌گذاری شود.

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

func updateVersion(_ record: inout (version: Int, title: String)) {
  record.version += 1
}

var dbRecord = (version: 2, title: "Exercism")
updateVersion(&dbRecord)

// dbRecord is now (3, "Exercism")
چند قاعده‌ی اضافی هم هست که باید درباره‌ی پارامترهای ورودی-خروجی بدانید.

۱  داخل تابعی که پارامتر ورودی-خروجی دارد، اجازه ندارید به متغیری که به‌عنوان پارامتر ورودی-خروجی فرستاده شده ارجاع دهید.
2. همان متغیر را نمی‌توان در یک تابع، به‌عنوان چند پارامتر ورودی-خروجی مختلف فرستاد.
   برای مثال، `inoutFunc(&mutVar, &mutVar)` خطای کامپایلر ایجاد می‌کند.

توابع تودرتو

در Swift می‌توانید توابعی را داخل توابع دیگر تعریف کنید. به این‌ها توابع تودرتو می‌گویند.

معمولاً از توابع تودرتو به‌عنوان توابع کمکی خصوصی استفاده می‌شود که کارهای مشخصی را برای تابع دربرگیرنده انجام می‌دهند، بدون آنکه فضای نام بیرونی را شلوغ کنند:

func makeNumber(_ number: Int) -> Int {
    func double(_ x: Int) -> Int {
        x * 2
    }

    func increment(_ x: Int) -> Int {
        x + 1
    }

    return number.isMultiple(of: 2) ? increment(double(number)) : double(number)
}

print(makeNumber(3)) // Prints 6
print(makeNumber(4)) // Prints 9

توابع تودرتو مثل توابع عادی رفتار می‌کنند، اما فقط در بدنه‌ی تابع دربرگیرنده قابل مشاهده هستند.

دستورالعمل‌ها

در این تمرین قرار است کد بیشتری بنویسید تا به شما کمک کند لازانیای بی‌نظیرتان را از کتاب آشپزی محبوبتان بپزید.

پنج کار در پیش دارید که همه به زمان صرف‌شده برای پخت لازانیا مربوط می‌شوند.

1. امکان تغییر زمان مورد انتظار در فر

می‌خواهید نسخه‌ی بهبودیافته‌ای از تابع remainingMinutesInOven بنویسید که تعداد دقیقه‌های سپری‌شده از حضور لازانیا در فر را به عنوان پارامتر می‌گیرد و بر اساس زمان مورد انتظار پخت در فر (به دقیقه) از تمرین قبلی برمی‌گرداند که لازانیا باید چند دقیقه‌ی دیگر در فر بماند. پیش‌تر به یک مقدار ثابت به نام expectedMinutesInOven تکیه کرده بودید که از بیرون تابع remainingMinutesInOven می‌آمد. این کار هم به سلیقه‌ی شما کمی شلوغ بود و هم به‌اندازه‌ی کافی انعطاف نداشت، چون اجازه نمی‌داد زمان مورد انتظار در فر را تغییر دهید.

برای مرتب کردن اوضاع، تابع remainingMinutesInOven را تعریف کنید که تعداد دقیقه‌های سپری‌شده از حضور لازانیا در فر را به عنوان پارامتر می‌گیرد، و همچنین پارامتری به نام expectedMinutesInOven که مقدار پیش‌فرضش ۴۰ است. تابع شما باید بر اساس زمان مورد انتظار پخت در فر (به دقیقه) از تمرین قبلی برگرداند که لازانیا باید چند دقیقه‌ی دیگر در فر بماند.

remainingMinutesInOven(elapsedMinutes: 30)
// Returns 10
remainingMinutesInOven(elapsedMinutes: 30, expectedMinutesInOven: 75)
// Returns 45

2. امکان تعداد متفاوت لایه‌ها

پیش‌تر تابعی به نام preparationTimeInMinutes نوشته بودید که تعداد لایه‌هایی را که به لازانیا اضافه کرده‌اید به عنوان پارامتر می‌گیرد و برمی‌گرداند که چند دقیقه صرف آماده کردن لازانیا کرده‌اید، با این فرض که آماده کردن هر لایه ۲ دقیقه طول می‌کشد. اما چندان از این تابع راضی نیستید، چون باید پیش از فراخوانی آن، خودتان تعداد لایه‌های لازانیایتان را بشمارید، و چه کسی وقت این کارها را دارد؟

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

preparationTimeInMinutes(layers: "sauce", "noodles")
// Returns 4
preparationTimeInMinutes(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns 24

3. محاسبه‌ی مقدار رشته و سس

پیش از آنکه شروع به چیدن لازانیا کنید، می‌خواهید مطمئن شوید سس و رشته‌ی کافی برای ساختن لازانیای رؤیایی‌تان دارید. برای هر لایه‌ی رشته در لازانیایتان، به ۳ رشته نیاز دارید. برای هر لایه‌ی سس در لازانیایتان، به ۰٫۲ لیتر سس نیاز دارید.

تابع quantities را تعریف کنید که پارامتری متغیر از نوع String به نام layers می‌گیرد و در آن لایه‌های مختلف لازانیایتان را فهرست می‌کنید. سپس تابع مقدار رشته و سس لازم برای درست کردن غذایتان را تعیین می‌کند. نتیجه باید در یک تاپل از نوع (noodles: Int, sauce: Double) برگردانده شود.

quantities(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns (noodles: 9, sauce: 0.8)
quantities(layers: "sauce", "noodles", "sauce", "noodles", "meat", "noodles", "mozzarella")
// Returns (noodles: 9, sauce: 0.4)

4. تبدیل لیتر به اونس مایع به‌صورت درجا

متأسفانه حجم سسی که در دسترس دارید، به‌جای لیتر بر حسب اونس مایع نوشته شده است. برای اینکه بدانید سس کافی در دسترس دارید یا نه، تابعی به نام toOz بنویسید که پارامتری تاپلی و درجایی از نوع (noodles: Int, sauce: Double) می‌گیرد و مقدار سس را به اونس مایع تبدیل می‌کند. در نظر داشته باشید که هر لیتر ۳۳٫۸۱۴ اونس مایع است.

var amount = (noodles: 9, sauce: 0.8)
toOz(&amount)

amount
// Returns (noodles: 9, sauce: 27.0512)

5. تعیین بهترین شرابی که باید سرو شود

در نهایت، می‌خواهید بدانید چه نوع شرابی را باید همراه لازانیا سرو کنید. شنیده‌اید که اگر مقدار موزارلا، ریکوتا و بشامل در لازانیا بیشتر از مقدار گوشت و سس باشد، باید شراب سفید سرو کنید و در غیر این صورت شراب قرمز. تابعی به نام redWine بنویسید که پارامتری متغیر از نوع String به نام layers می‌گیرد و در آن لایه‌های مختلف لازانیایتان را فهرست می‌کنید. این تابع از پنج تابع تودرتو استفاده می‌کند تا تعداد لایه‌های موزارلا، ریکوتا، بشامل، سس و گوشت را در لازانیا تعیین کند و با مقایسه‌ی تعداد لایه‌ها مشخص کند که آیا باید شراب قرمز سرو شود یا نه، و اگر آری true و در غیر این صورت false را برمی‌گرداند.

redWine(layers: "sauce", "noodles", "sauce", "meat", "mozzarella", "noodles", "sauce", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns true
redWine(layers: "sauce", "noodles", "béchamel", "meat", "mozzarella", "noodles", "ricotta", "eggplant", "béchamel", "noodles", "sauce", "mozzarella")
// Returns false
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Swift Exercism

آماده‌اید استاد لازانیا را شروع کنید؟

در Exercism ثبت‌نام کنید تا Swift را همراه با 35 مفهوم116 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.