Do you want to pick up from where you left of?
Take me there

กลุ่มข้อมูล (Collections)

List, tuple, keyword list, และ map

Lists

list คือ collection พื้นฐานที่อาจประกอบไปด้วยข้อมูลหลาย type นอกจากนี้ list ยังสามารถเก็บข้อมูลที่ซ้ำกันได้อีกด้วย

iex> [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]

Elixir พัฒนา list collection เป็นแบบ linked list หมายความว่า complexity ในการเข้าถึงข้อมูลคือ O(n) ด้วยเหตุผลนี้เอง การเพิ่มข้อมูลต่อเข้าไปตอนต้นของ list จะเร็วกว่าการเพิ่มเข้าไปต่อด้านท้าย

iex> list = [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]
iex> ["π" | list]
["π", 3.14, :pie, "Apple"]
iex> list ++ ["Cherry"]
[3.14, :pie, "Apple", "Cherry"]

List Concatenation

list concatenation นั้น ใช้ operator ++/2

iex> [1, 2] ++ [3, 4, 1]
[1, 2, 3, 4, 1]

หมายเหตุเกี่ยวกับ ++/2 ที่ใช้ด้านบน ใน Elixir (และภาษาอย่าง Erlang ที่ภาษา Elixir ต่อยอดมา) function หรือ operator มี 2 ส่วน คือ ชื่อที่ตั้งให้ (ในที่นี้คือ ++) และ arity

arity คือส่วนที่เป็น core เมื่อเราพูดถึง Elixir (และ Erlang) มันคือจำนวนของ argument ที่ function นั้นรับ (ในที่นี้คือ 2) arity และชื่อที่ตั้งให้รวมกันกับเครื่องหมาย / เราจะพูดเกี่ยวกับเรื่องนี้มากขึ้นในภายหลัง ตอนนี้ให้พอเข้าใจความหมายของเครื่องหมายก็พอ

List Subtraction

การนำ list มาลบกัน ทำได้โดยใช้ --/2 และเราก็สามารถลบค่าที่ไม่มีอยู่ใน list ตั้งต้นได้

iex> ["foo", :bar, 42] -- [42, "bar"]
["foo", :bar]

สังเกตค่าที่ซ้ำกันใน list สำหรับทุกๆ ค่าใน list ทางขวา ตอนลบ ตัวแรกที่ปรากฏใน list ทางซ้ายมือจะถูกลบออก

iex> [1,2,2,3,2,3] -- [1,2,3,2]
[2, 3]

หมายเหตุ list subtraction ใช้ strict comparison เพื่อ match ค่าต่างๆ

Head / Tail

ตอนที่เราใช้ list เป็นเรื่องธรรมดาที่เราจะทำงานกับส่วน head กับส่วน tail ของ list โดยส่วน head ของ list คือ element ตัวแรก ในขณะที่ส่วน tail ของ list ก็คือ element ที่เหลือใน list นั้น ภาษา Elixir มี 2 function ที่มาช่วยตรงส่วนเหล่านี้คือ hd และ tl

iex> hd [3.14, :pie, "Apple"]
3.14
iex> tl [3.14, :pie, "Apple"]
[:pie, "Apple"]

นอกเหนือจาก function ที่กล่าวมาข้างต้นแล้ว เราสามารถใช้ pattern matching และ cons operator | เพื่อแยกส่วน head กับ tail ของ list ออกจากกัน เราจะเรียนรู้เกี่ยวกับเรื่อง pattern นี้ในบทเรียนต่อๆ ไป

iex> [head | tail] = [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]
iex> head
3.14
iex> tail
[:pie, "Apple"]

Tuples

tuple คล้ายกับ list แต่ข้อมูลใน tuple จะถูกเก็บติดๆ กันในหน่วยความจำ ทำให้การเข้าถึงข้อมูลเร็วกว่าแต่ก็จะเสียทรัพยากรไปในการแก้ไขข้อมูลค่อนข้างเยอะ เนื่องจากจะต้องคัดลอก tuple ใหม่ทั้งหมดเข้าไปในหน่วยความจำ

tuple ประกาศได้ด้วยเครื่องหมายปีกกา

iex> {3.14, :pie, "Apple"}
{3.14, :pie, "Apple"}

เป็นเรื่องปกติสำหรับ tuple ที่จะถูกใช้ในส่วนการ return ของ function ประโยชน์ของการใช้งานในลักษณะนี้จะถูกอธิบายให้ชัดเจนมากขึ้นตอนที่เราไปถึงบท pattern matching

iex> File.read("path/to/existing/file")
{:ok, "... contents ..."}
iex> File.read("path/to/unknown/file")
{:error, :enoent}

Keyword lists

keyword list และ map คือ associative collection ของภาษา Elixir ซึ่งใน Elixir keyword list คือ list พิเศษที่ประกอบไปด้วย tuple ที่มี 2 element โดย element ตัวแรกคือ atom

keyword list นั้นมี performance แบบเดียวกับ list

iex> [foo: "bar", hello: "world"]
[foo: "bar", hello: "world"]
iex> [{:foo, "bar"}, {:hello, "world"}]
[foo: "bar", hello: "world"]

3 ลักษณะพิเศษที่สำคัญของ keyword list คือ

ด้วยเหตุผลเหล่านี้ keyword list จึงถูกนิยมใช้สำหรับส่ง option เข้าไปที่ function

Maps

ใน Elixir นั้น map คือการเก็บข้อมูลแบบ “go-to” key-value ซึ่งจะไม่เหมือนกับ keyword list คือ key สามารถเป็น type อะไรก็ได้ และไม่จำเป็นต้องเรียงลำดับกัน

เราสามารถประกาศ map ได้โดยใช้ syntax %{}

iex> map = %{:foo => "bar", "hello" => :world}
%{:foo => "bar", "hello" => :world}
iex> map[:foo]
"bar"
iex> map["hello"]
:world

ใน ​Elixir เวอร์ชั่น 1.2 ตัวแปรสามารถถูกใช้เป็น key ของ map ได้

iex> key = "hello"
"hello"
iex> %{key => "world"}
%{"hello" => "world"}

ถ้ามีการเพิ่มค่าซ้ำเข้าไปใน map แล้ว map จะนำค่านั้นไปทับค่าที่มีอยู่ก่อนหน้า

iex> %{:foo => "bar", :foo => "hello world"}
%{foo: "hello world"}

อย่างที่เราเห็นจากผลลัพธ์ข้างต้น จะมี syntax พิเศษสำหรับ map ที่มีแต่ key ที่เป็น atom

iex> %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}
iex> %{foo: "bar", hello: "world"} == %{:foo => "bar", :hello => "world"}
true

นอกเหนือจากนี้ยังมี syntax พิเศษสำหรับการเข้าถึง key ที่เป็น atom อีกด้วย

iex> map = %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}
iex> map.hello
"world"

คุณสมบัติที่น่าสนใจอีกอย่างของ map คือ map มี syntax ของตัวเองสำหรับการอัพเดต

iex> map = %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}
iex> %{map | foo: "baz"}
%{foo: "baz", hello: "world"}
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!