مسیرها
/
Elixir
Elixir
/
تمرین‌ها
/
نوبت بگیرید
نوبت بگیرید

نوبت بگیرید

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

مقدمه

فرایندها

در Elixir، همه‌ی کدها درون «فرایندها» اجرا می‌شوند.

به‌طور پیش‌فرض، هر تابع در همان فرایندی اجرا می‌شود که از آنجا فراخوانی شده است. وقتی لازم است تابع خاصی را صراحتاً در یک فرایند جدید اجرا کنید، از spawn/1 استفاده کنید:

spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>

spawn/1 فرایند جدیدی می‌سازد که تابع بدون آرگومانِ داده‌شده را اجرا می‌کند و یک «شناسه‌ی فرایند» را برمی‌گرداند که به اختصار PID نامیده می‌شود. فرایند جدید تا زمانی که تابع در حال اجراست زنده می‌ماند و پس از آن بی‌صدا از بین می‌رود.

فرایندهای Elixir را نباید با فرایندهای سیستم‌عامل اشتباه گرفت. فرایندهای Elixir حافظه و CPU بسیار کمتری مصرف می‌کنند. هیچ اشکالی ندارد که یک برنامه‌ی Elixir صدها فرایند Elixir اجرا کند.

پیام‌ها

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

می‌توانید با send/2 به هر فرایندی پیام بفرستید. آرگومان اول send/2 شناسه‌ی فرایند گیرنده است و آرگومان دوم پیام.

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

send/2 بررسی نمی‌کند که پیام توسط گیرنده دریافت شده است یا نه، و نه اینکه گیرنده هنوز زنده است یا نه. پیام در «صندوق پیام» گیرنده می‌نشیند و تنها در صورتی خوانده می‌شود که گیرنده صراحتاً درخواست دریافت پیام‌ها کند.

می‌توان پیام را با ماکروی receive/1 از صندوق پیام خواند. این ماکرو یک بلوک do می‌پذیرد که می‌تواند روی پیام‌ها تطبیق الگو انجام دهد.

receive do
  {:ping, sender_pid} -> send(sender_pid, :pong)
  :do_nothing -> nil
end

receive/1 یک پیام را از صندوق پیام برمی‌دارد که با هر یک از الگوهای داده‌شده مطابقت داشته باشد و عبارت مربوط به آن الگو را اجرا می‌کند. اگر پیامی در صندوق پیام نباشد، یا هیچ‌یک از پیام‌های صندوق پیام با هیچ‌یک از الگوها مطابقت نداشته باشد، receive/1 منتظر می‌ماند تا پیامی برسد.

حلقه‌ی Receive

اگر می‌خواهید بیش از یک پیام دریافت کنید، باید receive/1 را به‌صورت بازگشتی فراخوانی کنید. الگوی رایجی است که یک تابع بازگشتی پیاده‌سازی کنید، مثلاً با اسم loop، که receive/1 را فراخوانی می‌کند، کاری با پیام انجام می‌دهد و سپس خودش را فراخوانی می‌کند تا منتظر پیام‌های بیشتر بماند. اگر لازم است وضعیتی را از یک فراخوانی receive/1 به فراخوانی دیگر منتقل کنید، می‌توانید این کار را با پاس دادن آرگومانی به آن تابع loop انجام دهید.

شناسه‌های فرایند

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

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

شما در حال نوشتن یک سیستم نهفته برای یک دستگاه نوبت‌دهی هستید. مدل بسیار ساده‌ای است. می‌تواند شماره‌های متوالی بدهد و گزارش کند که آخرین شماره‌ی داده‌شده چه بوده است.

1. دستگاه را راه‌اندازی کنید

تابع start/0 را پیاده‌سازی کنید. باید یک فرایند جدید ایجاد کند و PID آن فرایند را برگرداند. فرایند جدید فعلاً لازم نیست کاری انجام دهد.

TakeANumber.start()
# => #PID<0.138.0>

توجه کنید که هر بار این کد را اجرا می‌کنید، ممکن است PID متفاوت باشد.

2. وضعیت دستگاه را گزارش دهید

دستگاه را طوری تغییر دهید که فرایند تازه ایجادشده، با وضعیت اولیه‌ی 0، آماده‌ی دریافت پیام‌ها باشد (یک حلقه‌ی دریافت راه بیندازد). باید بتواند پیام‌های {:report_state, sender_pid} را دریافت کند. در پاسخ به این پیام‌ها، باید وضعیت فعلی خود (آخرین شماره‌ی بلیت داده‌شده) را به sender_pid بفرستد و سپس منتظر پیام‌های بیشتر بماند.

machine_pid = TakeANumber.start()

# a client sending a message to the machine
send(machine_pid, {:report_state, self()})

# a client receiving a message from the machine
receive do
  msg -> msg
end

# => 0

3. شماره بدهید

دستگاه را طوری تغییر دهید که بتواند پیام‌های {:take_a_number, sender_pid} را دریافت کند. باید وضعیتش را ۱ واحد افزایش دهد، وضعیت جدید را به sender_pid بفرستد و سپس منتظر پیام‌های بیشتر بماند.

machine_pid = TakeANumber.start()

# a client sending a message to the machine
send(machine_pid, {:take_a_number, self()})

# a client receiving a message from the machine
receive do
  msg -> msg
end

# => 1

4. دستگاه را متوقف کنید

دستگاه را طوری تغییر دهید که بتواند پیام :stop را دریافت کند. باید انتظار برای پیام‌های بیشتر را متوقف کند.

5. پیام‌های غیرمنتظره را نادیده بگیرید

دستگاه را طوری تغییر دهید که وقتی پیامی غیرمنتظره دریافت می‌کند، آن را نادیده بگیرد و همچنان منتظر پیام‌های بیشتر بماند.

ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Elixir Exercism

آماده‌اید نوبت بگیرید را شروع کنید؟

در Exercism ثبت‌نام کنید تا Elixir را همراه با 58 مفهوم168 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.