شروع elixir : توضیحات بیشتر در مورد Maps, Keyword Lists, Sets, و Structs

با درود خدمت شما .

قبل از مطالعه این بخش لطفا آموزش های قبلی را مطالعه کنید :
http://devheroes.club/c/other-programming-languages/elixir

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

توضیحات بیشتر در مورد Maps, Keyword Lists, Sets, و Structs

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

تا به امروز در مقالاتی که قبلا ارسال کردیم برای هر کدام از تیتر ها خلاصه ای بیان شد ولی در ادامه سعی می کنیم اطلاعات بیشتری در مورد هر کدام مطرح کنیم از جمله اطلاعات تکمیلی , روش ذخیره و … و همینطور در کجا و چه زمانی باید استفاده شوند.

من فکر می کنم بهتر هست برای شروع اول ببنیم در چه زمانی و مکانی به کدام مورد های تیتر شده نیاز داریم؟

از Map استفاده کنیم؟ یا از Keyword List ؟

برای جواب دادن به این سوال باید سوالات زیر را از خودتان بپرسید

آیا من به یک پترن ماچین در برابر محتوا نیاز دارم ؟ ( به صورت مثال : مطابق با دیکشنری از کلیدی مثل :name در جایی که هستید استفاده می کنید ؟ )

اگر چنین چیزی هست شما نیازمند به یک map هستید

آیا شما بیشتر از یک ورودی برای کلید های خود دارید ؟

در این صورت شما نیازمند به Keyword module می باشید

آیا شما نیازمند تضمین المان های خود دارید و همینطور دستورات خود ؟

پس شما نیازمند به Keyword module می باشید

و در آخر اگر شما به این نقطه رسیدید پس نیاز به map خواهید داشت

( به احتمال زیاد منظور نویسنده احتمالاتی که در سوال های بالا نیست )

Keyword Lists

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

# my blog  : https://trangell.com/fa/
# public : http://devheroes.club
defmodule Canvas do

  @defaults [ fg: "black", bg: "white", font: "Merriweather" ]

  def draw_text(text, options \\ []) do
    options = Keyword.merge(@defaults, options)
    IO.puts "Drawing text #{inspect(text)}"
    IO.puts "Foreground:  #{options[:fg]}"
    IO.puts "Background:  #{Keyword.get(options, :bg)}"
    IO.puts "Font:        #{Keyword.get(options, :font)}"
    IO.puts "Pattern:     #{Keyword.get(options, :pattern, “solid”)}"
IO.puts "Style:       #{inspect Keyword.get_values(options, :style)}"
  end

end

Canvas.draw_text("hello", fg: "red", style: "italic", style: "bold")

#   Drawing text "hello"
#   Foreground:  red
#   Background:  white
#   Font:        Merriweather
#   Pattern:     solid
#   Style:       ["italic", "bold"]

برای درک بهتر کد در این قسمت هم قرار گرفت :

حال بیاییم یک تحلیل کوچک نسبت به کد های بالا داشته باشیم

در شروع یک ماژول جدید ساختیم به نام Canvas و یک فراداده ساختیم به نام @defaults که در آن چند پارامتر قرار دادیم.

و بعد از آن یک فانکشن ساختیم به نام draw_text که یک آرگمنتش مشخص و یکی دیگر می تواند مقداری دیفالت داشته باشد از نوع @defaults .

نکته : اگر می خواهید اطلاعات بیشتری نسبت به مرج کردند و همینطور دریافت اطلاعات به دست بیاورید و بیشتر نسبت به کلاس و فانکشن های Keyword آشنا شوید به لینک زیر مراجعه کنید :
https://hexdocs.pm/elixir/Keyword.html

حال اومده هر کدام از مواردی که می خواسته را چاپ کرده. اگر توجه کنید یک سری موارد در @defaults هست و یک سری اضافه شده .
و اگر بیشتر توجه کنید یک سری از موارد دیفالت رو با موارد جدید خودش جایگزین کرده و یک سری رو بهش اضافه کرده و یک سری همون مورد اصلیشو فراخوانی کرده

جمع بندی

برای دسترسی ساده شما می تونید از اپراتور kwlist[key] استفاده کنید . به همین سادگی !! و همینطور می توانید از تمام فانکشن های Keyword و Enum در ماژول فعال هست هم استفاده نمایید.

Maps

برای رفت به ساختار key/value ( کلید ها و ارزش ) map ها همیشه عملکرد عالی دارند شاید بشود گفت در تمام اندازه ها و احتمال ها .

شاید این حرف کمی شما رو در شک برده باشد ولی اشکال ندارد من می خوام در ادامه کار کمی با map بازی کنم.

توجه : تمام دستوراتی از جمله اضافه کردن فراخوانی و دلیت کردن و اعتبار سنجی که یک map به راحتی می تواند برای شما فراهم کند همه و همه در لینک زیر برای شما دسته بندی کردم

شروع بازی با Map

برای شروع شما باید یک iex در ترمینال خودتان تایپ کنید تا یک کنسول خالی باز شود

برای ساخت یک مپ دستور زیر را تایپ کنید :slight_smile:

iex(7)> map = %{name: "shahryar" , likes: "programming", where: “dallas"}
%{likes: "programming", name: "shahryar", where: "dallas"}

در دستور بالا ما یک مپ ساده ساختیم . حالا زمان این هست که فقط کلید های این مپ را چاپ کنیم

iex(8)> Map.keys map

[:likes, :name, :where]

حالا بیاییم بازی رو ببریم روی نمایش ارزش ها فقط

iex(9)> Map.values map

["programming", "shahryar", "dallas"]

برای فراخوانی چیکار باید بکنیم ؟ خیلی ساده با دو روش دوست داشتنی . دست شما همیشه در map بازه

iex(10)> map[:name]

"shahryar"

iex(11)> map.name

"shahryar"

حالا می خوایم چند کلید را از map ساخته شده حذف کنیم و بعد از اون چندتا جدید اضافه کنیم

iex(12)> map1 = Map.drop map, [:where, :likes]

%{name: "shahryar"}

iex(13)> map2 = Map.put map, :also_likes,"Ruby"

%{also_likes: "Ruby", likes: "programming", name: "shahryar",

  where: "dallas"}

حال بیاییم چند مرحله به جلوتر برویم و چند map ساخته شده را با هم اعتبار سنجی کنیم

iex(15)> Map.has_key? map1, :where

false

iex(16)> {value, updated_map } = Map.pop map2, :also_likes

{"Ruby", %{likes: "programming", name: "shahryar", where: "dallas"}}

iex(17)> Map.equal? map, updated_map

true

چطور بود ؟ این ها همه اش گوشه ای از امکانات map برای یک برنامه نویس elixir می باشد

پترن ماچین و به روز رسانی Map

همیشه سوال می شه که آیا این کد هارو زدی ؟ یا مثلا این کد ها را برای map نوشتی ؟ برای پاسخ به این سوالات به متن زیر دقت کنید

iex(28)> person1 = %{ name: "shahryar" , height: 1.88}

%{height: 1.88, name: "shahryar"}

آیا این یک ورودی برای کلید :name می باشد ؟

iex(29)> %{ name: a_name } = person1
%{height: 1.88, name: "shahryar"}
iex(30)> a_name
"shahryar"

آیا این ورودی ها برای کلید های :name و height می باشد؟

iex(31)> %{ name: _ , height: _ } = person1
%{height: 1.88, name: "shahryar"}

آیا ورودی برای :name که ارزش آن “shahryar” باشد داریم ؟

iex(32)> %{ name: "shahryar" } = person1
%{height: 1.88, name: "shahryar"}

آیا map هیچ کلیدی به نام :weight دارد یا خیر؟

iex(33)> %{ name: _ , weight: _ } = person1
** (MatchError) no match of right hand side value: %{height: 1.88, name: "shahryar"}

کل دستورات :

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

people = [
  %{ name: "Grumpy",    height: 1.24 },
  %{ name: "Dave",      height: 1.88 },
  %{ name: "Dopey",     height: 1.32 },
  %{ name: "Shaquille", height: 2.16 },
  %{ name: "Sneezy",    height: 1.28 }
]

IO.inspect(for person = %{ height: height } <- people, height > 1.5, do: person)

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

کد :

به کد زیر توجه کنید :

people = [
  %{ name: "Grumpy",    height: 1.24 },
  %{ name: "Dave",      height: 1.88 },
  %{ name: "Dopey",     height: 1.32 },
  %{ name: "Shaquille", height: 2.16 },
  %{ name: "Sneezy",    height: 1.28 }
]

defmodule HotelRoom do
  def book(%{name: name, height: height})
  when height > 1.9 do
    IO.puts "Need extra long bed for #{name}"
  end
  def book(%{name: name, height: height})
  when height < 1.3 do

    IO.puts "Need low shower controls for #{name}"
  end
  def book(person) do

    IO.puts "Need regular bed for #{person.name}"

  end
end
# Enum.each(people, fn(x) -> HotelRoom.book(x) end)
people |> Enum.each(&HotelRoom.book/1)

تحلیل آخرین خط کد و کدی که بالایش کامنت شده به وسیله کاربر @samdvr

Enum.each(people, fn(x) -> HotelRoom.book(x) end)
این معادل 
people |> Enum.each(&HotelRoom.book/1)

هستش.
<|
یا پایپ نتیجه قسمت چپ به عنوان اینپوت اول سمت راست وارد میکنه

"Hi" |> IO.puts
IO.puts "Hi"

& یک سینتکس که بهش میگن capture operator
اگر یک ارگومنت به anonymous function میخواهید بدید بجای نوشتن
میتونید از capture operator استفاده کنید
1/
نشانگر arity هست arity یعنی تعداد ارگومنت یک فانکشن میگیره اگر فانکشن دو ارگومنت میگیره arity ۲داره دلیل وجود این نشانه به دلیل وجود pattern matching در الیکسیر که یک فانکشن میتونه با Arity مختلف بدنه مختلف داشته باشه
Capture operator همیشه به arity نیاز داره

منبع :
https://dockyard.com/blog/2016/08/05/understand-capture-operator-in-elixir
مشکل در درک قطعه کد elixir

پیونشت : از دوست خوبمون بخاطر مشارکت تشکر می کنم .

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

شما نمی توانید در طول یک پترن ماچین به یک کلید متصل کنید . شاید این متن کمی گمراه کننده باشد برای همین به مثال های زیر توجه کنید

نوع درست :

%{2 => state} = %{ 1 => :ok , 2 => :error } 

state

و نوع اشتباه :

%{ item => :ok } = %{ 1 => :ok , 2 => :error }

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

** (CompileError) iex:4: illegal use of variable item inside map key match, maps can only match on existing variable by using ^item
    (stdlib) lists.erl:1354: :lists.mapfoldl/3

پترن ماچین می توانید با کلید متغیر منطبق باشید

وقتی به پایه پترن ماچین می رسیم , ما می توانیم ببنیم که pin operator چطور می تواند در بازی پترن ماچین نقش مهمی داشته باشد . ( سمت چپ پرتن ماچین )

به کد زیر توجه کنید :

iex(7)> data = %{ name: "shahryar" , state: "Tx", likes: "Swift" }
%{likes: "Swift", name: "shahryar", state: "Tx"}
iex(8)> for key <- [ :name, :likes ] do                           
...(8)> %{^key => value } = data                                  
...(8)> value                                                     
...(8)> end                                                       
["shahryar", "Swift"]

برای اطلاعات بیشتر در مورد pin operator می توانید به لینک زیر سر بزنید

اگر خلاصه بخواهیم در این مورد صحبت کنیم پین می تواند در ساخت یک پترن ماچین و مچ کردن با متغییر اصلی به شما کمک کند به کد های زیر توجه کنید

فکر می کنم با مثال بالا کامل متوجه pin و کار کرد آن شده باشید .

چطور map را به روز کنیم ( آبدیت )

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

ساده ترین راه به شرح زیر می باشد :

استراکچر

new_map %{old_map | key => value.. }

مثال زیر توجه کنید :

m = %{ a: 1, b: 2 , c: 3 }
%{ a: 1, b: 2 , c: 3 }



m1 = %{ m | b: "two" , c: "three" }
%{ a: 1, b: "two" , c: "three" }


m2 = %{ m1 | a: "one"}
%{a: "one", b: "two", c: "three" }

نکته : همانطور که قبلا هم به این نکته اشاره کردیم شما برای اضافه کردن به map باید از Map.put استفاده کنید

آشنایی با Structs

هروقتی که شما در الکسیر به %{} برخورد می کنید احتمال به این فکر می کنید که یک map دیدید . اما این نشون می ده که شما آشنایی زیادی با map ندارید . بیایید با یک مثال شروع کنیم . من می خوام یک map درست کنم که اسم داشته باشد !! اسم !! و همینطور یک سری ارزش های دیفالت . ولی تاحالا هر map که ساخته می شد anonymous بود یا ناشناس و ما به آن ارزش می دادیم . هرجا که نیاز بود سریع یک مپ می ساختیم و می بردیمش در یک آرگومنت فانکشن یا یک متغییر و بعد آن را در صورت نیاز صدا می زدیم .

ولی حال بیایید وارد دنیای Struct بشویم. قبل از ادامه توضیحات و همینطور ارائه چند شبهه کد می خوام یک نظر شخصی در مورد Struct خدمت شما عزیزان مطرح کنم . این تعریف از Struct بیشتر یک آشنایی هست نه موردی علمی . Struct برای من مثل یک مخزن می ماند که داخلش همه چیز یک بار تعریف شده است . مثل name و age و … و هر کدام هم دارای یک نوع type هستند مثلا age از نوع int و name از نوع string و در صورت نیاز برخی از آن ها یک عدد یا حرف پیشفرض نیز می گیرند . حال وقتی یک فرد می خواهد از استراکت استفاده کند بخاطر اینکه در یک چهارچوبی قرار دارد باید قوانین آن را نیز رعایت کند. و این مخزن بعد از پر شدن حال آماده این هست که برای شما خروجی پس بدهد . اما !! با راحت ترین روش ممکن .

باید توجه کنید Struct ها مثل یک چهارچوب می مانند و با defstruct شروع می شوند و در module تعریف می گردند.

به کد زیر توجه کنید و شما هم برای تمرین بیایید و یک فایل به نام defstruct.exs بسازید :

defmodule Subscriber do
  defstruct name: "", paid: false, over_18: true
end

حال زمان این هست که بیاییم و در iex با آن بازی کنیم .

اول یک نام فقط به استراکت وارد کنیم :

iex> s1 = %Subscriber{name: "shahryar" }
%Subscriber{name: "shahryar", over_18: true, paid: false}

حال دو پارامتر وارد کنیم :

iex> s2 = %Subscriber{name: "ruby" , over_18: false }
%Subscriber{name: "ruby", over_18: false, paid: fa

حالا بیاییم s2 را با یک پترن صدا کنیم :

iex> %Subscriber{name: a_name } = s2 
%Subscriber{name: "ruby", over_18: false, paid: false}
iex> a_name
"ruby"

حالا بیاییم مپ جدید به روز کنیم :

iex> s4 = %Subscriber{s2 | name: "mojtaba"}
%Subscriber{name: "mojtaba", over_18: false, paid: false}

یک آزمایش هم در رابطه با صدا زدن انجام بدیم :

iex> s4.name
"mojtaba"

حالا شما می پرسید واقعا چرا باید از Struct استفاده کنیم ؟ خیلی از موارد بالا دلیل استفاده از Struct هست ولی دلیل اصلی بخاطر رفتار مناسب و دقیق Struct هست که می تواند کد نویسی های شما را تمیز تر و کاراتر بکند.

دستورات بالا :

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

defmodule Attendee do

  defstruct name: "", paid: false, over_18: true

  def may_attend_after_party(attendee = %Attendee{}) do
    attendee.paid && attendee.over_18
  end

  def print_vip_badge(%Attendee{name: name}) when name !=  "" do
    IO.puts "Very cheap badge for #{name}"
  end

  def print_vip_badge(%Attendee{}) do
    raise "missing name for badge"
  end
end

در این بخش ما چند فانکشن دیگر درست کردیم که هر کدام یک سری موارد را چک می کنند . که کاملا مشخص می باشد .

iex> a1 = %Attendee{ name: "rails", over_18: true }
%Attendee{name: "rails", over_18: true, paid: false}
iex> Attendee.may_attend_after_party(a1)
false
iex> a2 = %Attendee{ name: "rails", over_18: true, paid: true }
%Attendee{name: "rails", over_18: true, paid: true}
iex> Attendee.may_attend_after_party(a2)                       
true
iex> Attendee.print_vip_badge(a2)
Very cheap badge for rails
:ok
iex> Attendee.print_vip_badge(a2)
Very cheap badge for rails
:ok
iex> a3 = %Attendee{ name: "", over_18: true, paid: true }     
%Attendee{name: "", over_18: true, paid: true}
iex> Attendee.print_vip_badge(a3)                         
** (RuntimeError) missing name for badge
    defstruct1.exs:16: Attendee.print_vip_badge/1

در آخر باید به این نکته اشاره کنم Struct به همین مقدار نمی باشد . دوباره با آن در فصل protocol ها برخورد خواهیم کرد

تمامی کد ها :

Struct های تو در تو

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

defmodule Customer do

  defstruct name: "", company: ""

end

defmodule BugReport do

  defstruct owner: %Customer{}, details: "", severity: 1

end

defmodule User do

  report = %BugReport{owner: %Customer{name: "Dave", company: "Pragmatic"}, details: "broken"}

  IO.inspect report

  report = %BugReport{ report | owner: %Customer{ report.owner | company: "PragProg" }}

  IO.inspect report

  IO.inspect update_in(report.owner.name, &("Mr. " <> &1))

end

اگر توجه کرده باشید به راحتی استراکت ها در داخل هم استفاده شده اند. حال بیاییم در iex یک متغییر بسازیم به صورت زیر :

report = %BugReport{owner: %Customer{name: "Dave", company: "Pragmatic"}, details: "b

حال شما برای دست یابی به ارزش کمپانی باید چه کدی بزنید ؟ report.owner.company بله درسته . به همین راحتی شما حتی اطلاعات را دسته بندی کردید و به زیبایی با کمترین خط ممکن فراخوانی می کنید.

به روز رسانی یکی از کلید ها

حال برای به روز رسانی استراکت های بالا باید چه کاری را انجام بدهید ؟ فکر کنید شما می خواهید report.owner.company را به روز رسانی کنید. و اولین فکری که تو ذهن شما می رد این هست که کل استراکت را بنویسید یا پایپ کنید یا دوباره و … خیلی از کار های دیگر که در مورد map انجام می دادید !
به نظر شما این راه کمی سخت نیست ؟ و همینطور دوباره نویسی بسیار زیای ندارد ؟

من به شما راه بهتری را پیشنهاد می دهم . فقط کافیست از put_in استفاده کنید

put_in(report.owner.company, "joom shoper")

خیلی جذاب شد و همینطور کاربردی ! این یک جادو نیست فقط یک کار ساده به وسیله ماکرو های داخل الکسیر .

نکته : برای به روز رسانی نام report.owner.name می توانید از کد زیر استفاده کنید

update_in(report.owner.name, &("MR. " <> &1)) 

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

نکته : شما می توانید تمامی موارد بالا را به صورت زیر نیز فراخوانی کنید

put_in(report[:owner][:company], "PragProg")

امکان دارد استراکت های تو در تو به صورت داینامیک درست گردد. یعنی اینکه کد بر اساس اطلاعات دیتابیس شما آن را ایجاد کند . دیگر به همین چند خط کد بسنده نکند . همین دلیل دید شما را می بندد و امکان جستجو یا انجام عملیات های جدید را از شما می گیرد !!؟
باید بگویم خیر . این درست نیست . الکسیر با ماکرو ها و فانکشن هایی که در داخل خود دارد کار را برای شما بسیار ساده تر چیزی که فکر می کنید کرده است

به صورت کلی ما از موارد زیر استفاده زیادی خواهیم داشت :

get_in
put_in
update_in
get_and_update_in

به کد زیر توجه کنید:

nested = %{
    buttercup: %{ 
      actor: %{
        first: "Robin",
        last:  "Wright"
      },
      role: "princess"
    },
    westley: %{
      actor: %{
        first: "Cary",
        last:  "Ewles"     # typo!
      },
      role: "farm boy"
    }
}
IO.inspect get_in(nested, [:buttercup])
# => %{actor: %{first: "Robin", last: "Wright"}, role: "princess"}
IO.inspect get_in(nested, [:buttercup, :actor])
# => %{first: "Robin", last: "Wright"}
IO.inspect get_in(nested, [:buttercup, :actor, :first])  
# => "Robin"
IO.inspect put_in(nested, [:westley, :actor, :last], "Elwes")
# => %{buttercup: %{actor: %{first: "Robin", last: "Wright"}, role: "princess"},
# =>     westley: %{actor: %{first: "Cary", last: "Elwes"}, role: "farm boy"}}

به همین راحتی شما هرچیزی که نیاز داشتید را دریافت و نمایش دادید

تمام کد های بالا :

به کد زیر توجه کنید :

ما می خواهیم هر کدام که در language حرف r دارد name آن ها را چاپ کنید .

authors = [
  %{ name: "José",  language: "Elixir" },
  %{ name: "Matz",  language: "Ruby" },
  %{ name: "Larry", language: "Perl" }
]
languages_with_an_r = fn (:get, collection, next_fn) ->
   for row <- collection do
     if String.contains?(row.language, "r") do
       next_fn.(row)
     end
   end
end
IO.inspect get_in(authors, [languages_with_an_r, :name]) 

#=> [ "José", nil, "Larry" ] 

کد :

ماژول های دسترسی

به تعدادی از توابع از قبل تعریف شده مثل get و get_and_update_in می گویند که برای شما کار هایی مثل فیلتر کردن را انجام می دهند . باید توجه داشت آن ها یکی از راه های سریع و ساده در الکسیر می باشند.

به کد های زیر توجه کنید . در آن ماژول های دسترسی استفاده شده است :

cast = [

  %{

    character: "Buttercup",

    actor: %{

      first: "Robin",

      last:  "Wright"

    },

    role: "princess"

  },

  %{

    character: "Westley",

    actor: %{

      first: "Cary",

      last:  "Elwes"

    },

    role: "farm boy"

  }

]




IO.inspect get_in(cast, [Access.all(), :character])

#=> ["Buttercup", "Westley"]




IO.inspect get_in(cast, [Access.at(1), :role])

#=> "farm boy"




IO.inspect get_and_update_in(cast, [Access.all(), :actor, :last],

                             fn (val) -> {val, String.upcase(val)} end)

#=> {["Wright", "Ewes"],

#    [%{actor: %{first: "Robin", last: "WRIGHT"}, character: "Buttercup",

#       role: "princess"},

#     %{actor: %{first: "Cary", last: "EWES"}, character: "Westley",

#       role: "farm boy"}]}

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

همانطور پیشنهاد می کنم موارد زیر را نیز ببنید :

https://media.pragprog.com/titles/elixir13/code/maps/access2.exs
https://media.pragprog.com/titles/elixir13/code/maps/access3.exs

توجه : شما می توانید با Access.pop به وسیله یک کلمه کلیدی ولیو مورد نظر را حذف کنید . در صورتی که کلید وجود نداشته باشد برای شما nil برگشت می دهد:

نمونه :

Access.pop(%{name: "elixir", creator: "valim"}, :name)
{"elixir", %{creator: "valim"}}

آشنایی با MapSet با چند مثال :

این بخش بسیار راحت می باشد برای آشنایی با ماژول MapSet فقط کافیست مثال های زیر را ببنید

iex(3)> set1 = 1..5 |>

...(3)> Enum.into(MapSet.new)

#MapSet<[1, 2, 3, 4, 5]>

iex(4)> MapSet.member? set1, 3

true

iex(5)> MapSet.member? set1, 6

false

iex(6)> set2 = 3..8 |> Enum.into(MapSet.new)

#MapSet<[3, 4, 5, 6, 7, 8]>

iex(7)> MapSet.union set1 , set2

#MapSet<[1, 2, 3, 4, 5, 6, 7, 8]>

iex(8)> MapSet.difference set1 , set2

#MapSet<[1, 2]>

iex(9)> MapSet.difference set2 , set1

#MapSet<[6, 7, 8]>

iex(10)> MapSet.intersection set2 , set1

#MapSet<[3, 4, 5]>

کد :

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

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

1 پسندیده

درود خدمت دوستان یک سوال در مورد struct داشتم این هست که آیا استفاده از آن ها مشکل ساز هست ؟ مثلا چون معرفی می شوند حافظه زیادی رو مصرف می کنند؟
با تشکر

خیر هیچ مشکلی ندارند . struct در واقع map هستند با چند فرق ظاهری

2 پسندیده

اگر ممکن باشه کمی از Stream و تفاوتش با Enum بگین .
مثلا اینکه Enum توابعش حریص هستن ولی Stream ها تنبل هستن و … یجایی هم خوندم Stream ها مثلا اگر از نت داده بیاد تا قیامت همچنان ادامه میدن به گرفتن داده ها و مثل Enum منتظر نمیشن تا کل داده ها بیاد بعد شروع کنه به پردازش.
نکته ای ک اینجا یکم نتونستم خوب درکش کنم در تفاوت مصرف حافظه این 2 بود و… اگر در این مورد روشنمون کنید ممنون میشم

سلام,
منظورتون از مشکل چیه؟
Struct ها مبتنی بر map هستند با این تفاوت که کیورد های Struct در زمان کمپایل چک میشه.
باید ببینید نیاز شما چیه و درست انتخاب کنید

2 پسندیده

اینجا دقت و efficiency مطرحه نه مصرف حافظه.

1 پسندیده

اخه تو همون کتابی هم که گفتین و تست هایی ک خودم کردم ب این نتیجه رسیدم . برای مثال اگر یک فایلو بخونیم بعدش split کنیم پاس بدیم به توابع ماژول Enum مصرف رم شدیدا بالا میره در حالی که اگر تبدیل کنیم به Stream بعدش بفرستیم به Enum مصرف رم خیلی کمتر میشه هرچند سرعتش یه کوچولو کمتر میشه ولی دیگه انقد به هم دیگه داده پاس نمیدن.البتع نویسنده توصیه کرده که از Stream وقتی قراره از شبکه داده بیاد یا مثلا قراره از سنسورهای دما مقدار رو بگیرین استفاده کنید ولی من بازم دقیق متوجه نشدم. اگر اینطوری باشه و Enum برای مقدارهای کمی بزرگ به ازای هربار پاس دادن به یه تابع دیگه دوباره همین مقدارو بفرسته اون وقت یه فایل متنی 1 مگابایتی با چندبار پاس دادن ممکنه تا 500 مگابایت فضا بگیره . اگر در اشتباهم ممنون میشم اصلاح کنید و متوجه بشم

یادم نرفته کتاب Programming Elixir
Dave Thomas
و صفحه 100 این مطلبو نوشته
کتاب واقعا قشنگینه و بعد خوندن 2 کتاب دارم اینو میخونم خیلی بهتر روشنم میکنه

پس خودتون تا حدودى جواب رو دارين

1 پسندیده

Stream Processing در الیکسیر بیشتر با https://github.com/elixir-lang/flow انجام میشه که برگرفته از Apache Spark و Akka Stream هستش

مستندات
https://hexdocs.pm/flow/Flow.html

1 پسندیده

بله فقط در مورد مصرف رم یکم بی اطلاع هستم . باید واقعا دقت کرد ب این موارد ؟میتونه یه لیست کمی بزرگ بر اثر این بی دقتی باعث بشه رم کاملا اشغال بشه یا مهم نیس ؟

ممنون بابت لینکا . Flow جالبه و علاوه بر چندتا تفاوتش تا جایی که متوجه شدم از concurency استفاده میکنه اگر اشتباه نکنم درسته؟ فکر کنم کار درستی نباشه Stream ها رو ول کنم و از همین الان برای هرچیزی دنبال کد و پکیج اماده باشم اگر بتونم Stream ها رو بهتر و عالی تر درک کنم استفاده از flow هم لذتبخشتر و حرفه ای تر میشه

مهم هست اما در اولويت نيست،

1 پسندیده

من فکر می کردم که چون داره مثل یک فانکشن تعریف می شه . در یک ماژول حتما داره حافظه بیشتری رو مصرف می کنه

بهتر بگم

در swift شما می یایید کلاس درست می کنید و داخلش چند متغییر درست می کنید و حالا اطلاعات رو به صورت آرایه می ریزید توش و صفحه به صفحه انتقال می دید بعد به صورت آبجکت ازش می یارید بیرون

خوب راه بالا منطقی هست و اکثرا به همین صورت انجام می دند

ولی شما بجای این کار می تونید از ثابت ها به صورت سراسری استفاده کنید که خوب همه جا بالا می یاد ولی متاسفانه مشکلات حافظه رم ایجاد می کنه در ios بخاطر همین فقط در موارد ضروری ازش استفاده می کنند .

در موقع مطرح کردن سوال ذهنم رفته سر اون حالا چرا رو نمی دونم :joy:

1 پسندیده

در swift شما می یایید کلاس درست می کنید و داخلش چند متغییر درست می کنید و حالا اطلاعات رو به صورت آرایه می ریزید توش و صفحه به صفحه انتقال می دید بعد به صورت آبجکت ازش می یارید بیرون؟

نگرفتم :unamused:

1 پسندیده

مهم بود که جواب گرفتیم @toomaj جان بقش مهم نیست :grin:

2 پسندیده

پاسخ تو مخى:
آهان! يعنى مهم نيست كه من هم بگيرم؟ :scream:

پاسخ اصلى:
خدا رو شكر، موفق باشيد.:+1:

2 پسندیده