با

بازگشت در Elixir

56 تمرین

درباره‌ی بازگشت

توابع بازگشتی توابعی هستند که خودشان را فراخوانی می‌کنند.

یک تابع بازگشتی باید دست‌کم یک «حالت پایه» و دست‌کم یک «حالت بازگشتی» داشته باشد.

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

بسیار پیش می‌آید که هر حالت در بند تابع جداگانه‌ی خودش نوشته می‌شود.

# base case
def count([]), do: 0

# recursive case
def count([_head | tail]), do: 1 + count(tail)

یک تابع بازگشتی می‌تواند حالت‌های پایه‌ی زیادی و/یا حالت‌های بازگشتی زیادی داشته باشد. برای نمونه دنباله‌ی فیبوناچی یک دنباله‌ی بازگشتی با دو حالت پایه است:

def fibonacci(0), do: 0
def fibonacci(1), do: 1
def fibonacci(n), do: fibonacci(n - 1) + fibonacci(n - 2)

شمارش تعداد تکرارهای یک مقدار مشخص مانند x در یک فهرست، دو حالت بازگشتی دارد:

def count_occurrences([], _x), do: 0
def count_occurrences([x | tail], x), do: 1 + count_occurrences(tail, x)
def count_occurrences([_ | tail], x), do: count_occurrences(tail, x)

حلقه‌ها از طریق بازگشت

به دلیل تغییرناپذیری، حلقه‌ها در Elixir متفاوت از زبان‌های امری نوشته می‌شوند. برای نمونه، حلقه‌ها معمولاً چنین شکلی دارند:

for(i = 0; i < array.size; i++) {
  # do something with array[i]
}

در یک زبان تابعی، تغییر دادن i (با اجرای i++) ممکن نیست. بنابراین، حلقه‌ها باید با بازگشت پیاده‌سازی شوند.

معادل یک حلقه‌ی for در Elixir چنین شکلی خواهد داشت:

def loop([]), do: nil

def loop([head | tail]) do
  do_something(head)
  loop(tail)
end

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

اجرای بی‌پایان

توابع بازگشتی، اگر نادرست پیاده‌سازی شوند، ممکن است هرگز نتیجه‌شان را برنگردانند. این می‌تواند دردسرساز باشد، چون هر بار که تابعی فراخوانی می‌شود، ارجاعی در حافظه ذخیره می‌شود، جایی که ماشین مجازی باید نتیجه را برگرداند (روی پشته‌ی فراخوانی). اگر یک تابع بازگشتی خودش را بی‌پایان فراخوانی کند، ممکن است حافظه تمام شود و ماشین مجازی از کار بیفتد (یک خطای سرریز پشته). ماشین مجازی Erlang که Elixir روی آن اجرا می‌شود، به‌طور ویژه برای بازگشت و پایداری بهینه شده است، بنابراین ممکن است زمان زیادی طول بکشد تا خطاهای بازگشت بی‌پایان آشکار شوند یا خرابی رخ دهد.

این مشکل اجرای بی‌پایان می‌تواند به این دلایل ایجاد شود:

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

بازگشت را یاد بگیرید

تمرین کردن قفل شده است

برای تمرین بازگشت قفل 10 تمرین دیگر را باز کنید