Aug 30, 2012

ทำ Infinity List ใน Python

พอดีไปขุด Project Euler มาเล่น แล้วมันต้องได้ยุ่งกับพวก infinity sequence อย่างจำนวนเฉพาะ, ฟีโบนักชีบ่อยๆ ตอนแรกก็ใช้แค่ list ธรรมดาเนี่ยแหละ แล้วถ้า index ที่อยากได้ไม่มีก็ค่อยไปเรียก function แยกมาคำนวณแล้วเก็บข้อมูลกลับเข้า list เอา

ทีนี้เขียนไปเขียนมารู้สึกว่า code มันไม่สวยเอาซะเลย เพราะไอ้จำนวนเหล่านี้เนี่ย เวลาหามันจะกระโดดข้ามไป index ที่ต้องการไม่ได้อยู่แล้ว บวกกับจำนวนพวกนี้มันหาเพิ่มได้เรื่อยๆ ไม่มีที่สิ้นสุด ดังนั้นในสายตาของนักคณิตศาสตร์อย่างผม ถ้าเขียน prime[100] แล้วเจอฟ้องว่า index out of range นี่คงเซ็งน่าดู เลยคิดว่ามันน่าจะมีวิธี hack ให้ใช้ syntax นี้หาค่าใน index ที่ต้องการแม้ต้อนนี้จะยังไม่มีได้

ค้นไปค้นมาก็เจอ list.__getitem__ เลยจัดการลงมือลุย
class InfinityList(list):
    def __iter__(self):
        n = 0
        while True:
            yield self[n]
            n += 1

    def __repr__(self):
        return super(InfinityList, self).__repr__()[:-1] + ', ...]'


class fibonacci(InfinityList):
    def __getitem__(self, n):
        while True:
            try:
                return super(InfinityList, self).__getitem__(n)
            except IndexError:
                self.append( super(InfinityList, self).__getitem__(-1) +
                             super(InfinityList, self).__getitem__(-2) )

    def __init__(self):
        super(InfinityList, self).__init__([1, 1])


class prime(InfinityList):
    def __getitem__(self, n):
        while True:
            try:
                return super(InfinityList, self).__getitem__(n)
            except IndexError:
                c = super(InfinityList, self).__getitem__(-1)
                while True:
                    c += 2
                    for p in self[:]:
                        if not c % p:
                            break
                    else:
                        self.append(c)
                        break

    def __init__(self):
        super(InfinityList, self).__init__([2, 3])

fibonacci = fibonacci()
prime = prime()
กรอเวลาไปข้างหน้า 8 ชั่วโมง ก็มีของเล่นใหม่ตามที่แปะเป็นตัวอย่างไว้ด้านบน
  1. ใน __getitem__ นี่ลืมไปว่าถ้าแก้ตรงนี้แล้วมันจะทำ recursion กับตัวเอง ทำให้ไม่สามารถใช้ syntax อย่าง fibonacci[-1] เพื่อดึงเอาตัวเลขออกมาได้ ก็เลยต้องเลี่ยงไปเรียก method จาก super class เอา
  2. จะให้ syntax แบบ list[n] ทำงานโดยเรียก __getitem__ ต้อง inherit class มาแล้ว define ไว้ตอนสร้าง class เท่านั้นด้วย มาสั่ง class.__getitem__ = outer_function ไม่ได้ (ป้องกัน injection เข้าระบบ)
  3. ตัว __getitem__ มันรับ parameter ได้อันเดียวก็จริง แต่ไม่ใช่แค่ตัวเลขเท่านั้น เพราะยังมี slice object อีกด้วย (นึกถึงเวลาเขียน some_list[4:-4] ไอ้ [4:-4] นั่นแหละ slice object) ดังนั้นงานนี้ play safe ใส่ while True ไปดีกว่า ช้าหน่อยยอมรับได้
  4. สุดท้ายก็คืออยากให้ class พวกนี้เป็น static ไปซะ (ตอนแรกออกแบบว่าจะให้เขียนเป็น prime.numbers[n] ด้วยซ้ำ) แต่เหนื่อยมากขี้เกียจทำแล้ว เลยจับประกาศชื่อ fibonacci, prime ทับกับชื่อ class ตัวมันเองไปซะเลย ยังไงก็มีได้แค่ instance เดียวอยู่แล้วหนิ 555+
เพียงเท่านี้ ถ้าอยากได้ prime ตำแหน่งที่ 101 ก็แค่สั่ง
prime[100]
จบเลย ง่ายมั้ย? อยากลองเล่นเองแล้ว? ไปจิ้ม code ได้จากที่นี่เลย XD

Aug 28, 2012

สมการรัก

เขียน Haskell อยู่ดีๆ แล้วก็ได้ไอ้นี่ออกมา...
let me = (<3) u where (u,me) = (1,(<3))

Aug 22, 2012

อะไรคือ Y Combinator

ถึงตอนนี้เราคงทำ recursion บน lambda เป็นกันแล้ว (ถ้ายังไม่เป็น แนะนำให้อ่านตอนก่อน)

กลับไปดูอีกรอบ จะเห็นว่าตอนเรียก recursion ต้องใส่ชื่อฟังก์ชันตัวเองลงไปเป็นตัวแปรหนึ่งเสมอ
lambda f, x: 1 if x == 0 else x * f(f, x-1)
แล้วถ้าเราไม่อยากทำอย่างนี้หละ อยากจะเรียกแค่ f(x-1) เหมือนการเรียกใช้งานฟังก์ชันตามปรกติทั่วไป จะทำได้มั้ย?

ถอยหนึ่งก้าว กลับไปเขียน factorial แบบธรรมดาๆ ก่อน
f = lambda x: 1 if x == 0 else x * f(x-1)
ฟังก์ชันนี้ยังคงสามารถเรียกใช้ได้ เนื่องจากว่าเราจองชื่อ f ไว้ให้มันแล้ว (ซึ่งไม่ใช่สิ่งที่เราต้องการ) นั่นหมายความว่าชื่อ f ต้องมีปรากฏอยู่ใน scope นี้จึงจะทำ recursion ได้ และมันมีอีกวิธีนึงที่จะทำให้ f ปรากฏอยู่ภายใน scope นี้โดยไม่ต้องจองชื่อหรือส่งผ่านเป็นตัวแปรใน scope เดียวกัน คือ
lambda f: lambda x: 1 if x == 0 else x * f(x-1)
แต่การประกาศเช่นนี้จะทำให้เราไม่สามารถใช้ฟังก์ชันได้ทันที เพราะเราต้องเอาฟังก์ชั่นนี้ส่งไปเป็นตัวแปรให้ตัวมันเองเรื่อยๆ เช่น ถ้าต้องการหา 5 factorial ต้องเขียนออกมาอย่างนี้
(lambda f: lambda x: 1 if x == 0 else x * f(x-1))(
  (lambda f: lambda x: 1 if x == 0 else x * f(x-1))(
    (lambda f: lambda x: 1 if x == 0 else x * f(x-1))(
      (lambda f: lambda x: 1 if x == 0 else x * f(x-1))(
        (lambda f: lambda x: 1 if x == 0 else x * f(x-1))(
          (lambda f: lambda x: 1 if x == 0 else x * f(x-1))(
            (lambda whatever: 42)
          )
        )
      )
    )
  )
)(5)
นั่นหมายความว่าเราต้องการอะไรซักอย่าง ที่จะทำหน้าที่ generate ฟังก์ชันของเรานี้ออกมาเรื่อยๆ ไม่ว่าจะมีอะไรเกิดขึ้นก็ตาม นี่คือแนวคิดของ fixed-point combinator โดยมีตัวหนึ่งที่เป็นมีชื่อเสียงเป็นที่รู้จักกันมากคือ Y combinator ซึ่งเขียนได้ดังนี้
Y = lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v)))
(นิยามเต็มๆ คือ Y = λf.(λx.f (x x)) (λx.f (x x)) ซึ่งไม่จำเป็นต้องติดตัวแปร v มาด้วย)

ลองมาสำรวจการทำงานของมันดีกว่า จะเห็นได้ว่า
Y(g)(n)
-> (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(g)(n)
   # reduce: g -> f
-> (lambda x: g(lambda v: x(x)(v)))(lambda x: g(lambda v: x(x)(v)))(n)
   # reduce: (lambda x: g(lambda v: x(x)(v))) -> x
-> g(lambda v: (lambda x: g(lambda v: x(x)(v)))(lambda x: g(lambda v: x(x)(v)))(v))(n)
   # reduce: n -> v
-> g((lambda x: g(lambda v: x(x)(v)))(lambda x: g(lambda v: x(x)(v)))(n))
   # for inner g function, expand back to lambda: f <- g
-> g(lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v)))(g)(n))
   # by definition
-> g(Y(g)(n))
   # thus
-> g(g(Y(g)(n)))
-> g(g(g(Y(g)(n))))
-> g(g(g(g(...))))
วิธีเอามาใช้งานก็ไม่ยุ่งยาก เพียง
(lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(  # Y-comb
  lambda f:                                  # recursive function name
    lambda x: 1 if x == 0 else x * f(x-1)    # recursive function definition
  )(5)                                       # recursive function argument
ลองเปลี่ยนมาหา fibonacci บ้าง ก็ง่ายนิดเดียว
(lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(
  lambda f:
    lambda x: x if x <= 1 else f(x-1) + f(x-2)
  )(5)
ง่ายจริงป่ะ?

Aug 21, 2012

Recursive Lambda ความงามบนความงาม...

พอถล่ำลึกลงไปใน functional programming ซักพักหนึ่ง จะเริ่มเกิดคำถามขึ้นมาว่า
"แล้วเราจะทำ recursion บน lambda ได้หรือเปล่า?"
ฟังดูเผินๆ ไม่น่าเป็นไปได้ เพราะ lambda ทำให้ฟังก์ชันไม่มีชื่อ แต่การ recursion ต้องอาศัยชื่อของฟังก์ชันนั้นๆ เพื่อเรียกมันขึ้นมาทำงานเสมอ สมมติเช่นฟังก์ชันง่ายๆ ที่จะทำการยกกำลังสองเลขที่ได้รับมาทันที
(lambda x: x**2)(5)
นี่คือฟอร์มที่เรียบง่ายสุด และถ้าเราทดลองเพิ่มตัวแปรเข้าไปให้ฟังก์ชันนี้ (โดยไม่จำเป็นต้องใช้มัน) อาจจะเขียนฟังก์ชันนี้ออกมาได้ว่า
(lambda w, x: x**2)(999, 5)
แน่นอนว่ากรณีนี้เราไม่สนใจเลข 999 ที่ถูกนำไปเก็บไว้ยังตัวแปร w เลย ...แล้วเราก็คงจะถึงทางตัน ถ้าไม่สังเกตว่าตัวแปร w นั้นหนะ รับค่าอะไรเข้ามาก็ได้ (เพราะไม่ได้ใช้) นี่รวมไปถึงเราอาจจะยัด lambda เข้าไปให้มันก็ได้
(lambda w, x: x**2)(lambda u: 42, 5)
นั่นหมายความว่า lambda ไม่จำเป็นต้องไร้ชื่อเสียทีเดียว แน่หละมันอาจจะไร้ชื่อใน scope หนึ่งๆ แต่กับ scope ที่ใหญ่ขึ้นกว่าตัวมันแล้ว เราสามารถตั้งชื่อให้มันได้ ถึงตอนนี้จะเปลี่ยนชื่อตัวแปรกันซักหน่อย พร้อมกับเอาฟังก์ชันสำหรับคำนวณจากด้านบนมาใส่แทน lambda u: 42 ไปซะ
(lambda g, y: y**2)(lambda x: x**2, 5)
จะเห็นว่าเลข 5 จะถูกเก็บไว้ในตัวแปร y และคำนวณ y**2 ตรงๆ โดยที่ lambda x: x**2 ไม่ได้แสดงบทบาทอะไร แต่เนื่องจากฟังก์ชัน lambda x: x**2 ถูก scope ด้านหน้าแปะป้ายชื่อให้ว่า g เป็นที่เรียบร้อยแล้ว ดังนั้น เราสามารถส่งผ่านเลข 5 ให้ไปทำงานกับ lambda x: x**2 ได้ดังนี้
(lambda g, y: g(y))(lambda x: x**2, 5)
แม้ว่าตอนนี้เราจะสามารถเรียกชื่อของ lambda x: x**2 ใน scope ที่ใหญ่กว่าตัวมันได้ แต่มันก็ไม่มีประโยชน์เลยเพราะการจะทำ recursion นั้นต้องสามารถเรียกชื่อตัวมันเองใน scope ที่มันอยู่ได้ ดังนั้น เราจะใช้ท่าเดิมคือส่งผ่านเจ้า lambda x: x**2 นี้ต่อไปเรื่อยๆ ไม่ให้มันหายไป โดย
(lambda g, y: g(g, y))(lambda f, x: x**2, 5)
เท่านี้ ใน scope ของ lambda f, x: x**2 ก็สามารถเรียกตัวเองได้แล้ว (ชื่อว่า f) ถึงตอนนี้ก็ได้เวลาเขียน recursion ให้มัน อาจเริ่มจากฟังก์ชันง่ายๆ อย่าง factorial ก็ได้
(lambda g, y: g(g, y))(lambda f, x: 1 if x == 0 else x * f(f, x-1), 5)
สังเกตว่าการเรียก recursion ภายใน lambda นี้ ต้องส่งผ่านชื่อฟังก์ชันตัวเองเป็นตัวแปรพ่วงลงไปด้วยเสมอนะ

จบแล้ว แค่นี้แหละ recursion บน lambda ไม่ยากอย่างที่คิดเลยใช่มั้ยครับ ;)

Aug 20, 2012

เขียน Loop ยังไงดี?

ในการเขียนโปรแกรม เรามักต้องพบปะกับท่านี้เสมอๆ
text = input()
while text != 'exit':
    # do something with text
    # ...
    text = input()
ดูเผินๆ ก็ไม่น่ามีปัญหาอะไร แต่นี่จะทำให้โปรแกรมเมอร์สำคัญผิดกับ input ครั้งแรก หรือไม่งั้นก็มองโค้ดผ่านๆ แล้วนึกว่า input ด้านนอกนั้นจะไม่เกี่ยวกับกิจกรรมที่เกิดขึ้นใน while loop ที่ตามมาเลย (ยิ่งถ้าต้องมีการ pre-process input ก่อนที่จะเอาไป check while อีก) ทางแก้ง่ายๆ คือเปลี่ยนไปเขียนแบบนี้แทน
while True:
    text = input()
    if text == 'exit':
        break
    # do something with text
    # ...


อีกเรื่องที่ไม่ค่อยเกี่ยวกันเท่าไหร่คือ การรับข้อมูลจาก database มาแสดงผล ส่วนใหญ่จะทำท่านี้
$result = mysql_query($sql);
while ($row = mysql_fetch_row($result)) {
    echo $row[0], $row[1], $row[2];
}
ในทางปฎิบัติมันก็ใช้ได้เลย แต่ถ้ามาคิดๆ ดูแล้ว ข้อมูลที่ query ออกมาจาก database ได้นั้นควรจะมีจำนวนจำกัดเสมอ (ไม่มี database ไหนที่เก็บข้อมูลได้เป็น infinity เป็นแน่??) นั่นหมายความว่าทางทฤษฎีแล้ว มันไม่ควรเขียนด้วย while loop แต่ควรเขียนด้วย for loop (อาจเป็น for-each loop) เช่นนี้
// codeigniter active record
$query = $this->db->get($sql);
foreach ($query->result() as $row) {
    echo $row->title, $row->author, $row->content;
}
ท่านี้จะทำให้มีปัญหาด้าน memory (เพราะต้อง query ข้อมูลทั้งหมดออกมาเก็บไว้ใน array ก่อน แล้วจึงค่อยวนเข้าไปอ่านข้อมูลใน array นั้น) ซึ่งแก้ได้ที่ระดับ library โดยเปลี่ยนไปใช้ generator แทน array



ท้ายสุด กลับไปที่ while loop แบบแรก โดยมีเงื่อนไขเพิ่มเติมตรงที่อยากรู้ว่าเป็นการทำงานรอบที่เท่าไหร่ อาจเขียนเพิ่มเป็น
i = 0
while True:
    text = input()
    if text == 'exit':
        break
    # do something with text
    # ...
    print('loop no:', i, '; input is:', text)
    i += 1
จะเห็นว่ามี i = 0 เป็น initial statement อยู่นอก scope ของ while ตรงนี้จริงๆ ไม่มีปัญหาอะไรเลย แต่ถ้าอยากให้มันสวยขึ้นด้วยการจัดระเบียบ block สามารถใช้ประโยชน์ของ infinity generator ได้
from itertools import count

for i in count():
    text = input()
    if text == 'exit':
        break
    # do something with text
    # ...
    print('loop no:', i, '; input is:', text)
หรือยิ่งไปกว่านั้น ทำการเปลี่ยนรูปแบบการเขียนเพื่อให้ไม่ต้องตรวจสอบสำหรับ break ออกจาก loop แต่ไปตรวจตั้งแต่หัว loop เลยว่าหลุดออกจาก loop นี้หรือยัง อย่างนี้
from itertools import count

for i, text in zip(count(), iter(input, 'exit')):
    # do something with text
    # ...
    print('loop no:', i, '; input is:', text)
แนวคิดจะเปลี่ยนไปนิดนึงด้วย ตรงที่เราไม่ได้ใช้ while loop แล้ว (loop แต่ละครั้งไม่เชื่อมโยงกัน) เปลี่ยนมาเป็น for loop (แต่ละครั้งมีความเกี่ยวเนื่องกัน อย่างน้อยๆ ก็ index ที่ไล่กันไปเรื่อยๆ) นั่นเอง ซึ่งส่วนตัวแล้วผมชอบอย่างหลังมากกว่า

Aug 19, 2012

จัดระเบียบ Blog



สืบเนื่องจากเมื่อวานอ่าน blog ของ @phatpeth แล้วรู้สึกว่า theme มันสวยดี ... อันที่จริงเราเองก็ไปเล่นตั้งแต่วันแรกๆ ที่มันออกเลยนะ แต่ทีนี้มันติดปัญหาที่ว่า syntax highlighter ตัวเก่งของเราเนี่ย มันดันทำงานไม่ได้ในโหมดนี้ซะหนิ (กลัวว่าต้องไปใช้วิธี cap code เป็นภาพเอาเหมือนของ @iMacbaszii) เลยอู้ไว้ก่อน ไม่ยอมย้ายมาใช้แบบนี้ซักที

บังเอิญว่าวันนี้คิดไงไม่รู้ (ว่างจัด?) ตอนแรกว่าจะย้ายทั้ง blog ของเราไปอยู่บน GitHub Pages แล้ว เพราะถูกโฉลกกับ markdown มากๆ แต่ลองได้แป๊ปๆ รู้สึก learning curve มันสูงเกินไป อีกทั้งพวก comment เก่าๆ ก็ไม่น่าจะย้ายตามมาได้ด้วย

ตัดใจได้ก็เปลี่ยนมานั่งหาวิธี hack เอา syntax highlighter ใช้กับเจ้า blogger จนได้ (จริงๆ เปลี่ยนค่ายไปใช้ prettifier แหละ) ก็ต้องขอขอบคุณ Alex Conrad ผู้เขียน bootstrap-lib อันนี้ไว้ให้ด้วยเน้อ ;)

ตอนนี้ก็ติดอยู่อย่างเดียวตรงที่พื้นหลังของแต่ละ post มันเปลี่ยนเป็นสีเข้มๆ ไม่ได้ --- แต่ก็ช่างมันเหอะ จะได้ออกจากโลกมืดๆ ซะบ้าง หลังจากใช้สีดำเป็น default มาตั้งแต่ปี 5 ปีก่อน (ก่อนหน้านั้นอีกตอนอยู่ที่ spaces.live.com ใช้สีขาวนะเออ) ก็หวังว่าจะอ่าน blog กันได้สบายตาขึ้นนะครับ TwT

เสียดายอย่างสองอย่างตรงที่ theme dynamic view นี่มันแสดง facebook like box ไม่ได้แล้ว พวก feed aggregator ก็เอามาแปะข้างๆ ไม่ได้ (งานนี้ tutuor0x อดอัพตาม เพราะมีสัญญาผูกพันกับ blognone อยู่ว่าต้องแสดง feed แลกกัน) ก็น่าเสียดายเล็กน้อยเหมือนกันแต่ไม่เป็นไร

อ๋อ นอกจากเปลี่ยน theme แล้ว ยังจัดระเบียบ tag ใหม่ด้วย คือเมื่อก่อนเนี่ยใช้ระบบ category มาตลอด ซึ่งก็เข้าท่าดีเพราะสมัยนั้นเขียนเป็นหัวข้อๆ ไป แต่พักหลังนี้รู้สึกว่าเขียนกระจัดกระจายมากเลย คิดว่าเปลี่ยนมาใช้ tag น่าจะ work กว่านะ

ที่เหลือก็คงต้องหาภาพมาประกอบบทความแต่ละตอนบ้างแล้วสินะ ไม่งั้นเวลาดูแบบ flipcard แล้วจะรู้สึกโล้นๆ 555

Aug 18, 2012

ทำไม Functional Programming ถึงไม่ได้รับความนิยม


จากที่ @lewcpe คอมเมนท์ไว้ในตอนก่อน มานั่งคิดๆ ดูว่าทำไม functional programming ถึงไม่ได้รับความนิยมเท่าไหร่

คิดว่าสาเหตุใหญ่ๆ น่าจะเป็นเพราะปรกติแล้ว มนุษย์เราจะคิดการทำงานทาง "ซ้าย -> ขวา" คิดจากส่วนที่เล็กสุดออกไปยังระดับที่ใหญ่ขึ้นเรื่อยๆ เช่นเขียนโปรแกรมอย่างนี้

sh:
echo 'hello to the old world' | tr ' ' '\n' | sort -r | paste -sd ' '
ruby:
puts 'hello to the old world'.split.sort.reverse.join ' '
แต่ในการเขียนโปรแกรมแบบ functional programming การทำงานของจะเป็นในทิศทาง "ซ้าย <- ขวา" แทน ซึ่งขัดกับความรู้สึกของเรา

python:
print(' '.join(reversed(sorted('hello to the old world'.split()))))
haskell:
print (join " " . reverse . sort . splitOn " ") "hello to the old world"


อีกเรื่องที่ยากคงเป็นเพราะไม่มี for/while loop มีแต่ recursion ให้ใช้ อันนี้สาย imperative ทำใจลำบากพอควรเลย

ส่วนเรื่องอื่นๆ อย่าง immutable คงไม่มีปัญหาเท่าไหร่ อันที่จริงถ้าไม่สนว่าจะ optimize กันสุดๆ ไม่ว่าจะเขียนด้วยภาษาอะไรก็ควรยึดเรื่องนี้ไว้หน่อย มันทำให้ bug งี่เง่าหายไปได้หลายตัวเลยหละ :3

Aug 17, 2012

Function Composition กับ Programming


ตอนม.ปลาย คงเคยเห็นฟังก์ชั่นที่เขียนแทนด้วยสัญลักษณ์ g o f กันมาแล้ว ซึ่งมันหมายความง่ายๆ เช่นนี้
(g o f) (x) = g(f(x))
(นิยามเต็มๆ ของมันคือ ถ้า f: X -> Y และ g: Y -> Z แล้ว g o f: X -> Z ---- ที่ต้องนิยามเช่นนี้เพราะเราจะสามารถละการเขียน argument x ติดไปกับนิยามได้)

ในการเขียนโปรแกรม (โดยเฉพาะเชิง functional) เรามักต้องทำงานแบบฟังก์ชันต่อเนื่อง คือ output จาก function หนึ่ง จะถูกนำมาใช้เป็น input ให้ฟังก์ชันถัดไปเป็นลูกโซ่

เขียนอธิบายเป็นภาษาโปรแกรมได้คือ
first_input = x
first_output = f(first_input)
second_input = first_output
second_output = g(second_input)
y = second_output
หรือเพื่อไม่ให้เปลืองตัวแปร ทั้งหมดนี้สามารถย่อได้เหลือ
y = g(f(x))
คำถามคือ ถ้าเราต้องการประกาศแค่ฟังก์ชั่นที่ทำงานต่อกันไปเรื่อยๆ เช่นนี้ โดยที่ไม่ต้องการใส่ input ให้ฟังก์ชั่นโดยทันที เราจะทำอย่างไร?

ทางออกหนึ่งคือใช้ lambda เข้าช่วย
h = lambda x: g(f(x))
แล้วเวลาจะเรียกใช้ฟังก์ชั่นนี้ ก็แค่สั่ง
y = h(x)
ฟังดูง่ายดี แต่นึกดูอีกที ทำไมเราถึงต้องทำอะไรให้มันยุ่งยากด้วยการเอา lambda เข้ามาเกี่ยวด้วย?

ในภาษา imperative คงไม่มีทางเลือกอื่น แต่สำหรับภาษา functional จ๋าอย่าง Haskell เราสามารถเขียนแบบนี้ได้
let h = g . f
จะเห็นว่าง่ายดายเหมือนกับ g o f ตอนแรกเลย เวลาใช้ก็แค่
h x
หรือถ้าจะละการประกาศฟังก์ชั่น h ทิ้งไป เพื่อหาผลลัพท์ทันทีเลย ก็ทำได้โดย
(g . f) x
อ่าห์... นี่มันคณิตศาสตร์ชัดๆ!!

Aug 16, 2012

ความเคยชิน


คิดมานานแล้วว่านิ้วก้อยซ้ายมันใช้งานน้อยมากๆ และปุ่ม CapsLock ก็ไม่โดนใช้เลย น่าจะเอาปุ่มนี้ไว้ใช้เปลี่ยนภาษา

เดิมเคยเปลี่ยนภาษาด้วย ~ (Thai-Windows style) มาก่อน ต่อมาใช้ Linux ที่ต้องใช้ปุ่ม ~ บ่อยๆ เลยเปลี่ยนเป็น Alt+Shift ซึ่งมันจะมีปัญหา lost focus บ้าง คราวนีเปลี่ยนอีกรอบ เลยจับเวลาที่ต้องใช้ปรับตัวซักหน่อย (เช่นเดียวกับที่เคยทำตอนเปลี่ยนจาก Emacs มาเป็น vi [1] [2])

ปรากฏว่าใช้เวลาแค่วันเดียวเท่านั้นเองแฮะ น่าแปลกใจมากๆ

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

สรุปว่าอย่าไปใจร้อนสินะ เวลาจะเปลี่ยนอะไรซักอย่าง (แต่ก็อย่าเอื่อยเฉื่อย เฉไฉไม่ยอมเปลี่ยนพอ)