ค่าแบบต่างๆ ใน Elixir | muitsfriday.dev

web-logo-doge muitsfriday.dev

ค่าแบบต่างๆ ใน Elixir

ค่าแบบต่างๆ ใน Elixir | Functional Programming with Elixir | Part 2

Mon Jun 06 2022

Value type ต่างๆ ของ Elixir

ไม่ว่าเราจะเขียนโปรแกรมด้วยภาษาไหน การรู้ว่า value (ค่า) ที่ภาษานั้นสามารถใช้ได้มีรูปแบบอะไรบ้าง คือพื้นฐานที่สำคัญมากพอๆ กับปุฟเฟต์ที่เราต้องกินทุกสัปดาห์

Value (ค่า) ของ elixir นั้นจะแบ่งเป็น type ต่างๆ จะมีทั้งแบบที่เข้าใจง่ายๆ อีซี่ๆ ในแบบเรามักจะเห็นในภาษาอื่นอยู่แล้ว พวกนี้เราจะทำเป็นอ่านผ่านๆ เพื่อให้บทความที่น่าจะยาวเหยียดนี้สั้นที่สุด

Value TypeDescriptionExample
integerประเภทตัวเลขจำนวนเต็ม1 , 2 , 1000 , -10
floatประเภทตัวเลขทศนิยม13.24 , 0.99
booleanประเภทค่าความจริงtrue , false
stringเก็บค่าที่เป็นตัวอักษร“hello” , “many” , ""

Type ที่ซับซ้อนกว่า

Atom

อะตอมคือ value type แบบนึงที่ค่าแต่ละค่าที่สร้างขึ้นมาจะไม่ไปซ้ำกับค่าในระบบอื่นๆ ถ้าในภาษาอื่นเราก็จะคุ้นเคยกับ symbol

ค่าอะคอมสร้างขึ้นมาได้ง่ายๆ ด้วยการใช้เครื่องหมาย : นำหน้าค่าอะตอมที่ต้องการ

ตัวอย่าง

:hello คือ atom ที่มีค่าเป็น hello แต่ไม่เกี่ยวอะไรกับ string "hello" ทั้งสิ้น

create an atom in elixir

ประโยชร์ของค่าประเภท atom คือการใช้แทนค่า constant ที่เรามักจะใช้ในภาษาอื่นๆ

ตัวอย่างใน JS

using constant in javascript

จากตัวอย่างเรากำหนด constant จากค่า string ซึ่งไม่ได้ผิดอะไรเพราะปกติเรามักจะใช้แบบนี้กัน แต่การเขียนแบบนี้จริงๆ มีข้อเสียหลักๆอยู่สองข้อ

  • ค่าที่ใช้ input เข้ามาเป็น string หมายความว่าถ้าเกิดภาษาเรามี type ขึ้นมา signature ของ function จะเป็น foo(string) ดูแล้วออกจะ mislead จากจุดประสงค์จริงๆของฟังก์ชันไป
  • เรามีโอกาสที่จะ input ค่า string ที่ดั๊นไปตรงกับ constant value แล้วได้ผลไม่คาดคิดขึ้นเช่นจากตัวอย่าง การส่ง string "type_a" เข้าไปจะให้ผลเหมือนกับการส่ง constant TYPE_A หมายความว่าตัวฟังก์ชันจะเข้า case default ก็ต่อเมื่อ x ไม่เท่ากับ "type_a" และ "type_b" แต่ความตั้งใจจริงๆ เราอาจจะอยากให้ string ทุกตัวลง default ไปต่างหาก เราสามารถแก้ปัญหานี้ได้ใน JS ด้วยตัวอย่างข้างล่าง

using constant in javascript

atom ที่เราจะเห็นบ่อยยิ่งกว่าโปรโมชันรายเดือนของเว็บขายของออนไลนคือ :ok , :error เพราะมักจะถูกใช้ในการเป็นตัวบอกสถานะของโปรแกรมได้ว่าทำงานถูกต้องไหม ปกติแล้วเวลาเขียนโปรแกรมเรามักจะชอบ return true/ falseหรือ "ok" เป็น string เพื่อบอกว่าโปรแกรมทำงานถูกต้อง แต่ใน elixir มี type พิเศษนี้ขึ้นมาเพื่อใช้ในการนี้โดยเฉพาะ ทำให้ไม่ต้องรับส่งเป็น string ตรงๆ

Note: value พิเศษสามตัว true, false, nil จริงๆ แล้วเป็น atom นะ แต่ตัวภาษาแสนใจดียอมละ : ออกสำหรับค่าเหล่านี้เพื่อให้เหล่าโปรแกรมเมอร์เขียนโค้ดได้มีความสุขขึ้น

ภาพนี้แสดงให้เห็นว่าจริงๆ true และ :true คือตัวเดียวกันนั่นแหละ

true is an atom in elixir

Function (anonymous)

ถ้าเราไม่สามารถมอง function เป็นค่าๆ นึงได้ภาษานั้นก็คงไม่ใช่ภาษา functional แน่ๆ

elixir เราสามารถสร้าง value ที่เป็นประเภท function ขึ้นมาได้ด้วยการสั่ง fn ... end เราจะมีชื่อเรียก function ที่ถูกสร้างขึ้นมาด้วยวิธีนี้ว่าฟังก์ชันไร้ชื่อ (anonymous function) elixir มีการแยกฟังก์ชันปกติและฟังก์ชันแบบไร้ชื่อออกจากกัน ตอนนี้โปรดใจเย็นและมาดูที่ฝั่งไร้ชื่อกันก่อน (function แบบมีชื่อจะอธิบายตอนหลัง ขอเวลาอีกไม่นาน..)

สิบปากว่าไม่เท่าตาเห็นตัวอย่าง เราลองมาสร้าง anonymous function กันง่ายๆ สักตัว

fn a, b -> a + b end

เป็น function ที่รับ input 2 ตัวชื่อ a และ b และ return ค่า a + b ออกมา

function in elixir

จงมองว่า function แบบนี้ก็เป็น value แบบนึงแล้วชีวิตในโลก functional จะง่ายขึ้นอีกเป็นกอง~

วิธีการ evaluate ค่าจาก anonymous function

มีฟังก์ชันแล้วถ้าไม่สามารถเอามารันได้จะมีประโยชน์อะไรเนอะ และนี่ก็คือภาษา functional ซะด้วยในการรัน function แทนที่เราจะมองว่าเป็นการสั่งให้ชุดคำสั่งทำงาน ในภาษาโอปป้า functional style เราจะใช้คำว่า evalute แทนมองว่าเป็นการหาค่า output จาก input ที่กำหนดเข้าไป

ในที่นี้เราจะสร้าง annonymous function ขึ้นมาและทำการ evaluate function นั้นทันที ตามตัวอย่างนี้

(fn a, b -> a + b end).(3, 10)

จากตัวอย่าง เราสามารถ evaluate anonymous function ได้ด้วยการใช้ .() (ต้องเติมจุดก่อนด้วยนะ) แล้วก็ใส่ argument/input เข้าไป ในตัวอย่างใส่ 3, 10 ลงไป evalute ออกมาได้ 13

List

List คือ type ที่ทำตัวเป็นเหมือนกล่องเก็บค่าอื่นๆ เอาไว้ โดยค่าที่เก็บนั้นมีลำดับชัดเจน สามารถบอกได้ว่าตัวไหนมาก่อน มาหลัง วิธีประกาศก็แบบนี้

[1, "a", true, :ok]

ถ้าไครเคยเขียนโปรแกรมภาษาอื่นๆ มาก่อนก็จะแบบ โอวนี่มัน array นี่เอง~

จากตัวอย่างที่ได้ยกมาเป็น list ที่มีความยาว 4 เก็บค่าตามที่กำหนดเอาไว้

Tuple

Tuple (ทูเปิ้ล) คือประเภทข้อมูลที่ทำตัวเป็นกล่องเก็บค่าอื่นๆ เอาไว้ และแน่นอนว่าผมไม่ได้ก็อปปี้มาผิดแล้วลืมเปลี่ยนคำจาก List ข้างบน เพราะถ้ามองแค่ concept ภายนอกมันเหมือนกันเลย มาดูวิธีสร้างค่านี้กันก่อน

{1, "a", true, :ok}

เปลี่ยนจาก [] เป็น {} แค่นี้เองงงงง

แล้วมันจะมีไว้ทำไมสองแบบ

ถ้าดูจากประโยชน์การใช้งานแล้วทั้ง list และ tuple ทำหน้าที่แทบจะ identical กันเลยเรียกได้ว่าใช้แทนกันได้ ข้อแตกต่างกันระหว่าง List และ Tuple นั้นจะอยู่ในระดับของโครงสร้างภาษา

List นั้นจะเก็บข้อมูลในแบบ linked list ซึ่งข้อมูลแต่ละตัวที่เก็บใน list จะมี reference ไปหาข้อมูลตัวถัดไป ซึ่งเวลาเราจะหาความยาวของ list นั้นจะต้องนับทีละอันตั้งแต่เริ่ม list (เพราะเรามองได้ทีละช่อง)

list in elixir

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

tuple in elixir

โดยส่วนตัวที่เจอๆ มาเรามักจะใช้ tuple กับการ return additional information เช่นเรามี function ติดต่อ http เพื่อเอา data สิ่งที่ return ออกมาจะเป็น {:ok, response} อะไรแบบนี้เพื่อบอกสถานะว่าติดต่อได้สำเร็จหรือไม่

Operator ที่ใช้กันบ่อยๆ

ตารางนี้คือสรุปรวม operator ที่เรามักจะต้องใช้บ่อยๆ

OperatorDescriptionExample
+ - * /จัดการบวก ลบ คูณ หาร ตัวเลขธรรมดาๆ1 + 2

10 / 7
++ --ต่อ List และ ลบของออกจาก List[1, 2, 3] ++ [4, 5] ได้ [1, 2, 3, 4, 5]

[1, 2, 3] -- [2] ได้ [1, 3]
<>ต่อ string"hello" <> "world" ได้เป็น "hello world"
and or notเชื่อม logictrue and true ได้ true

true or false ได้ false
``
&&&& ให้ค่าที่เป็น false หรือ nil ไล่จากซ้ายไปขวา ถ้าไม่เจอ false หรือ nil เลยจะได้ค่าสุดท้าย0 && 1 && 2 ได้ 2

0 && nil && 2 ได้ nil

false && nil && false ได้ false
!ทุกค่าที่ไม่ใช่ false nil จะ evaluate ได้ false!0 ได้ false

!1 ได้ false

!nil ได้ true
== != === !=== <= >= < >ใช้เปรียบเทียบ1 === 1.0 ได้ false

1 == 1.0 ได้ true

Note: การ compare ระหว่าง type มีลำดับดังนี้

number < atom < reference < function < port < pid < tuple < map < list < bitstring

เช่น

1 < :atom 

ได้ true

ดูเต็มๆ ที่นี่ https://elixir-lang.org/getting-started/basic-operators.html เลยแจ้