TL;DR
คำถามลอยไปลอยมา หลังจาก /me สามารถแทนค่าความจริงด้วย functions ได้แล้ว ข้อมูลอีกชนิดที่ยังเหลืออยู่คือ booleans หรือ ค่าความจริง True/False จะสามารถแทนด้วย funcitons ได้ยังไง? และตอนนี้คือคำตอบ
Booleans
/me จะแทนค่าความจริงด้วยฟังก์ชั่นได้ยังไง? คำถามลอยไปลอยมา
หนึ่งในคำตอบที่น่าสนใจ เกิดจากความจริงอย่างหนึ่ง คือ ค่าความจริง ไม่ว่าจะ จริง(True) หรือ เท็จ(False) มักใช้พร้อมๆ Conditional Statements ซึ่งโดยทั่วไปแล้ว มันคือ “ถ้า…เป็นจริง ฉันจะทำ… ถ้าไม่ฉันจะทำ…“
ดูเหมือนว่าพฤติกรรมของ Booleans คือ เงื่อนไขสำหรับเลือก actions อันใดอันหนึ่ง จาก 2 เหตุการณ์ ดังนั้นเราสามารถใช้พฤติกรรมลักษณะนี้ในการสร้าง Booleans ด้วยฟังก์ชั่นที่รับตัวแปร 2 ตัว แล้วเลือกตัวใดตัวหนึ่งออกไป ดังนี้
function TRUE (x, y) {
return x;
}
function FALSE (x, y) {
return y;
}
TRUE เป็นฟังก์ชั่นที่ return ตัวแปรแรกเสมอ และ FALSE เป็นฟังก์ชั่นที่ return ตัวแปรที่สองเสมอ เมื่อเปลี่ยนเป็น arrow functions จะได้ดังนี้
const TRUE = (x)=>(y)=>x;
const FALSE = (x)=>(y)=>y;
ทำนองเดียวกับกับตัวเลข /me จำเป็นต้องสร้าง to_boolean เพื่อใช้ในเปลี่ยนฟังก์ชั่นค่าความจริงพวกนี้ไปเป็น native javascript
function to_boolean(b) {
return b(true)(false)
}
ตรวจสอบอีกที
>> to_boolean(TRUE)
=> true
>> to_boolean(FALSE)
=> false
#ง่ายกว่างี้มีอีกไหม? #ง่ายกว่างี้มีอีกไหม? #ง่ายกว่างี้มีอีกไหม? #ง่ายกว่างี้มีอีกไหม? #ง่ายกว่างี้มีอีกไหม?
เมื่อได้ Booleans แล้ว เครื่องมือต่อมาที่ขาดไม่ได้คือ IF “ถ้า….ถ้าใช้ แล้ว….ถ้าไม่ แล้ว…..” และแน่นอนว่า จากรูปแบบของ Booleans แล้ว /me สามารถนำมาสร้าง IF ได้อย่างง่ายดาย
function IF(cond, x, y) {
return cond(x)(y)
}
แต่ความจริงแล้ว IF เป็นแค่ syntactic sugar ที่ช่วยอำนวยความสะดวกในการอ่านโค้ดและเขียนโค้ดเท่านั้นเอง เพราะว่าเราสามารถใช้ Booleans แทน IF ได้เลยทันที เพราะทั้งคู่ใช้ concept เหมือนกัน
>> IF(TRUE)("A")("B")
=> "A"
>> TRUE("A")("B")
=> "A"
>> IF(FALSE)("A")("B")
=> "B"
>> FALSE("A")("B")
=> "B"
ด้วยเงื่อนไขที่เหมือนกันนี้ เรายังสามารถ refactoring IF ให้มีความง่ายมากขึ้น
function IF(cond) {
return cond
}
// Arrow functions
const IF = (b)=>b;
เพราะว่า ทุกครั้งที่ IF(boolean)(ActionThen)(ActionElse) เทียบเท่ากับ boolean(ActionThen)(ActionElse) ดังนั้น IF(boolean) เทียบเท่ากับ boolean
#ฮูเร้ #ฮูเร้ #ฮูเร้ #ฮูเร้ #ฮูเร้ #ฮูเร้
และนี้ ก็เป็นอีก checkpoint สำหรับ isPrime(p)
const isPrime = (p)=>{
for i in range(TWO,p):
IF(p%i==0)( return "Not Prime")(null)
return p
}
// โค้ดดูเปลี่ยนไปจากเดิมเยอะ เพราะว่า /me เปลี่ยนจาก psudo-code เป็น javascript ที่ควรจะเป็น แต่ดูเหมือนว่าก็ยังไม่เป็นรูปเป็นร่างเท่าที่ควร เฮ้ออออ~ #สู้ต่อไปทาเคชิ
หลังจากที่เราสร้าง TRUE, FALSE และ IF ได้แล้ว /me อยากจะแถมเครื่องมืออีกอย่างที่น่าสนใจ(แต่ isPrime ของเราไม่ต้องใช้มัน) และมันก็คือ
Logic Operations
สำหรับโปรแกรมเมอร์บางคนอาจจะงงๆ เพราะมักคุ้นเคยกับ &&, ||, ! หรือ สำหรับสาวก python ก็จะใช้เป็นคำว่า and, or, not
เราสามารถ implement ได้ดังนี้
const AND = (a)=>(b)=>a(b)(a);
const OR = (a)=>(b)=>a(a)(b);
const NOT = (c)=>(a)=>(b)=>c(b)(a); //Applicative Order
// const NOT = (p)=>p(FALSE)(TRUE); //Normal Order
const XOR = (a)=>(b)=>a(NOT(b))(b);
สามารถอธิบายง่ายๆ โดยใช้หลักของ logic ทั่วไป
พฤติกรรมของ AND
สร้างจากความจริง 2 ข้อคือ
>> false && B
=> false
>> true && B
=> B
เมื่อ a เป็น FALSE แล้วแปลว่า “ไม่ว่ายังไงก็ตาม expression นี้ ก็เป็น false” จึงเลือกให้ return a ซึ่งมีค่าเป็น FALSE
เมื่อ a เป็น TRUE แล้วก็เลือก return b
พฤติกรรมของ OR
สร้างจากความจริง 2 ข้อคือ
>> true || B
=> true
>> false || B
=> B
- ทำนองเดียวกับ AND -
พฤติกรรมของ XOR
สร้างจากสัจพจน์ a xor b => (a and not b) or (not a and b) ดังนั้น
ถ้า a เป็น TRUE แล้ว จะเหลือแค่ (a and not b) ดังนั้น เราจึง return (not b)
ถ้า a เป็น FALSE แล้ว จะเหลือแค่ (not a and b) ดังนั้น เราจึง return (b)
และ สุดท้าย logic NOT
สังเกตว่ามันมีการสร้าง 2 แบบ สำหรับ Application order และ Normal order แล้วทั้ง 2 คืออะไร?
แน่นอนว่าหลายคนอาจสงสัยว่า Application order และ Normal order ต่างกันยังไง? ทั้งๆที่ทั้ง 2 NOT มีพฤติกรรมเหมือนกันคือการกลับค่าความจริง TRUE => FALSE และ FALSE => TRUE
ทั้งสองความลำดับการให้ความสำคัญระหว่าง arguments และ caller function โดย
- Normal order เลือกที่จะรัน caller function ก่อนที่จะไปรันแต่ละ arguments
- Application order เลือกที่จะรัน arguments ทุกค่าให้หมดก่อน แล้วจึงรัน caller function
ตัวอย่างง่ายๆ เช่น
// สมมติว่ามี functions เหล่านี้
// Normal order (Lazy evaluation)
>> double(average(2, 4))
=> plus (average(2, 4), average(2, 4))
=> plus (divide(plus(2, 4), 2), average(2, 4))
=> plus (divide(6, 2), average(2, 4))
=> plus (3, average(2, 4))
=> plus (3, divide(plus(2, 4), 2))
=> plus (3, divide(6, 2))
=> plus (3, 3)
=> 6
// Application order
>> false || B
>> double(average(2, 4))
=> double(divide(plus(2, 4), 2))
=> double(divide(6, 2))
=> double(3)
=> 6
ดูเหมือนว่า Application order เป็นสิ่งที่ดีกว่า เพราะว่า มันลดจำนวนครั้งในการรันคำสั่งได้มากกว่า แต่ในบางกรณี ก็ไม่ดีอย่างที่คิด เพราะว่า Application order ต้องรันทุกๆ arguments ก่อนซึ่งบางครั้งเราไม่จำเป็นขนาดนั้น เพราะเราอาจจะไม่จำเป็นต้องใช้มัน
>> const IF = (cond, funcA, funcB)=> {
... if(cond)
... return A;
... return B
... }
// Normal order (Lazy evaluation)
>> IF(true, "hello", (1/0));
=> "hello"
// Application order
>> IF(true, "hello", (1/0));
=> IF(true, "hello", ERROR);
=> ERROR
แต่เราไม่มีสิทธิเลือกหรอกนะว่า จะใช้ Evaluation order แบบไหน เพราะว่าแต่ละภาษาเลือกมาให้เราใช้เรียบร้อยแล้ว และ javascript เลือก Application order นั้นเอง
กลับมาต่อกับเรื่อง NOT
พฤติกรรมของ NOT
มีลักษณะต่างกับอันอื่น คือ มันจะสร้าง booleans ตัวใหม่มาแทนตัวเก่า ที่สลับ argumants กับตัวเดิม และนั้นก็คือ not
จบแล้วสำหรับ data type พื้นฐานอย่างค่าความจริง true, false และตัวดำเนินการทางลอจิก อย่าง AND, OR สุดท้ายก็เป็นหัวข้อที่เกินมาอย่างงงๆ Evaluation order
สำหรับตอนต่อไป /me จะพาไปเจอกับคำสั่งที่ขาดไม่ได้ นั้นก็คือคำสั่ง “เปรียบเทียบ”
อ่านตอนอื่นๆได้ที่
- EP1: Introduction
- EP2: Numbers
- EP3: Arithmetic operators
- EP4: Booleans
- EP5: Predicates & Comparison Operators
- EP6: Recursion
- EP7: List
- EP8: String
- EP9: Epilogue * ยังไม่เขียน
ต้นฉบับ Programming with Nothing
PS2.โค้ดบางส่วนในบทความ อาจจะรันไม่ได้จริงๆ เพราะ javascript ไม่ได้เปลี่ยน multiple argument function เป็น nested single-argument function อัตโนมัติ อย่างในภาษา Haskell หรือ Lips ดังนั้นฟังก์ชั่นหลายตัวแปร จะต้องแปลงให้อยู่ในรูปตัวแปรเดียวก่อน ถึงจะสามารถรันได้อย่างปกติ แต่อาจจะทำให้อ่านยากมากขึ้น /me จึงขออธิบายด้วย multiple argument function
แต่สามารถ ดูโค้ดส่วนต่างๆได้ที่ Github จะทยอย push ตามบล๊อคที่ทยอยเขียน
ขอบคุณ คำอธิบาย Evaluation Order เพิ่มเติมจาก CSE 505: Concepts of Programming Languages