وقتی با بازگشت روی دادههای قابل شمارش (فهرستها، بیتاسترینگها، رشتهها) کار میکنید، معمولاً دو دغدغه وجود دارد:
برای رسیدگی به این دغدغهها میتوان از یک انباره استفاده کرد.
انباره متغیری است که علاوه بر خود دادهها منتقل میشود. از آن برای انتقال وضعیت جاری اجرای تابع، از یک فراخوانی به فراخوانی بعدی، تا رسیدن به حالت پایه استفاده میشود. در حالت پایه، از انباره برای برگرداندن مقدار نهایی فراخوانی بازگشتی تابع استفاده میشود.
انباره را باید نویسندهی تابع مقداردهی اولیه کند، نه کاربر تابع. برای این کار دو تابع تعریف کنید: یک تابع عمومی که فقط دادههای لازم را بهعنوان ورودی میگیرد و انباره را مقداردهی اولیه میکند و یک تابع خصوصی که انباره را هم میگیرد. در Elixir، الگوی رایجی است که نام تابع خصوصی را با do_ شروع کنند.
# Count the length of a list without an accumulator
def count([]), do: 0
def count([_head | tail]), do: 1 + count(tail)
# Count the length of a list with an accumulator
def count(list), do: do_count(list, 0)
defp do_count([], count), do: count
defp do_count([_head | tail], count), do: do_count(tail, count + 1)
استفاده از انباره به ما اجازه میدهد توابع بازگشتی را به توابع بازگشتی دمی تبدیل کنیم. تابع زمانی بازگشتی دمی است که آخرین عملیاتی که اجرا میکند، فراخوانی خودش باشد.
در آزمایشگاه تحقیقاتی DNA خود، روشهای مختلفی را برای فشردهسازی دادههای تحقیقیتان و صرفهجویی در فضای ذخیرهسازی بررسی کردهاید. یکی از همکارانتان پیشنهاد میکند دادههای DNA را به یک نمایش دودویی تبدیل کنید:
| اسید نوکلئیک | Code |
|---|---|
| یک فاصله | 0000 |
| A | 0001 |
| C | 0010 |
| G | 0100 |
| T | 1000 |
روی این موضوع تأمل میکنید، چون این کار بهطور بالقوه هزینههای ذخیرهسازی دادههای موردنیاز را نصف میکند، اما به قیمت کاهش خوانایی برای انسان تمام میشود. تصمیم میگیرید ماژولی بنویسید که دادههایتان را کدگذاری و رمزگشایی کند تا میزان صرفهجویی را بسنجید.
تابع encode_nucleotide/1 را طوری پیادهسازی کنید که کدپوینت اسید نوکلئیک را بپذیرد و مقدار عدد صحیح کد کدگذاریشده را برگرداند.
DNA.encode_nucleotide(?A)
# => 1
# (which is equal to 0b0001)
تابع decode_nucleotide/1 را طوری پیادهسازی کنید که مقدار عدد صحیح کد کدگذاریشده را بپذیرد و کدپوینت اسید نوکلئیک را برگرداند.
DNA.decode_nucleotide(0b0001)
# => 65
# (which is equal to ?A)
تابع encode/1 را طوری پیادهسازی کنید که یک charlist نشاندهندهی اسیدهای نوکلئیک و فاصلهها را بپذیرد و یک bitstring از دادههای کدگذاریشده برگرداند.
DNA.encode(~c"AC GT")
# => <<18, 4, 8::size(4)>>
تابع decode/1 را طوری پیادهسازی کنید که یک bitstring نشاندهندهی اسیدهای نوکلئیک و فاصلهها را بپذیرد و دادههای رمزگشاییشده را بهصورت یک charlist برگرداند.
DNA.decode(<<132, 2, 1::size(4)>>)
# => ~c"TG CA"