شروع elixir : آشنایی با Anonymous Functions

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



#آموزش سریع و آشنایی با الکسیر

###آشنایی با Anonymous Functions

همانطور که می دانید الکسیر یک زبان فانکشیونال هست پس نباید دور از انتظار باشد که فانکشن ها پایه اصلی این زبان باشند.

Anonymous Functions به صورت زیر نوشته می شوند :

fn​
​ parameter-list -> body
​ parameter-list -> body ...
end​

اگر بخواهیم کمی به صورت خودمانی این مورد را تحلیل کنیم می توانیم بگوییم fn و end مثل این می ماند یک سری رشته ها را در خود احاطه کرده اند. و به ما این قدرت را می دهد تا یک مقدار تابع را به دیگر توابع انتقال دهیم .

در ساده ترین مثال یک تابع دارای یک لیست از پارامتر ها و یک بدنه به وسیله -> از هم جدا می شوند.

مثال :

iex> sum = fn (a, b) -> a + b end
#Function<12.118419387/2 in :erl_eval.expr/5>

از دید یک اماتور مثل من شما آمدید دو آرگومنت گرفتید و روی آن ها یک سری عملیات انجام دادید مثل جمع حالا زمان آن هست صدایشان بزنیم و به آن ها ارزش بدهیم

iex> sum.(1,2)

همانطور که انتظار داریم باید عدد ۳ را برای ما برگشت بدهد . حالا بیاییم دوباره برگشت کنیم به کد و از دید دیگر به آن نگاه کنیم
ما یک فانکشنی ساختیم که که دوتا آرگومنت گرفته است و آن ها را می دهد به یک عملیات جدید که در کد بالا جمع می باشد و بعد end قرار می دهد و می گویند هرچی که هست را در sum ذخیره کن تا برای ما برگشت بدهی .

یک مثال دیگر بزنیم . این بار نمی خواهیم آرگومنت خاصی در آن تعریف کنیم

در این مثال ما می خواهیم هر وقت Anonymous Functions صدا زده شد اسم shahryar را برگشت بدهد در کنسول

iex> greet = fn -> IO.puts "shahryar" end
#Function<20.118419387/0 in :erl_eval.expr/5>

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

iex> greet.()
shahryar
:ok

هم اسمی که می خواستیم را چاپ کرد و هم یک اتم :ok نیز برای ما برگشت داد .

نکته : در صحبت های بالا ما شاید کلمه مثل اختصاص دادن استفاده کردیم این صحبت شاید برای برخی ها این صدای زنگ را به صدا بیاورد که شما گفنید نمی توان در الکسیر اختصاص داد و …. در جواب باید بگویم این فقط برای درک بهتر استفاده می شود ( یعنی استفاده از کلمه اختصاص ) شما در زمانی که می گوید ما به a ارزش x و به b ارزش y را اختصاص دادیم در حقیقت در تمام مراحل بالا شما یک پتر درست کردید مثل مثال زیر :

{a, b} =  {1, 2}

خوب برای من که تا الان خیلی جذاب شد . در حقیقت الان یک پترن دارم و می دونم که الکسیر یکی از هدف های اصلیش و مانورش روی پترن ماچینشه ( فارسی انگلیسیش کردم کمی ) و همین موضوع برای من این نتیجه رو داره که من به یک چیزی کاملا انعطاف پذیری در Anonymous Functions دست پیدا کردم. و دوباره باید به این نکته اشاره کنیم که ما چیزی را اختصاص ندادیم بلکه یک پترن انعطاف پذیر درست کردیم و داریم ازش استفاده می کنیم.

برای اینکه دقیق تر درک شود یک مثال دیگر برای جمع بندی در زیر قرار می دهم

iex> swap = fn { a, b } -> { b , a } end
#Function<6.118419387/1 in :erl_eval.expr/5>
iex> swap.({6 , 8})
{8, 6}

مثال های بیشتر :

###یک تابع و چندین بدنه

فکر می کنم از تایتلی که برای این بخش انتخاب کردم تا حدودی متوجه شدید که می خوام در مورد چه چیزی حرف بزنیم . تاحالا ما یک Anonymous Functions درست می کردیم که یک کار را انجام می داد و دارای یک بدنه بود . حالا فکر کنید شما می خواهید بر اساس دو احتمال یا دو فعالیت احتمالی کد هایی را درج کنید و تعداد بدنه فانکشن شما از نظر انجام فعالیت از یک به دو الی ۳ می رسد . اینجاست که باز هم اگر مثل من تازه با این زبان آشنا شدید تعجب می کنید و جذب می شوید.
مثال ما در این رابطه هست که یک فایلی را می خواهیم باز کنیم اگر فایل در جای مناسبش بود و باز شد بگوید خیلی خوب فایل شما در تاریخ فلان باز شد و اگر فایل ما مشکل پیدا کرد و در جای خودش نبود بگوید چه اروری دارد و ….
خیلی خوب می شه که شما از پترن ماچین استفاده کنید درسته ؟ چون خیلی خوب می شه اتم ها رو یا بهتر ارور ها را مدیریت کرد. خیلی جذابه اگر در زبان دیگری بود باید یک چندتایی شرط قرار می دادیدم .

مثال :

iex> handle_open = fn                                               
...> {:ok , file } -> "Read data : #{IO.read(file, :line)}"
...> {_, error} -> "Error: #{:file.format_error(error)}"
...> end

کمی به خط های بالا توجه کنید من یک متغییر ساختم که در آن یک Anonymous Functions که سناریویی که در بالا برای این مثال گفتم را می خواهد برای ما انجام بدهد . حالا می یاییم صداش می زنیم

اول فایلی که در مسیر اشتباه هست را آدرس می دهم :

iex> handle_open.(File.open("mbox.txts"))                           
"Error: no such file or directory"

حال می آیم همان خط را با مسیر درست و فایل مورد نظر ارزش گذاری می کنم :

iex> handle_open.(File.open("mbox.txt")) 
"Read data : From [email protected]  Sat Aug  1 16:13:40 2015\n"

چطور بود ؟ من که فکر می کنم خیلی جذاب بوده . و واقعا این امکان برای من کار های بسیار زیادی در آینده روی پروژه هام انجام می دهد

نکته : برای یاداوری به دوستانی که تازه شروع کردند خروجی یک آدرس اشتباه به صورت زیر می باشد

iex> File.open("mbox.txts")
{:error, :enoent}

در بالا سمت چپ در نظر گرفته نشد و سمت راست در نظر گرفته شد

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


یکمی فانکشن هارو زیبا تر بکنیم و همینطور کاربردی تر . به کد زیر توجه کنید

###فراخوانی فانکشن در فانکشن

fun1 = fn -> fn -> "hello" end end

شما در یک فانکشن در حقیقت یک فانکشن دیگری را فراخوانی کردید . یعنی چی ؟ یعنی شما دارید دو فانکشن فراخوانی می کنید شاید حتی دلتان بخواهد بیشتر هم فراخوانی کنید.

اگر بخواهیم استراکچر آن را ساده تر نمایش بدهیم به صورت زیر می شود :

fun1 = ​fn​ ->
​           fn​ ->
​               "​​Hello"​
​           end​
​       ​end​

نکته : برای درک بیشتر فانکشن های ساده مثال زده می شود وگرنه می شود همین کد را بسیار تو در تو مثال زد. اگر می خواهید آن را بیشتر پیشرفت بدهید به عنوان تمرین برای شما باشد

حالا نحوه فراخوانی آن به چه صورت می باشد :

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

fun1.()

و برای شما احتمالا چنین چیزی فراخوانی می گردد :

#Function<20.118419387/0 in :erl_eval.expr/5>

خوب حالا فکر کنم فهمیدید باید چطور باید آن را صدا کنید

iex> fun1.().()

بله به همین سادگی پیام استرینگی که نوشتید چاپ می گردد

نکته : الکسیر تا اینجا که من تست کردم خیلی جاها () پرانتز باز بسته را برای انعطاف پذیری بیشتر این امکان را می دهد تا حذف گردد. اما اگر فکر می کنید نیاز به پرانتز دارید می توانید به صورت زیر نیز انجام بدهید :

fun2 = fn -> (fn -> "hi shahryar" end) end

در اینجا شاید برای شما سوال پیش بیاید آیا می شود آرگومنت فانکشن اول را در فانکشن های بعدی لود کرد یا خیر؟ برای این جواب به سوال به مثال زیر توجه کنید

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

iex> func = fn name ->
...> fn -> "hello #{name}"
...> end
...> end
#Function<6.118419387/1 in :erl_eval.expr/5>

حال آن را صدا می زنیم

iex> func.(“shahryar”).()
"hello shahryar"

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

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

iex(35)> add = fn n ->
...(35)> fn other ->
...(35)> n + other
...(35)> end
...(35)> end
#Function<6.118419387/1 in :erl_eval.expr/5>
iex(36)> add.(2).(3)
5

تمرین :

خوب دوستان برای اینکه بخش Anonymous Function بیشتر از این نشه و موارد دیگه به عنوان تمرین برای خود شما باشه فقط چند مثال دیگر خدمتتون می زنم که بدوند Anonymous Functions چقدر قدرت دارد

Enum.each [1,2,3,4], fn x -> IO.puts "#{x}" end

پس دادن خود فانکشن

times2 = fn n -> n * 2 end

مثال :

iex> list = [1,2,3,4,5,6]
[1, 2, 3, 4, 5, 6]
iex> Enum.map list, fn elm -> elm *2 end
[2, 4, 6, 8, 10, 12]

اگر می خواهید در کد های خودتان کد های کوتاه تر بنویسید باز هم الکسیر همراه شما خواهد بود

iex> addy = &(&1 + 1)
#Function<6.118419387/1 in :erl_eval.expr/5>
iex> addy.(33)
34

مثال دیگر از کوتاه نویسی

iex> square = &(&1 * &1)
#Function<6.118419387/1 in :erl_eval.expr/5>
iex> square.(8)
64

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

دانلود pdf این آموزش :

2 پسندیده

خیلی عالی و بی نظیر
کاش از when و… هم استفاده میکردی که بنظر من واقعا یکی از جذابیتای این زبانن
f = fn
x, y when x > 0 -> x + y
x, y -> x * y
end

1 پسندیده

تشکر از شما دوست عزیز . فکر کنم when در فصل های بعدی در موردش صحبت می کنه . حتما اونم ترجمه می کنم فعلن که سعی دارم کل کتاب رو ترجمه کنم هرجاش که کاربردی هست

1 پسندیده