Enum

เนื้อหาบางส่วนของบทแปลฉบับนี้อาจล้าสมัย
ต้นฉบับมีการเปลี่ยนแปลงรายย่อยเกิดขึ้นเป็นจำนวนมากนับตั้งแต่การปรับปรุงครั้งล่าสุด

ชุดของ algorithm สำหรับใช้งานกับ Enum

สารบัญ

Enum

module Enum มีมากกว่า 70 function ให้ใช้งาน ที่น่าสนใจคือ colllection ทั้งหมดนอกจาก tuple ที่เราได้เรียนกันมาใน บทที่แล้ว ล้วนแล้วแต่เป็น Enum

บทนี้จะครอบคลุมเพียงแค่ function บางส่วน แต่คุณก็สามารถไปศึกษาเพิ่มได้เอง เอาล่ะ มาทำลองเล่นใน IEx กันสักหน่อยดีกว่า

iex> Enum.__info__(:functions) |> Enum.each(fn({function, arity}) ->
...>   IO.puts "#{function}/#{arity}"
...> end)
all?/1
all?/2
any?/1
any?/2
at/2
at/3
...

จากที่ลองจะเห็นว่ามี function มากมายให้เราใช้งานจริง ๆ Enum นับเป็น core ของการเขียนโปรแกรมแบบ functional แล้วมันก็มีประโยชน์มาก ๆ เลยทีเดียว

หากเราใช้มันร่วมกับสิ่งเยี่ยมยอดอื่น ๆ ของ Elixir มันจะทำให้ developer ทำงานได้อย่างมีประสิทธิภาพมากเลยทีเดียว

สำหรับรายชื่อ function ทั้งหมด สามารถเข้าไปอ่านเพิ่มได้ที่ Enum สำหรับ lazy enumeration ใช้โมดูล Stream.

all?

เมื่อเราใช้ all?/2 กับ Enum จำนวนมาก เราสามารถใช้งาน function กับ item ทั้งหมดของ collection ได้ โดยจะตอบ true เมื่อ item ใน collection ทั้งหมดเป็น true นอกจากนั้นจะเป็น false

iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end)
false
iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 1 end)
true

any?

any?/2 จะคืนค่า true ถ้ามีตัวใดตัวนึงใน collection เป็น true

iex> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 5 end)
true

chunk_every

ถ้าคุณต้องการแบ่ง collection เป็นกลุ่มเล็ก ๆ chunk_every/2 เป็น function ที่คุณกำลังตามหา

iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]

มีตัวเลือกเล็กน้อยสำหรับ chunk_every/4 แต่เราจะไม่พูดถึงมันตรงนี้ ดูเพิ่มเติมที่ the official documentation of this function

chunk_by

ถ้าเราต้องการจับกลุ่ม collection ของเราด้วยค่าอื่นนอกเหนือจาก size ของมัน เราสามารถใช้ function chunk_by/2 ได้. มันรับ enumerable และ function เข้าไป และคืนค่าเมื่อ function สร้างกลุ่มใหม่ แล้วเริ่มทำกับตัวต่อไป

iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]
iex> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"]]

map_every

บางครั้งการดึงค่าเป็นก้อน ๆ จาก collection ไม่เพียงพอกับสิ่งที่เราต้องการ ในสถานการณ์นี้ map_every/3 มีประโยชน์มากในการเข้าถึง item ทุก ๆ nth ครั้ง เริ่มโดยเข้าถึงตัวแรกสุดก่อน

# ใช้ function ทุก ๆ 3 ค่า
iex> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]

each

มันอาจจะจำเป็นที่จะใช้ค่าแต่ละตัวใน collection โดยไม่สร้างค่าใหม่ สำหรับสถานการณ์นี้เราสามารถใช้ each/2 ได้

iex> Enum.each(["one", "two", "three"], fn(s) -> IO.puts(s) end)
one
two
three
:ok

หมายเหตุ: function each/2 จะคืนค่า atom :ok

map

เราสามารถใช้ function กับแต่ละ item เพื่อสร้างเป็น collection ใหม่ได้ด้วย function map/2

iex> Enum.map([0, 1, 2, 3], fn(x) -> x - 1 end)
[-1, 0, 1, 2]

min

min/1 หาค่าน้อยที่สุดใน collection

iex> Enum.min([5, 3, 0, -1])
-1

min/2 ทำเหมือนกัน แต่ถ้า enumerable เป็นค่าว่างมันจะคืนค่าของ function ที่เราใส่เข้าไปแทน

iex> Enum.min([], fn -> :foo end)
:foo

max

max/1 คืนค่ามากที่สุดของ collection:

iex> Enum.max([5, 3, 0, -1])
5

max/2 กลายเป็น max/1 ถ้า enumerable มีค่า เช่นเดียวกันกับ min/2 ที่จะกลายเป็น min/1:

Enum.max([], fn -> :bar end)
:bar

reduce

ด้วย function reduce/3 ทำให้เราสามารถรวบ collection ให้เหลือเพียงค่าเดียวได้ การที่จะรวบ collection เราสามารถส่ง accumulator (10 ในตัวอย่างด้านล่างนี้) เข้าไปใน function ถ้าไม่ได้กำหนด accumulator มันจะใช้ตัวแรกเป็น accumulator แทน

iex> Enum.reduce([1, 2, 3], 10, fn(x, acc) -> x + acc end)
16

iex> Enum.reduce([1, 2, 3], fn(x, acc) -> x + acc end)
6

iex> Enum.reduce(["a","b","c"], "1", fn(x,acc)-> x <> acc end)
"cba1"

sort

การ sort collection สามารถทำได้ง่ายนิดเดียวด้วย 2 function ที่ Elixir มีไว้ให้ใช้

sort/1 ใช้ term ของ Erlang ในการจัดลำดับ uses Erlang’s term ordering to determine the sorted order:

iex> Enum.sort([5, 6, 1, 3, -1, 4])
[-1, 1, 3, 4, 5, 6]

iex> Enum.sort([:foo, "bar", Enum, -1, 4])
[-1, 4, Enum, :foo, "bar"]

เราสามารถใช้ sort function ที่เราสร้างขึ้นมาเองได้ใน sort/2

# ใช้ function
iex> Enum.sort([%{:val => 4}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 4}, %{val: 1}]

# โดยไม่ใช้ function
iex> Enum.sort([%{:count => 4}, %{:count => 1}])
[%{count: 1}, %{count: 4}]

uniq_by

เราสามารถใช้ uniq_by/2 เพื่อลบค่าซ้ำใน enumerables ได้:

iex> Enum.uniq_by([1, 2, 3, 2, 1, 1, 1, 1, 1], fn x -> x end)
[1, 2, 3]