ค่าแบบต่างๆ ใน Elixir
ค่าแบบต่างๆ ใน Elixir | Functional Programming with Elixir | Part 2
Mon Jun 06 2022
muitsfriday.dev
ค่าแบบต่างๆ ใน Elixir | Functional Programming with Elixir | Part 2
Mon Jun 06 2022
ไม่ว่าเราจะเขียนโปรแกรมด้วยภาษาไหน การรู้ว่า value (ค่า) ที่ภาษานั้นสามารถใช้ได้มีรูปแบบอะไรบ้าง คือพื้นฐานที่สำคัญมากพอๆ กับปุฟเฟต์ที่เราต้องกินทุกสัปดาห์
Value (ค่า) ของ elixir นั้นจะแบ่งเป็น type ต่างๆ จะมีทั้งแบบที่เข้าใจง่ายๆ อีซี่ๆ ในแบบเรามักจะเห็นในภาษาอื่นอยู่แล้ว พวกนี้เราจะทำเป็นอ่านผ่านๆ เพื่อให้บทความที่น่าจะยาวเหยียดนี้สั้นที่สุด
Value Type | Description | Example |
---|---|---|
integer | ประเภทตัวเลขจำนวนเต็ม | 1 , 2 , 1000 , -10 |
float | ประเภทตัวเลขทศนิยม | 13.24 , 0.99 |
boolean | ประเภทค่าความจริง | true , false |
string | เก็บค่าที่เป็นตัวอักษร | “hello” , “many” , "" |
อะตอมคือ value type แบบนึงที่ค่าแต่ละค่าที่สร้างขึ้นมาจะไม่ไปซ้ำกับค่าในระบบอื่นๆ ถ้าในภาษาอื่นเราก็จะคุ้นเคยกับ symbol
ค่าอะคอมสร้างขึ้นมาได้ง่ายๆ ด้วยการใช้เครื่องหมาย :
นำหน้าค่าอะตอมที่ต้องการ
ตัวอย่าง
:hello
คือ atom ที่มีค่าเป็น hello แต่ไม่เกี่ยวอะไรกับ string "hello"
ทั้งสิ้น
ประโยชร์ของค่าประเภท atom คือการใช้แทนค่า constant ที่เรามักจะใช้ในภาษาอื่นๆ
ตัวอย่างใน JS
จากตัวอย่างเรากำหนด constant จากค่า string ซึ่งไม่ได้ผิดอะไรเพราะปกติเรามักจะใช้แบบนี้กัน แต่การเขียนแบบนี้จริงๆ มีข้อเสียหลักๆอยู่สองข้อ
foo(string)
ดูแล้วออกจะ mislead จากจุดประสงค์จริงๆของฟังก์ชันไป"type_a"
เข้าไปจะให้ผลเหมือนกับการส่ง constant TYPE_A
หมายความว่าตัวฟังก์ชันจะเข้า case default ก็ต่อเมื่อ x ไม่เท่ากับ "type_a"
และ "type_b"
แต่ความตั้งใจจริงๆ เราอาจจะอยากให้ string ทุกตัวลง default ไปต่างหาก เราสามารถแก้ปัญหานี้ได้ใน JS ด้วยตัวอย่างข้างล่างatom ที่เราจะเห็นบ่อยยิ่งกว่าโปรโมชันรายเดือนของเว็บขายของออนไลนคือ :ok
, :error
เพราะมักจะถูกใช้ในการเป็นตัวบอกสถานะของโปรแกรมได้ว่าทำงานถูกต้องไหม ปกติแล้วเวลาเขียนโปรแกรมเรามักจะชอบ return true
/ false
หรือ "ok"
เป็น string เพื่อบอกว่าโปรแกรมทำงานถูกต้อง แต่ใน elixir มี type พิเศษนี้ขึ้นมาเพื่อใช้ในการนี้โดยเฉพาะ ทำให้ไม่ต้องรับส่งเป็น string ตรงๆ
Note: value พิเศษสามตัว true
, false
, nil
จริงๆ แล้วเป็น atom นะ แต่ตัวภาษาแสนใจดียอมละ :
ออกสำหรับค่าเหล่านี้เพื่อให้เหล่าโปรแกรมเมอร์เขียนโค้ดได้มีความสุขขึ้น
ภาพนี้แสดงให้เห็นว่าจริงๆ true
และ :true
คือตัวเดียวกันนั่นแหละ
ถ้าเราไม่สามารถมอง 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 แบบนี้ก็เป็น value แบบนึงแล้วชีวิตในโลก functional จะง่ายขึ้นอีกเป็นกอง~
มีฟังก์ชันแล้วถ้าไม่สามารถเอามารันได้จะมีประโยชน์อะไรเนอะ และนี่ก็คือภาษา 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 คือ type ที่ทำตัวเป็นเหมือนกล่องเก็บค่าอื่นๆ เอาไว้ โดยค่าที่เก็บนั้นมีลำดับชัดเจน สามารถบอกได้ว่าตัวไหนมาก่อน มาหลัง วิธีประกาศก็แบบนี้
[1, "a", true, :ok]
ถ้าไครเคยเขียนโปรแกรมภาษาอื่นๆ มาก่อนก็จะแบบ โอวนี่มัน array นี่เอง~
จากตัวอย่างที่ได้ยกมาเป็น list ที่มีความยาว 4 เก็บค่าตามที่กำหนดเอาไว้
Tuple (ทูเปิ้ล) คือประเภทข้อมูลที่ทำตัวเป็นกล่องเก็บค่าอื่นๆ เอาไว้ และแน่นอนว่าผมไม่ได้ก็อปปี้มาผิดแล้วลืมเปลี่ยนคำจาก List ข้างบน เพราะถ้ามองแค่ concept ภายนอกมันเหมือนกันเลย มาดูวิธีสร้างค่านี้กันก่อน
{1, "a", true, :ok}
เปลี่ยนจาก []
เป็น {}
แค่นี้เองงงงง
ถ้าดูจากประโยชน์การใช้งานแล้วทั้ง list และ tuple ทำหน้าที่แทบจะ identical กันเลยเรียกได้ว่าใช้แทนกันได้ ข้อแตกต่างกันระหว่าง List และ Tuple นั้นจะอยู่ในระดับของโครงสร้างภาษา
List นั้นจะเก็บข้อมูลในแบบ linked list ซึ่งข้อมูลแต่ละตัวที่เก็บใน list จะมี reference ไปหาข้อมูลตัวถัดไป ซึ่งเวลาเราจะหาความยาวของ list นั้นจะต้องนับทีละอันตั้งแต่เริ่ม list (เพราะเรามองได้ทีละช่อง)
Tuple จะเก็บข้อมูลทั้งก้อนลงใน memory ติดๆกัน เราจะรู้ความยาวของ tuple ได้ทันที ไม่ต้องไล่นับทีละตัว แต่ข้อเสียใหญ่ๆ คือเวลาเพิ่ม หรือลดข้อมูล เราต้องสร้างทั้ง tuple ขึ้นมาใหม่ (ถึงจะ share memory กับของเดิมก็เถอะนะ)
โดยส่วนตัวที่เจอๆ มาเรามักจะใช้ tuple กับการ return additional information เช่นเรามี function ติดต่อ http เพื่อเอา data สิ่งที่ return ออกมาจะเป็น {:ok, response}
อะไรแบบนี้เพื่อบอกสถานะว่าติดต่อได้สำเร็จหรือไม่
ตารางนี้คือสรุปรวม operator ที่เรามักจะต้องใช้บ่อยๆ
Operator | Description | Example |
---|---|---|
+ - * / | จัดการบวก ลบ คูณ หาร ตัวเลขธรรมดาๆ | 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 | เชื่อม logic | true 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 เลยแจ้