tag:blogger.com,1999:blog-97049022024-03-13T09:01:06.074+07:00pphetraJavaเร็วส์, Javascript, Erlang, Python, Ruby, Clojure, Groovy, เลี้ยงลูก, วาดภาพPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.comBlogger856125tag:blogger.com,1999:blog-9704902.post-58570530441460783462013-12-23T18:10:00.000+07:002013-12-23T18:10:26.184+07:00Fizz Buzz in Haskell without Ifเห็นน้องรูฟจัดกิจกรรม Kata เขียน FizzBuzz แบบไม่ให้ if แล้วคันมือบ้าง ก็เลยขุดเอา Haskell มาลองใช้<br />
<br />
แนวคิดก็คือ เรารู้ว่าชุดของ fizzbuzz มันจะซ้ำเมื่อวนซ้ำเดิมทุกๆ 15 ค่า เราก็เลยสร้าง function ที่ทำหน้าที่แปลงตัวเลข เป็นข้อความ ขึ้นมา 15 ตัว แล้วใช้ function cycle ทำหน้าผลิตซ้ำมันให้กลายเป็น infinite array<br />
<br />
<pre>
fizz n = "fizz"
buzz n = "buzz"
fizzbuzz n = "fizzbuzz"
as_is n = show n
fn_table = cycle [as_is, as_is, fizz, as_is, buzz, fizz, as_is, as_is, fizz, buzz, as_is, fizz, as_is, as_is, fizzbuzz]
</pre>
<br />
ตาราง fizz buzz ก็เกิดจากการเรียกใช้ function ด้วย ตัวเลข index ของ ตำแหน่งของมัน<br />
<code><br />
</code>
<br />
<code><span class="nf" style="background-color: white; color: #990000; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; font-weight: bold; line-height: 14.545454025268555px; white-space: pre;">table</span><span style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;"> </span><span class="ow" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; font-weight: bold; line-height: 14.545454025268555px; white-space: pre;">=</span><span style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;"> </span><span class="n" style="background-color: white; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">zipWith</span><span style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;"> </span><span class="p" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">(</span><span class="o" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; font-weight: bold; line-height: 14.545454025268555px; white-space: pre;">$</span><span class="p" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">)</span><span style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;"> </span><span class="n" style="background-color: white; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">fn_table</span><span style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;"> </span><span class="p" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">[</span><span class="mi" style="background-color: white; color: #009999; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">1</span><span class="o" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; font-weight: bold; line-height: 14.545454025268555px; white-space: pre;">..</span><span class="p" style="background-color: white; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 11.818181991577148px; line-height: 14.545454025268555px; white-space: pre;">]</span></code><br />
<br />
ลองเรียก 100 ลำดับแรกขึ้นมาตรวจสอบ
<br />
<code><br /></code>
<code>
take 100 table </code><br />
<code>["1","2","fizz","4","buzz","fizz","7","8","fizz","buzz","11","fizz","13","14","fizzbuzz","16","17","fizz","19","buzz","fizz","22","23","fizz","buzz","26","fizz","28","29","fizzbuzz","31","32","fizz","34","buzz","fizz","37","38","fizz","buzz","41","fizz","43","44","fizzbuzz","46","47","fizz","49","buzz","fizz","52","53","fizz","buzz","56","fizz","58","59","fizzbuzz","61","62","fizz","64","buzz","fizz","67","68","fizz","buzz","71","fizz","73","74","fizzbuzz","76","77","fizz","79","buzz","fizz","82","83","fizz","buzz","86","fizz","88","89","fizzbuzz","91","92","fizz","94","buzz","fizz","97","98","fizz","buzz"]<br />
</code><br />
<code><br /></code>
น้องรูฟเห็น code แล้ว บอกว่า ทำไมไม่มี test หล่ะพี่ เราก็นึกในใจ code สั้นขนาดนี้ยังต้องมี test อีกหรือ แต่เพื่อให้เข้ากระแสจึงต้องไปค้นคว้าสักหน่อยว่า ถ้าต้องเขียน test ใน haskell เราจะใช้ท่าไหนดี
<br />
ใน haskell เขาไม่นิยมเขียน unit test แบบทั่วๆไป เขาบอกว่ามัน primitive มากที่ต้องเขียน test เพื่อ assert เงื่อนไขแบบ manual ทำไมเราไม่เขียนแค่ specification ของ function เรา แล้วเดี๋ยว haskell จะ random data เข้ามาทำทดสอบให้เราเอง<br />
<br />
อันนี้คือ spec ของ fizzbuzz ของเรา<br />
<br />
<pre>
show_at n = table !! (n - 1)
test' n = fn n == show_at n
where fn n
| (n `rem` 15) == 0 = "fizzbuzz"
| (n `rem` 5) == 0 = "buzz"
| (n `rem` 3) == 0 = "fizz"
| otherwise = show n
</pre>
<br />
show_at ก็คือ ค่าที่ได้จากตาราง fizz buzz ที่เรา generate ขึ้นมาจาก code ข้างบน<br />
ส่วน fn ก็คือ spec ที่เรากำหนดขึ้นมาทดสอบ<br />
<br />
ลองทดสอบ run ดู
<br />
<br />
<code>
*Main> quickCheck test' </code><br />
<code>*** Failed! (after 1 test and 1 shrink):
Exception:
Prelude.(!!): negative index
0</code><br />
<br />
<code></code>จะเห็นว่า Fail ตั้งแต่ค่าแรกสุด เลย นั่นคือค่า fizz buzz กรณี n = 0
<br />
แต่เราไม่อยากปวดหัวกับ fizzbuzz ที่ n <= 0 เราก็เลย กำหนด test ขึ้นมาใหม่ ให้ใช้ index ค่าระหว่าง 1 ถึง 10000 เท่านั้น<br />
<br />
<code>test n = forAll (elements [1..10000]) $ \n -> test' n </code><br />
<br />
ลองทดสอบ run ดู จะเห็นว่า quickCheck จะ random ค่าระหว่าง 1 ถึง 10000 ขึ้นมา 100 ค่า แล้วทำการทดสอบให้เรา<br />
<code><br /></code>
<code>
*Main> quickCheck test </code><br />
<code>+++ OK, passed 100 tests.
</code>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com1tag:blogger.com,1999:blog-9704902.post-17679331510270249852011-05-14T21:03:00.003+07:002011-05-14T21:13:53.252+07:00เอาเข้าไปOpendream ได้ Notice จาก web site ชื่อดัง<br /><br />> ผมคงไม่สามารถร่วมงานกับ OD ที่สนิทกับ Roofimon และ Pphetra<br />> ได้อีกแล้วน่ะครับ สองคนนี้เกรียนเกินไป<br />> <br />> ผมไม่มีปัญหาอะไรกับ OD นะครับ และเคารพในการตัดสินใจของ OD<br />> ที่จะร่วมงานกับสาวกจาวาทั้งสอง (ว่าจะมีประโยชน์กับ OD เอง)<br />> แต่ Blognone คงจะไม่สามารถเอื้อประโยชน์กับ OD<br />> ที่มีสองคนนี้อยู่ร่วมงานด้วยได้อีกต่อไปครับ<br />> ทั้งในแง่การโพสต์รับสมัครงาน และกิจกรรมอื่นๆ ในอนาคต<br />> <br />> นอกจากนี้ งานส่วนตัวในอนาคตของผมก็คงจะไม่ร่วมงานที่มี OD<br />> เป็นพาร์ทเนอร์อีกเช่นกัน ไม่ว่าจะเป็น CC หรืออื่นๆ นะครับ<br /><br />กลุ่ม CC ก็ได้กับเขาด้วย<br /><br />> เรียนทุกท่าน<br /><br />> ผมขอถอนตัวจากทีม CC Thailand ครับ ด้วยเหตุผลว่า CC Thailand มี<br />> OpenDream เป็นพาร์ทเนอร์ และผมมีนโยบายส่วนตัวที่จะไม่ทำงานร่วมกับ OD<br />> ซึ่งผมมีปัญหากับพนักงาน-สมาชิกบางคนของ OD ครับ<br />> (ไม่ได้มีปัญหาอะไรกับคุณเก่งนะครับ แต่เนื่องจากตัวองค์กร OD<br />> มีความสัมพันธ์กับพนักงานเหล่านี้ ซึ่งเคยว่าร้ายผม<br />> ก็คงต้องขอถอนตัวครับ)<br /><br />> สุดท้ายขอบคุณทุกท่านที่ช่วยผลักดัน CC Thai<br />> ให้ประสบความสำเร็จขึ้นมาได้ครับ และอวยพรให้ก้าวหน้าต่อไป<br /><br />คาดการณ์ว่าในอนาคต ถ้าท่านฯยังไม่หายแค้น <br />Opendream คงมีโอกาสโดนข่าวปล่อยเล่นงานแน่ๆ #สื่อเท่านั้นที่ครองโลกPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com10tag:blogger.com,1999:blog-9704902.post-34871397414323606062011-02-02T12:23:00.004+07:002011-02-02T12:41:40.641+07:00git - อยาก commit รวมกับ commit ก่อนหน้านั้นเรื่องมีอยู่ว่า หลังจากที่ผม commit code ไปเป็นชุด (แก้ feature A แต่พบว่า feature B มี bug ด้วย ก็เลยเกิด commit ไล่ๆกัน)<br />แล้วเกิดพบว่า ลืม commit file ไปหนึ่ง file<br />แต่อยากให้ commit นี้ไปรวมอยู่ใน commit ก่อนหน้านั้น<br />กรณีที่เป็น case พึ่ง commit ไปหยกๆ เราก็สามารถใช้ commit --amend เข้ามาช่วยได้<br />แต่ถ้าเป็นกรณีที่มี commit อื่นๆมาคั่นหล่ะ<br /><br /><table style="width:auto;"><tr><td><a href="http://picasaweb.google.com/lh/photo/OcpUGAtxn11NUyfZfEyv_02K_8Qur-vvJwDLf80-tVE?feat=embedwebsite"><img src="http://lh4.ggpht.com/_I3r3qYlfei4/TUjrTDLDpII/AAAAAAAABZs/ykYtns8lBw4/s800/2011-02-02_1216.png" height="88" width="459" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:right">From <a href="http://picasaweb.google.com/pphetra/Pphetra?authkey=Gv1sRgCLX1iv_q2PjXHA&feat=embedwebsite">pphetra</a></td></tr></table><br /><br /><span class="Apple-style-span"><span style="font-weight:bold;">Note</span>: ใช้ได้กับกรณีที่ยังไม่ push ขึ้น repository เท่านั้น</span><br /><br />พระเอกของเรื่องนี้ก็คือ <code>git rebase --interaction</code><br />ขั้นตอนก็คือ<br /><br />เนื่องจากเรามี code ที่ต้องการแก้ไข ค้างอยู่ ให้ทำการ stash เก็บไว้ก่อน<br /><pre><br />$ git stash<br />Saved working directory and index state WIP on (no branch): b1b2bd6 fix bug: บางครั้งก็กด zoom ผลลัพท์จากการ serach ได้ บางครั้งก็ไม่ได้<br />HEAD is now at b1b2bd6 fix bug: บางครั้งก็กด zoom ผลลัพท์จากการ serach ได้ บางครั้งก็ไม่ได้<br /></pre><br /><br />จากนั้นก็สั่ง rebase โดยกำหนด commit id ตัวก่อนหน้าตัวที่เราจะแก้<br /><pre><br />$ git rebase -i d1b9dc72a4a8f57b1dcb43c704494d8fcb639fd2<br /><br />pick 516712b ERP-1807 implement login panel.<br />pick 38d8bda fix bug: duplicate primary key constraint 's name.<br />pick d316ecd fix bug: บางครั้งก็กด zoom ผลลัพท์จากการ serach ได้ บางครั้งก็ไม่ได้<br /><br /># Rebase d1b9dc7..d316ecd onto d1b9dc7<br />#<br /># Commands:<br /># p, pick = use commit<br /># r, reword = use commit, but edit the commit message<br /># e, edit = use commit, but stop for amending<br /># s, squash = use commit, but meld into previous commit<br /># f, fixup = like "squash", but discard this commit's log message<br />#<br /># If you remove a line here THAT COMMIT WILL BE LOST.<br /># However, if you remove everything, the rebase will be aborted.<br />#<br /></pre><br />เลือกเปลี่ยน commit ที่เป็นเป้าหมายในการ amend ให้เป็น 'edit'<br /><br />จากนั้นก็สั่ง <code>git stash pop</code><br />แล้วก็เลือก commit --amend เข้าไป<br /><br />สุดท้ายก็สั่ง <code>git rebase --continue</code>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com3tag:blogger.com,1999:blog-9704902.post-40428833440328429692010-11-22T09:16:00.001+07:002010-11-22T09:23:16.519+07:00javascript: undefined && null<object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/CDRa4pWj0X4?fs=1&hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/CDRa4pWj0X4?fs=1&hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com6tag:blogger.com,1999:blog-9704902.post-25658766209859936402010-11-10T08:46:00.003+07:002010-11-10T09:25:09.514+07:00ทบทวน drama เมื่อ 3 ปีก่อนขออนุญาติน้อง Deans4j นะ ที่ quote คำพูดบางอันมาออก <br />เพราะพี่ว่า "ความจริงมันเปิดเผยแล้ว ทุกคนเข้าใจแล้วว่าอะไรเป็นอะไร"<br /><br />คุณ bow แกเขียน benchmark เทียบ Java, Python, Ruby<br />บังเอิญคุณ bow แก้มี domain ที่ใช้อยู่ในเรื่อง "scientific computation"<br />แล้วมีประสบการณ์ java ไม่พอที่จะ optimize<br />เจ้า deans4j ก็เลยเข้าไปจุดระเบิด<br /><br />http://www.blognone.com/node/4385<br /><br />มีเรื่องมีราว<br />เจ้าน้อง deans ก็ไปตั้งกระทู้ไว้ใน blog ตัวเอง<br /><br />http://deans4j.wordpress.com/2007/04/12/against-idiot/<br /><br />สมัยนั้นผมเป็นแฟน blognone และ markpeak<br />เห็นเจ้า deans4j aggressive ขนาดนั้น ทนไม่ได้ก็เลยไป comment ไว้แบบนี้<br /><br /><blockquote><br />Dean4j, คุณมีความคิดด้านลบอยู่เต็มตัวเลยนะ<br /><br />ผมลองเล่าเรื่องผมให้ฟังแล้วกัน<br />มีอยุ่ช่วงที่ผมรู้สึกว่า “คนเก่งๆหายไปไหนหมด”<br />มีอยู่วันหนึ่งมีคน post ในพันธ์ทิพย์ว่า<br />“ต้องการหาคนเป็น database ช่วยกันทำ project สนุกๆกัน”<br />ผมก็เลย mail เล่าไปว่าเคยทำอะไร อย่างไร<br />ปรากฎว่า mail ที่เขาตอบกลับมา นั้นทำให้ผมช็อค ไปเหมือนกัน<br />เขาบอกว่า เขาเกลียดคนชนิดผมมากเลย คนที่ภูมิใจในตัวเองสูง<br />ประโยคนั้นเป็นประโยคที่ดีมากเลย<br />ทำให้ต้องกลับมาทบทวนตัวเองใหม่<br />ซึ่งจากการทบทวนแล้ว ผมก็พบว่า สิ่งที่ผมเล่าอย่างธรรมดาๆนั้น<br />สำหรับคนอื่นแล้วมันดู arrogance เกินไป<br /><br />ทำให้ผมต้องระมัดระวัง<br />วิธีการเขียนหรือเรื่องต่างๆ ที่อาจทำให้คนอื่นเข้าใจผิดในเจตนา<br />แต่ก็ไม่วายผิดผลาดอีก<br />อย่างเมื่อปลายปีก่อน apple เขาจัดอบรมฟรี<br />ผมก็ไปกับเขาด้วย<br />ก็ไปเจอพี่คนหนึ่ง อัธยาศัยดีมาก<br />กลับมาก็ mail คุยกัน<br />พี่เขาก็ถามว่า ปัจจุบัน ทำอะไรอยู่บ้าง<br />ผมก็เขียนเล่าเรื่องอย่างยาวเลย<br />ผลเป็นอย่างไรรุ้ไหม<br />พี่เขาเลิกติดต่อผมเลย<br />ซึ่งผมเดาได้ไม่ยากเลยว่า<br />มันต้องเกิดจากเนื้อความในจดหมาย<br />ซึ่งมันน่าจะมีส่วนผสมของความ Proud มากไปหน่อย<br />ทำให้คนอ่านรู้สึกว่าเราเป็นคน arrogance<br /><br />บางทีความอัดอั้นตันใจของเรา, ความภูมิใจที่ถุกเก็บกดของเรา<br />ความรู้สึกไม่เต็มของเรา, ความรู้สึกไม่พอใจต่างๆ<br />มันเผลอสะท้อนออกไปทางสิ่งที่เราเขียน<br />โดยเราก็ไม่รู้ตัว<br /><br />สำหรับ post ที่คุณตอบในบทความ “BowDerKliene”<br />ถ้ามองในแง่ fact แล้ว. เป็นการตอบที่ดีมาก<br />แต่ถ้ามองในแง่ของท่าทีแล้ว<br />มันมีน้ำหนักของการดูถูก และการท้าตีท้าต่อยมากไปหน่อย<br />ค่อนข้าง arrogance สูง<br /><br />ส่วนเรื่อง FUD<br />อย่างไง FUD ก็เป็นสิ่งที่หลีกเลี่ยงไม่ได้<br />ตราบใดที่ คน ยังเป็น คน อยุ่<br />การชักจูงคนให้เห็นอีกมุม ไม่สามารถใช้วิธีปะทะได้หรอก<br />มันยิ่งทำให้เขาปฏิเสธมากขึ้นเท่านั้น<br /></blockquote><br /><br />จากนั้น deans กับผม ก็เขียนจดหมายคุยกันหลังไมค์<br />นี่คือจดหมายที่ผมตอบเขา<br /><br /><blockquote>ผมกับคุณมีส่วนหนึ่งที่เหมือนกัน ก็คือ เรามีความโกรธเป็นเจ้าเรือน<br />เรามีความคาดหวังสูงกับสิ่งรอบข้าง<br />ซึ่งมันมักจะทำให้เราผิดหวังกับโมโหอยู่บ่อยๆ<br /><br />อย่าง mk บางทีตอนแรกคุณก็อาจจะรู้สึกว่า<br />เฮ้ย คนนี้ น่าจะเป็นเพื่อนกับเราได้ เขามีเป้าหมายบางอย่างที่ตรงกับเรา <br />แต่สุดท้ายคุณก็เริ่มพบว่า มันมีบางสิ่งที่มันไม่ตรงกันนัก<br />ความคิดเห็น อุดมการณ์ การแสดงออก<br /><br />ส่วนเรื่องที่คุณผิดหวังกับวิธีคิดของเขา<br />ผมขอให้มองเขาเป็นคนธรรมดาเหมือนเรานี่แหล่ะ<br />มีการก้าวที่ผิดพลาด มี double standard (ทุกคนเป็นเหมือนกันหมด)<br />มีความขัดแย้งในสิ่งที่เชื่อกับสิ่งที่ทำ<br />สิ่งที่เขากำลังทำอยู่ ก็คือการเรียนรู้ชีิวิตของเขา <br /><br />พูดง่ายนะแต่ทำยาก<br />อย่างผม สมัยก่อนฟังเพลงคาราบาว ชอบมาก<br />แต่พอรู้ว่าชีวิตนักร้อง กับ เนื้อหาเพลงของเขา มันไม่ไปด้วยกัน<br />ก็เลยเลิกฟัง ทำใจไม่ได้<br />พวกอาการนี้ เวลาเกิดขึ้นแล้วมันแก้ไขให้กลับมาเหมือนเดิมยาก<br />ปัจจุบัน ก็เลยต้องหาวิธีสกัด ไม่ให้เกิดความคาดหวังขึ้นมา<br />จะได้ไม่ต้องผิดผวัง<br /><br />การปะทะเกิดขึ้นได้<br />แต่อย่าให้อารมณ์ร้ายๆ มามีผลต่อชีวิตเรา<br /></blockquote><br /><br />อันนี้คือบางส่วนที่เขา ตอบกลับมา<br /><blockquote><br />ประเด็นคือสิ่งที่ mk ปฏิบัติต่อผมก็น่ารังเกียจจริงๆ แล้วเต็มไปด้วยอคติ ความต้องการเอาชนะ เค้ามีปัญหาอะไรกับผม เค้าไม่เคยคิดจะเคลียร์ ผมเคยพยายามที่จะเคลียร์หลังไมค์ แต่เค้าก็ได้แต่หนีไป แถมมีแต่คำพูดขู่ และดูถูกผมซะยิ่งกว่า<br />...<br /><span style="font-weight:bold;">ผมอยากให้พี่รับรู้ไว้ว่าไม่ใช่ผมที่เริ่มก่อน แต่เป็นเค้าที่เล่นนอกเกมสารพัดจนทำให้ผมเก็บอาการไม่อยู่ เค้าพยายาม discredit ผมหลายทาง เค้าเลือกที่จะประณามผมในที่สาธารณะ space ตัวเอง ทั้งๆ ที่ผมพยายามจะเคลียร์กับเค้าในที่ส่วนตัว จนที่สุดมันลงเอยอย่างนี้ผมจึงค่อยออกมา</span><br /></blockquote><br /><br />เมื่อก่อนผมไม่เข้าใจที่ deans พูดหรอกนะ แต่ปัจจุบัน พฤติกรรมของ markpeak มันยืนยันตามคำพูดน้อง deans ทุกอย่าง<br /><br /><br />ตัดภาพกลับมาปัจจุบัน<br />หลังจากเรามี party "java เกรียน day"<br />ทุกอย่างที่ผ่านมา 3 ปี ก็ชัดเจนแล้วสำหรับผม<br /><br />สุดท้ายผมก็ทำในสิ่งที่ผมแนะนำคนอื่นไม่ได้<br />อารมณ์ร้ายๆ ก็มามีผลต่อชิวิตผม<br /><blockquote><br />ศาสดาเจ็บใจมาก ตามมาเหน็บผมอีกแล้ว, พวกเกรียนใน net นี่ คงรู้สึกว่าพูดอย่างไรก็ได้เพราะใน net มันไกลตีน หรือเราควรจะลดระยะดี<br /></blockquote><br />หวังว่าสุดท้าย ตีนผมคงจะไม่ปะทะปากใครนะPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com10tag:blogger.com,1999:blog-9704902.post-58212243970027528402010-11-09T09:22:00.004+07:002010-11-09T09:39:28.673+07:00funciton form ใน javascriptหลังๆมา style การเขียน javascript ของผมมักจะตกไปอยู่ใน form แบบนี้<br />(โลกของ javascript ไม่มี class, ทุกอย่างเป็น object)<br /><pre class="hl">myService = {<br /> sayHi: <span class="keyword">function</span>(msg) {<br /> this.output(<span class="string">"-"</span> + msg + <span class="string">"-"</span>);<br /> },<br /> <br /> output: <span class="keyword">function</span>(msg) {<br /> alert(msg);<br /> }<br />}</pre><br /><br />หลังๆ javascript code ที่เขียนมันเริ่มใหญ่ (ถึงตอนนี้ก็ราวๆ 70000 บรรทัดแล้ว)<br />เวลา debug ก็เริ่มงงๆว่า ใครมันเป้นคนเรียก function ที่กูเขียนวะ<br /><br />ใน webkit มันมี function ที่ชื่อ console.trace()<br />funciton นี้จะช่วย dump stacktrace ณ จุดที่เราใส่<br />ลองทดลองใส่ console.dump ใน code ข้างบน ก่อนตำแหน่ง alert<br /><br />ผลลัพท์ที่ได้<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_I3r3qYlfei4/TNizrVUQ6NI/AAAAAAAABXM/fAyVetOczWs/s1600/Screen%2Bshot%2B2010-11-09%2Bat%2B9.31.41%2BAM.png"><img style="cursor:pointer; cursor:hand;width: 188px; height: 84px;" src="http://4.bp.blogspot.com/_I3r3qYlfei4/TNizrVUQ6NI/AAAAAAAABXM/fAyVetOczWs/s400/Screen%2Bshot%2B2010-11-09%2Bat%2B9.31.41%2BAM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5537373298889910482" /></a><br />:( ได้ anonymous ออกมาหมดเลย ตกลงก็เลยไม่รู้เลยว่าใครเรียก<br /><br />แต่ทุกปัญหามีทางออก วิธีแก้ก็คือ ปรับรูป form การประกาศ javascript เสียใหม่ เปลี่ยนเป็น<br /><pre class="hl">myService = {<br /> sayHi: <span class="keyword">function</span> <span class="function-name">sayHi</span>(msg) {<br /> this.output(<span class="string">"-"</span> + msg + <span class="string">"-"</span>);<br /> },<br /> <br /> output: <span class="keyword">function</span> <span class="function-name">output</span>(msg) {<br /> console.trace();<br /> alert(msg);<br /> }<br />}</pre><br /><br />ทดลอง run ใหม่<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/TNizrQP9-HI/AAAAAAAABXU/eatBnepHw64/s1600/Screen%2Bshot%2B2010-11-09%2Bat%2B9.33.43%2BAM.png"><img style="cursor:pointer; cursor:hand;width: 175px; height: 65px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/TNizrQP9-HI/AAAAAAAABXU/eatBnepHw64/s400/Screen%2Bshot%2B2010-11-09%2Bat%2B9.33.43%2BAM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5537373297529714802" /></a><br />ทีนี้ก็ต้องเลือกแล้ว จะเอา form เยิ่นเย้อ หรือเอาแบบที่เวลาไล่ stacktrace แล้วมันไล่ง่ายPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-40642927620269437692010-10-27T21:54:00.005+07:002010-10-27T22:25:31.600+07:00ติดใจ repl ของ Clojureช่วงนี้ research หาลู่ทางรับงานจากอเมริกา <br />โจทย์ก็คือไป crawl ดึงข้อมูล จาก medicare.gov<br />ความยากอยู่ตรง site นั้นใช้ .net เขียนแบบ component based (stateful)<br />การจะได้ข้อมูลมา ต้องฝ่าด่านตอบคำถาม จำนวน 4-5 หน้าเข้าไปก่อน<br />ก็เลยต้องใช้ browser จริงๆเข้าไปดึงข้อมูล<br /><br />หวยก็เลยออกมาที่ Selenium + WebDriver<br />แต่ครั้นจะเขียนด้วย java ก็ไม่สนุก<br />เขียนด้วย ruby/python ก็เคยเขียนแล้ว<br />ก็เลยเลือกใช้ Clojure<br /><br />Lisp มันมีข้อดีอย่างหนึ่งก็คือ <br />มันมี <a href="http://en.wikipedia.org/wiki/Read-eval-print_loop">REPL</a> ทำให้เราสามารถเขียนโปรแกรมในลักษณะ incremental ได้<br />เขียนไป ทดลองไป ปรับแต่งไป<br /><br />สภาพแวดล้อมที่เลือกใช้พัฒนาก็คือ<br />emacs + <a href="http://github.com/ninjudd/cake">cake</a><br /><br />ลองดูตัวอย่าง ตอนทดสอบได้ใน video นี้ <br />(ดูช่วงต้นๆก็พอ, net/web มันช้า หลังๆหมดไปกับการรอมันโหลด <br /> ใครว่า web ราชการไทยช้า web ราชการฝรั่งก็ช้าเหมือนกัน)<br /><br /><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzVHrUqdlAhRA0z1YAtLddZk7fRbIfQXEjMPRn6TcVuGA5pyipt43VWzC5yHag1tZaRp0-lZ15tsCs' class='b-hbp-video b-uploaded' frameborder='0'></iframe><br /><br />Updated: video upload ผ่านทาง blogger ได้ความละเอียดต่ำเหลือเกิน<br />ดูรูปประกอบแล้วกัน<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/TMhDW6XNa0I/AAAAAAAABXE/94InM0ul6U8/s1600/Screen+shot+2010-10-27+at+10.19.47+PM.png"><img style="cursor:pointer; cursor:hand;width: 373px; height: 400px;" src="http://3.bp.blogspot.com/_I3r3qYlfei4/TMhDW6XNa0I/AAAAAAAABXE/94InM0ul6U8/s400/Screen+shot+2010-10-27+at+10.19.47+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5532746203127442242" /></a><br /><br /><br />ตัวอย่าง code ที่ใช้<br /><pre class="hl"><br />(<span class="keyword">defn</span> <span class="function-name">send-key</span> [by txt]<br /> (<span class="keyword">let</span> [elm (.findElement *driver* by)]<br /> (.sendKeys elm (<span class="builtin">into-array</span> [txt]))))<br /><br />(<span class="keyword">defn</span> <span class="function-name">click</span> [by]<br /> (<span class="keyword">let</span> [elm (.findElement *driver* by)]<br /> (.click elm)))<br /><br />(<span class="keyword">defn</span> <span class="function-name">init-queue</span> []<br /> (<span class="keyword">def</span> <span class="function-name">*queue*</span> (LinkedBlockingQueue.)))<br /><br />(<span class="keyword">defn</span> <span class="function-name">start-thread</span> [driver func]<br /> (.start<br /> (Thread.<br /> (<span class="builtin">fn</span> []<br /> (<span class="keyword">loop</span> []<br /> (<span class="keyword">let</span> [code (.take *queue*)]<br /> (<span class="keyword">binding</span> [*driver* driver]<br /> (func code)))<br /> (<span class="keyword">recur</span>))))))<br /><br />(<span class="keyword">defn</span> <span class="function-name">fetch</span> [code]<br /> (go <span class="string">"https://www.medicare.gov/find-a-plan/questions/home.aspx"</span>)<br /> (send-key (by-css-selector <span class="string">"div#zip-code-field > input"</span>) code)<br /> (click (by-css-selector <span class="string">"input[type=submit][alternatetext=\"Find Plans\"]"</span>)))</pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-64500744385132272482010-10-26T08:33:00.002+07:002010-10-26T08:58:07.066+07:00เรียนรู้วิธีที่ ExtJS format dateใน ExtJS เราใช้ function format ในการกำหนดรูปแบบการแสดงผลของ Date object<br />ลองดูตัวอย่าง<br /><pre class="hl"><br />new Date().format("c")<br />"2010-10-26T08:35:30+07:00"<br /><br />new Date().format("d/n/Y")<br />"26/10/2010"<br /></pre><br /><br />คำถามที่น่าสนใจก็คือ เขาใช้วิธีไหนในการ implement function นี้<br /><br />ลองเปิดไล่ดู code ใน file src/util/Date.js<br />เริ่มจาก object ที่ชื่อ formatCodes<br /><pre class="hl">formatCodes : {<br /> d: <span class="string">"String.leftPad(this.getDate(), 2, '0')"</span>,<br /> D: <span class="string">"Date.getShortDayName(this.getDay())"</span>, <span class="comment-delimiter">// </span><span class="comment">get localised short day name<br /></span> j: <span class="string">"this.getDate()"</span>,<br /> l: <span class="string">"Date.dayNames[this.getDay()]"</span>,<br /> N: <span class="string">"(this.getDay() ? this.getDay() : 7)"</span>,<br /> S: <span class="string">"this.getSuffix()"</span>,<br /> w: <span class="string">"this.getDay()"</span>,<br /> z: <span class="string">"this.getDayOfYear()"</span>,<br /> W: <span class="string">"String.leftPad(this.getWeekOfYear(), 2, '0')"</span>,<br /> F: <span class="string">"Date.monthNames[this.getMonth()]"</span>,<br /> m: <span class="string">"String.leftPad(this.getMonth() + 1, 2, '0')"</span>,<br /> M: <span class="string">"Date.getShortMonthName(this.getMonth())"</span>, <span class="comment-delimiter">// </span><span class="comment">get localised short month name<br /></span> n: <span class="string">"(this.getMonth() + 1)"</span>,<br /> ....<br /> }</pre><br />คำถามที่ตามมาก็คือ ทำไมถึงเก็บ code ฝั่งขวาเป็น String<br /><br />ลองไล่จาก top down บ้าง, โดยย้อยกลับไปดู function format ว่า code ข้างในเป็นอย่างไร<br /><pre class='hl'>Date.prototype.format = Date.prototype.dateFormat;<br /><br /> dateFormat : <span class="keyword">function</span>(format) {<br /> <span class="keyword">if</span> (Date.formatFunctions[format] == <span class="keyword">null</span>) {<br /> Date.createFormat(format);<br /> }<br /> <span class="keyword">return</span> Date.formatFunctions[format].call(<span class="keyword">this</span>);<br /> },</pre><br /><br />จะเห็นว่า เมื่อได้รับ parameter เป็น format แล้ว, function มันจะพยายาม lookup หา function จากตาราง Date.formatFunctions ก่อน<br />ถ้าไม่เจอ ก็จะไปเรียก Date.createFormat เพื่อสร้าง function ให้ก่อน<br /><br />ลองตามไปดู function createFormat บ้าง<br />code ที่เป็นพระเอกของเราก็คือ บรรทัดสุดท้าย<br /><pre class="hl"> createFormat : <span class="keyword">function</span>(format) {<br /> <span class="keyword">var</span> <span class="variable-name">code</span> = [],<br /> special = <span class="keyword">false</span>,<br /> ch = <span class="string">''</span>;<br /><br /> <span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable-name">i</span> = 0; i < format.length; ++i) {<br /> ch = format.charAt(i);<br /> <span class="keyword">if</span> (!special && ch == <span class="string">"\\"</span>) {<br /> special = <span class="keyword">true</span>;<br /> } <span class="keyword">else</span> if (special) {<br /> special = <span class="keyword">false</span>;<br /> code.push(<span class="string">"'"</span> + String.escape(ch) + <span class="string">"'"</span>);<br /> } <span class="keyword">else</span> {<br /> code.push(Date.getFormatCode(ch))<br /> }<br /> }<br /> <b>Date.formatFunctions[format] = <span class="keyword">new</span> Function(<span class="string">"return "</span> + code.join(<span class="string">'+'</span>));</b><br /> },</pre><br />จะเห็นว่ามัน loop ไปตามแต่ละ character ที่อยู่ใน format string ที่เราส่งให้<br />จากนั้นก็ไปเอาไป lookup ตาราง formatCodes ที่เราเห็นข้างบน ได้ค่ามาก็จัดการ แปะให้กลายเป็น anonymous function <br />แล้วก็เก็บ cache ไว้ใน Date.formatFunctions<br /><br />ลอง dump Date.formatFunctions มาดู<br /><pre class="hl">Date.formatFunctions["d/n/Y"]<br />function anonymous() { return String.leftPad(this.getDate(), 2, '0')+'/'+(this.getMonth() + 1)+'/'+this.getFullYear();<br />}</pre><br /><br />พอเห็น code แบบนี้แล้ว การเพิ่มวันที่ไทยเข้าไปเองก็ไม่ยากแล้วPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-2213584279020464602010-08-08T09:52:00.001+07:002010-08-08T10:10:09.977+07:00export canvas to image<p>เมื่อวานกลุ่ม Creative Hacker ได้มีโอกาสไปนั่งคุยกันเรื่อง HTML5. ประเด็นหนึ่งที่ผมสนใจติดตามต่อ ก็คือเรื่องที่เราสามารถ export canvas ของเราออกมาเป็น Image ได้เลย</p><p>สิ่งที่สงสัยก็คือ, image ที่มัน export ออกมามันอยู่ในรูปแบบอะไร. กลับมาบ้านก็เลยใช้ chrome ช่วยเฉลย</p><p><span style="color: #0080ff; font-family: Menlo, monospace; font-size: 11px; white-space: pre-wrap;">$('#canvas')[0].toDataURL('image/png')</span></p><p><span style="font-family: Menlo, monospace; font-size: 11px; white-space: pre-wrap;">"<span class="console-formatted-string source-code" style="color: #c41a16; font-family: Menlo, monospace; font-size: 11px; white-space: pre-wrap;">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAABkCAYAA...</span>"</span></p><p><span style="font-family: Menlo, monospace; font-size: small;"><span style="font-size: 11px; white-space: pre-wrap;">Update 2010-08-08 10:03</span></span></p><p><span style="font-family: Menlo, monospace; font-size: small;"><span style="font-size: 11px; white-space: pre-wrap;">ใน firefox ถ้าต้องการให้ save ลงเป็น file เลยให้ทำดังนี้</span></span></p><pre class="hl"><span class="keyword">var </span><span class="variable-name">data</span> = $(<span class="string">'#canvas'</span>)[0].toDataURL(<span class="string">'image/png'</span>);<br />data.replace(<span class="string">'image/png'</span>, <span class="string">'image/octet-stream'</span>);<br />document.location.href=data;</pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-31507515278070803382010-07-12T09:18:00.004+07:002010-07-12T09:57:59.834+07:00"new" operator in javascriptช่วงนี้ได้มีโอกาสสอน javascript ก็เลยได้มีโอกาสทบทวนหลักการพื้นฐานของมัน <br />ปกติผมมักจะใช้อย่างเดียว ไม่เคยอ่าน spec หรือศึกษารากฐานของมัน (คิดว่าหลายๆคนก็คงเป็นเช่นนี้เหมือนกัน)<br /><br />วันนี้จะพูดถึงประเด็นหนึ่งที่น่าสนใจก็คือ เรื่อง new operator<br /><br />ลองดู code นี้ ที่แสดงการสร้าง object ใหม่ๆด้วย new operator<br /><pre class="hl"><span class="keyword">function</span> <span class="function-name">Person</span>(name) {<br /> this.name = name;<br />}<br /><br />p1 = <span class="keyword">new</span> Person(<span class="string">'pok'</span>);</pre><br />โปรแกรมเมอร์ที่มาจากสาย OOP ทั้งหลายเห็น code นี้แล้วมักจะรู้สึกทะแม่งๆ ตรงที่ว่า <br />เหตุใด function ธรรมดา ถึงเอาไปสร้าง object ได้<br />(ใน javascript, function ที่ใช้นำไปสร้าง object เราเรียกมันว่า constructor)<br /><br />สาเหตุที่เราสงสัยแบบนี้ ก็เพราะว่า paradigm ของเราผูกอยู่กับ โลก OOP แบบ class กับ instance<br />แต่ใน javascript นั้นโลกของมันไม่มี class, ทุกอย่างในโลกของ javascript คือ Object (มี object อยู่ type เดียวเท่านั้น)<br />สิ่งที่ต่างกันก็คือ constructor ที่ใช้สร้าง Object นั้นๆ <br />เช่น date object ก็เกิดจาก constructor ที่ชื่อ Date<br /><br />เพื่อให้เข้าใจถึง concept constructor เราลองดูขั้นตอนภายในของ new operator บ้างว่า จริงๆแล้วมันทำงานอย่างไร<br />เริ่มด้วยการ inspect เจ้า p1 ที่สร้างจาก code ข้างบน<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZJYgm7I/AAAAAAAABVc/xoFRZ-0LTtg/s1600/p1.png"><img style="cursor:pointer; cursor:hand;width: 400px; height: 278px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZJYgm7I/AAAAAAAABVc/xoFRZ-0LTtg/s400/p1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492847163570428850" /></a><br /><br />ทีนี้เราจะลองสร้าง p2 ที่มี structure เหมือนกับ p1 แต่ไม่ใช้ new operator ในการสร้าง<br />เริ่มด้วย new Object ธรรมดาขึ้นมาก่อน (อย่างที่บอก ใน javascirpt ทุกอย่างคือ Object ธรรมดาๆ)<br /><pre class="hl">p2 = <span class="keyword">new</span> Object()</pre><br />ลอง inspect ดู จะเห็นว่า มันคือ object เปล่าๆ ซึ่งมี __proto__ ชี้ไปยัง root object<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_I3r3qYlfei4/TDqDZ7BSoiI/AAAAAAAABVs/gX1vhXkHWeA/s1600/newp2.png"><img style="cursor:pointer; cursor:hand;width: 400px; height: 170px;" src="http://2.bp.blogspot.com/_I3r3qYlfei4/TDqDZ7BSoiI/AAAAAAAABVs/gX1vhXkHWeA/s400/newp2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492847176894816802" /></a><br /><br />สิ่งที่เราต้องทำก็คือ เปลี่ยน __proto__ reference ให้ชี้ไปยัง prototype ของ Person<br />(function ทุก function ใน javascript จะมี property ที่ชื่อ prototype เกิดขึ้นโดยอัตโนมัติ)<br /><pre class="hl">p2.__proto__ = Person.prototype;</pre><br /><br />สุดท้ายก็ทำการเรียกใช้ function Person โดยเปลี่ยน scope ของ this ให้กลายเป็น p2 object ของเรา<br /><pre class="hl">Person.apply(p2, [<span class="string">'pok'</span>]);</pre><br /><br />ลอง inspect p2 ดู จะเห็นว่าหน้าตามันเหมือน p1 ที่สร้างจาก <code>new Person(...)</code> แล้ว<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZluanvI/AAAAAAAABVk/qGOLqevKSpc/s1600/p2.png"><img style="cursor:pointer; cursor:hand;width: 400px; height: 271px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZluanvI/AAAAAAAABVk/qGOLqevKSpc/s400/p2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492847171178503922" /></a><br /><br />Note: เราอาจเรียก function ที่เราใช้กับ <code>new</code> operator ว่า Template function ก็ได้<br />เพราะหน้าที่มันก็คือการ assign property ให้กับ object เปล่าๆที่ถูกสร้างขึ้นมาโดย new operatorPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com2tag:blogger.com,1999:blog-9704902.post-66850341086890268772010-04-29T12:00:00.002+07:002010-04-29T12:02:01.736+07:00Good Designerมีคนตั้งคำถามใน reddit (ดูแล้วก็เหมาะที่เราจะใช้ถามตัวเองด้วย)<br><br><div><a class="title" href="http://www.reddit.com/r/web_design/comments/bx4t4/how_do_you_become_a_real_designer/" style="text-decoration:none"><font size="5"><font face="Arial"><font color="#0C343D">How do you become a "real" designer ?</font></font></font></a></div><br>เขาเป็นใคร และทำไมถึงถามคำถามนี้<br><br><hr><blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"><font size="3">I'm a developer, not a designer - I know my way around HTML and CSS. I also have a passing knowledge of Gimp and Inkscape. The technical stuff I don't have a problem with.</font><br><br><font size="3">However, when I try to design a site myself the <font color="#990000">result is rather boring and minimalist.</font> It's valid, accessible, SEO-friendly and so on, but it <font color="#990000">lacks originality and doesn't really stand out.</font></font></blockquote><hr><br><div>มีคำตอบมากมายหลายแบบ (แน่นอน คำถามมันปลายเปิดขนาดนั้น)</div><br>บางคนก็ว่า<br><hr><blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"><font face="verdana, arial, helvetica, sans-serif"><font size="3">A lot of design is </font><font color="#990000"><font size="3">obsessing</font></font><font size="3"> over the </font><font color="#990000"><font size="3">tiniest details</font></font><font size="3"> that have a </font><font color="#990000"><font size="3">larger impact</font></font><font size="3"> on the overall "feel" of the design than you might have anticipated.</font></font></blockquote><hr><br><div>อันนี้ก็น่าสนใจ</div><hr><blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"><font face="verdana, arial, helvetica, sans-serif"><font size="3">I think if I could sum up effective web design today in a single word, it would be “</font><font color="#990000"><font size="3">subtlety</font></font><font size="3">”.</font></font><br><font face="verdana, arial, helvetica, sans-serif"><font size="3">Most boring designs have no subtlety, because there isn’t really any detail there.</font></font><br><font face="verdana, arial, helvetica, sans-serif"><font size="3">However, most really bad designs also have no subtlety, </font><font color="#990000"><font size="3">because everything is overdone</font></font><font size="3">.</font></font></blockquote><p style="margin-left:0px;margin-right:0px"></p><hr><font size="2"><font face="verdana, arial, helvetica, sans-serif"><font color="#134F5C">(อะไรคือ subtlety?, ถ้าสงสัยแล้ว อย่าปล่อยให้มันลอยผ่านไปโดยไม่ค้นหา)</font></font></font><p></p><br><br>แล้ว Artistic หล่ะ<br><hr><blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"><font face="verdana, arial, helvetica, sans-serif"><font size="3">Artistic ability </font><b style="font-style:normal"><font size="3">is</font></b><font size="3"> a prerequisite to becoming a </font><b style="font-style:normal"><font size="3">great</font></b><font size="3"> graphic designer. That said, artistic ability is a </font><i><font size="3">skill</font></i><font size="3">.</font></font></blockquote><hr><br>skill คือ ทักษะ, ทักษะเกิดจากอะไร<br><hr><blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"><font face="verdana, arial, helvetica, sans-serif"><font size="3">it's <font color="#990000">practise, practise, practise </font>like painting, because every project is different, every client is different and you can't afford to ever stop learning. In a roundabout way, for example, being a good photographer can be more important in design, than being good at photoshop.</font></font><br><br><font face="verdana, arial, helvetica, sans-serif"><font size="3">It's about your </font><i><font size="3">ideas</font></i><font size="3">, ideas you can only get by having a massive arsenal of previous experience (personal is good, professional is better, you just need to have </font><i><font size="3">finished</font></i><font size="3"> a lot of work). <font color="#990000">If you don't have this, you don't know what is possible and what you are capable of.</font></font></font></blockquote><hr><br>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-26809563226032304122010-04-22T13:43:00.003+07:002010-04-22T13:56:26.551+07:00Evolution of programming languagesอ่านบทความ Functional Logic Programming ใน Communications of the ACM แล้วชอบที่เขาเกริ่นลำดับพัฒนาการของ programming language<br /><br />mnemonic instructions กับ symbolic labels ใน Assembly เกิดขึ้นเพื่อที่จะซ่อน macine code กับ address<br />arrays and expression ใน Fortran เกิดขึ้นเพื่อซ่อน registers<br />structured programming เกิดขึ้นเพื่อซ่อน goto กับ jump labels<br />encapsulate ใน OOP เกิดขึ้นเพื่อซ่อน representation of data<br /><br />declarative language(เช่น functional programming) เกิดขึ้นเพื่อซ่อน ลำดับการทำงาน, assignment และ control statementsPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com1tag:blogger.com,1999:blog-9704902.post-77704175732658536232010-04-21T21:43:00.003+07:002010-04-21T21:52:56.411+07:00update เส้นทางขี่จักรยานเห็นเก่ง post <a href="http://keng.ws/2010/04/my-bicycle-path/">เส้นทางขี่จักรยาน</a>แล้วฉุกใจคิด<br />เดิมคิดว่า ระยะทางจากบ้านไป opendream น่าจะประมาณ 5 กิโล<br />วันนี้ก็เลยลอง plot แผนที่ดู<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_I3r3qYlfei4/S88QSmFIwsI/AAAAAAAABU8/yey2FN41Qq8/s1600/Screen+shot+2010-04-21+at+9.42.44+PM.png"><img style="cursor:pointer; cursor:hand;width: 366px; height: 400px;" src="http://4.bp.blogspot.com/_I3r3qYlfei4/S88QSmFIwsI/AAAAAAAABU8/yey2FN41Qq8/s400/Screen+shot+2010-04-21+at+9.42.44+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5462602784669483714" /></a><br /><br />สรุปว่าจากบ้านไป opendream = 10 KM<br />มิน่าวันนี้ทำไมรู้สึกเพลียๆ (อากาศมันร้อนด้วย)<br /><br />ปล. เส้นสีฟ้าคือ ไปกลับ บ้าน-บริษัท Ingres<br />เส้นสีแดงคือ เส้นกลับถ้าใช้เสือหมอบ (ถ้าใช้เสื้อภูเขา กลับเส้นฟ้า)<br />เส้นสีเขียว คือ ไปกลับ บ้าน-opendream<br />ทั้งฟ้าและแดง ระยะทางเท่าๆกัน = 5 KMPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-67832233160163124332010-03-23T09:50:00.002+07:002010-03-23T09:57:10.350+07:00ชอบแบบไหน<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_I3r3qYlfei4/S6gs5Y6kvgI/AAAAAAAABTg/iAsyJVVfx-U/s1600-h/Screen+shot+2010-03-23+at+9.30.59+AM.png"><img style="cursor:pointer; cursor:hand;width: 400px; height: 203px;" src="http://4.bp.blogspot.com/_I3r3qYlfei4/S6gs5Y6kvgI/AAAAAAAABTg/iAsyJVVfx-U/s400/Screen+shot+2010-03-23+at+9.30.59+AM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5451656713384279554" /></a><br />สองรูปข้างบนแสดงการต่อวงจรแบบเดียวกัน<br />คำถามคือ "แวบแรกที่เห็น คุณชอบแบบไหน?"<br /><br /><br />สมมติฐานของผมก็คือ จากคำตอบนั้นน่าจะคาดเดาได้ด้วยว่า คุณน่าจะชอบ programming language ตัวไหน.PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com11tag:blogger.com,1999:blog-9704902.post-77831179562438474422010-03-16T10:58:00.003+07:002010-03-16T11:26:29.761+07:00วาดรูป-ฝึกสังเกตช่วงนี้ลูกๆปิดเทอม เลยได้ฤกษ์พากันไปเที่ยว แน่นอนว่าหน้าร้อนอย่างนี้ จะมีที่ไหนดีไปกว่าทะเลอีก ระหว่างทางต้องผ่านพิพิธภัณฑ์สัตว์น้ำหว้ากอด้วย แน่นอนว่าไม่แวะไม่ได้<br /><br />ระหว่างเดินดูสัตว์น้ำทั้งหลาย ผมก็พยายามฝึกให้ลูกมองเห็นสิ่งเล็กๆน้อยๆต่างๆ โดยใช้วิธีตั้งคำถาม หรือให้เทียบเคียงกับตัวเอง เช่น พอเจอเจ้า<a href="http://images.google.com/images?rls=en&q=ปลากลับหัว&oe=UTF-8">ปลากลับหัว </a>ก็ชวนกันเดินด้วยมือแทนเท้า<br /><br />ไปเที่ยวคราวนี้ลูกเริ่มโตขึ้น(5 ขวบ) ก็เลยเพิ่มกลยุทธ์การเรียนรู้ด้วยการวาดรูป<br />วิธีการก็คือ พ่อเป็นคนวาด ส่วนลูกทำหน้าที่บอกรายละเอียดว่าถูกต้องไหม นับจำนวนว่าครบไม่ครบ<br /><br />อันนี้คือ เต่าทะเล กับ ปลาดาว ที่วาดกัน ลูกต้องนับขาปลาดาว กับนับจำนวนลวดลายของเต่า<br /><br /><table style="width:auto;"><tr><td><a href="http://picasaweb.google.com/lh/photo/PnYWWWgJZxFHZ6WHuxzsDw?feat=embedwebsite"><img src="http://lh4.ggpht.com/_I3r3qYlfei4/S58HUW8Tj9I/AAAAAAAABSw/QvC0pOr8kTY/s144/2010-03-16%2008.51.18.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:right">From <a href="http://picasaweb.google.com/pphetra/714BannGroodHuahin?feat=embedwebsite">7-14 bann grood,huahin</a></td></tr></table>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-31387978495869510532010-01-28T11:43:00.009+07:002010-02-01T14:24:09.043+07:00code <- พิกัด -> diagramวันก่อนนั่งแกะ code ของ <a href="http://bitbucket.org/justin/webmachine/wiki/Home">Webmachine</a> เห็น code ส่วนนี้น่าสนใจดี<br /><br />อันนี้เป็น <a href="http://bitbucket.org/justin/webmachine/wiki/BigHTTPGraph">diagram แสดง flow decision</a> ของ webmachine <br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/S2Z_LsjMgRI/AAAAAAAABNs/wQ_gZtV4Uog/s1600-h/Screen+shot+2010-02-01+at+2.13.14+PM.png"><img style="cursor:pointer; cursor:hand;width: 400px; height: 253px;" src="http://3.bp.blogspot.com/_I3r3qYlfei4/S2Z_LsjMgRI/AAAAAAAABNs/wQ_gZtV4Uog/s400/Screen+shot+2010-02-01+at+2.13.14+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5433169839383150866" /></a><br /><br />ถ้าดูที่ขอบของ diagram เขาจะแสดง<span style="font-weight:bold;">เลขพิกัด</span>ด้วย<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_I3r3qYlfei4/S2Z_06BM22I/AAAAAAAABN0/AEIcE2MFlMI/s1600-h/Screen+shot+2010-02-01+at+2.16.06+PM.png"><img style="cursor:pointer; cursor:hand;width: 382px; height: 400px;" src="http://4.bp.blogspot.com/_I3r3qYlfei4/S2Z_06BM22I/AAAAAAAABN0/AEIcE2MFlMI/s400/Screen+shot+2010-02-01+at+2.16.06+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5433170547373300578" /></a><br /><br />ดู <a href="http://bitbucket.org/justin/webmachine/src/tip/src/webmachine_decision_core.erl">code ที่เขา implement จริง</a> จะเห็นว่าเขาตั้งชื่อ function อ้างอิงเลขพิกัดและ version ของเอกสารด้วย<br /><pre class="hl"><span class="comment-delimiter">%% </span><span class="comment">"Forbidden?"<br /></span><span class="function-name">decision</span>(v3b7) -><br /> decision_test(resource_call(forbidden), true, 403, v3b6);<br /><span class="comment-delimiter">%% </span><span class="comment">"Okay Content-* Headers?"<br /></span><span class="function-name">decision</span>(v3b6) -><br /> decision_test(resource_call(valid_content_headers), true, v3b5, 501);<br /><span class="comment-delimiter">%% </span><span class="comment">"Known Content-Type?"<br /></span><span class="function-name">decision</span>(v3b5) -><br /> decision_test(resource_call(known_content_type), true, v3b4, 415);<br /><span class="comment-delimiter">%% </span><span class="comment">"Req Entity Too Large?"<br /></span><span class="function-name">decision</span>(v3b4) -><br /> decision_test(resource_call(valid_entity_length), true, v3b3, 413);<br /><span class="comment-delimiter">%% </span><span class="comment">"OPTIONS?"<br /></span><span class="function-name">decision</span>(v3b3) -><br /> <span class="keyword">case</span> method() <span class="keyword">of</span> <br /> <span class="string">'OPTIONS'</span> -><br /> <span class="variable-name">Hdrs</span> = resource_call(options),<br /> respond(200, <span class="variable-name">Hdrs</span>);<br /> <span class="variable-name">_</span> -><br /> d(v3c3)<br /> <span class="keyword">end</span>;</pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-85496322275720123872010-01-28T09:57:00.003+07:002010-01-28T10:17:33.946+07:00Helloworld with Mochiwebใน erlang ถ้าคิดว่าจะใช้อะไรเป็น web server ดี ก็ต้องคิดถึง Yaws ก่อนเพื่อน<br />แต่ในระยะหลังเริ่มได้ยิน mochiweb และเห็นมี project ใหม่ๆที่นิยมใช้มากขึ้นเรื่อยๆ ก็เลยได้ฤกษ์ทำการทดสอบแบบง่ายๆ โดยจะ implement simple helloworld<br /><br />เริ่มด้วยการ checkout source code ของ mochiweb ก่อน<br /><br />svn checkout http://mochiweb.googlecode.com/svn/trunk/ mochiweb<br />cd mochiweb<br /><br />จากนั้นก็สั่ง <code>make</code> เพื่อ compile code (แน่นอนว่าต้องติดตั้ง erlang รอไว้ก่อนแล้ว)<br /><br />เริ่มต้นสร้างโปรเจค โดยสั่ง<br /><br />escript /PATH_TO/mochiweb/scripts/new_mochiweb.erl helloworld<br /><br />จะได้ directory หน้าตาประมาณนี้<br /><br /><pre class="hl">./helloworld/<br />./helloworld/support/<br /> run_tests.escript<br /> include.mk<br /> start.sh<br /> start-dev.sh<br />./helloworld/src/<br /> skel_web.erl<br /> skel_sup.erl<br /> skel_deps.erl<br /> skel_app.erl<br /> skel.hrl<br /> skel.erl<br /> skel.app<br /> Makefile<br />./helloworld/priv/<br />./helloworld/priv/www/<br /> index.html<br /> Makefile<br />./helloworld/include/<br />./helloworld/doc/<br />./helloworld/deps/</pre><br />file ที่เป็นหัวใจหลักก็คือ helloword_web.erl ทำหน้าที่เป็น process ที่ handle incoming request<br />ให้แก้ไข function loop ดังนี้<br /><pre class="hl"><span class="function-name">loop</span>(<span class="variable-name">Req</span>, <span class="variable-name">DocRoot</span>) -><br /> <span class="string">"/"</span> ++ <span class="variable-name">Path</span> = <span class="variable-name">Req</span>:<span class="builtin">get</span>(path),<br /> <span class="keyword">case</span> <span class="variable-name">Req</span>:<span class="builtin">get</span>(method) <span class="keyword">of</span><br /> <span class="variable-name">Method</span> <span class="keyword">when</span> <span class="variable-name">Method</span> =:= <span class="string">'GET'</span>; <span class="variable-name">Method</span> =:= <span class="string">'HEAD'</span> -><br /> <span class="keyword">case</span> <span class="variable-name">Path</span> <span class="keyword">of</span><br /> <span class="string">"helloworld"</span> -><br /> <span class="variable-name">Req</span>:<span class="type">ok</span>({<span class="string">"text/html"</span>, <span class="string">"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"><br /> <html><br /> <head><title >Welcome to mochiweb</title></head><br /> <body><br /> <h1>Helloworld</h1><br /> </body><br /> </html>"</span>});<br /> <span class="variable-name">_</span> -><br /> <span class="variable-name">Req</span>:<span class="type">serve_file</span>(<span class="variable-name">Path</span>, <span class="variable-name">DocRoot</span>)<br /> <span class="keyword">end</span>;<br /> <span class="string">'POST'</span> -><br /> <span class="keyword">case</span> <span class="variable-name">Path</span> <span class="keyword">of</span><br /> <span class="variable-name">_</span> -><br /> <span class="variable-name">Req</span>:<span class="type">not_found</span>()<br /> <span class="keyword">end</span>;<br /> <span class="variable-name">_</span> -><br /> <span class="variable-name">Req</span>:<span class="type">respond</span>({501, [], []})<br /> <span class="keyword">end</span>.</pre><br /><br />สั่ง run server โดยใช้คำสั่ง start-dev.sh<br />แล้วเรียก http://localhost:8000/helloworld <br /><br />ถ้าเห็น crash report แบบนี้ อย่าตกใจ<br /><pre class="hl"><br />=CRASH REPORT==== 28-Jan-2010::09:31:23 ===<br /> crasher:<br /> initial call: mochiweb_socket_server:acceptor_loop/1<br /> pid: <0.57.0><br /> registered_name: []<br /> exception error: bad argument<br /> in function erlang:universaltime_to_localtime/1<br /> called as erlang:universaltime_to_localtime({{1969,12,31},<br /> {23,59,59}})<br /></pre><br />มันเป็น bug ใช้วิธี export TZ=GMT-7 แก้ปัญหาไปพลางๆก่อน<br /><br />ทดลองแก้ไข code แล้วสั่ง refresh browser จะเห็นว่ามันไม่ reload ให้<br />ไม่ต้องเศร้าใจ ให้เปิด command prompt สั่ง make , ใน development mode, mochiweb จะตรวจดูว่า binary code เปลี่ยนหรือไม่ ถ้าเปลี่ยนมันจะ reload ให้เอง<br /><br />ลองเปลี่ยน code ให้ รับ url /helloworld/ANYNAME แล้วแสดง message Hello ANYNAME ดูบ้าง<br /><pre class="hl"> <span class="keyword">case</span> <span class="variable-name">Path</span> <span class="keyword">of</span><br /> <span class="string">"helloworld/"</span> ++ <span class="variable-name">ID</span> -><br /> <span class="variable-name">Msg</span> = <span class="type">io_lib</span>:<span class="type">format</span>(<span class="string">"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"><br /> <html><br /> <head><title >Welcome to mochiweb</title></head><br /> <body><br /> Hello ~s<br /> </body><br /> </html>"</span>, [<span class="variable-name">ID</span>]),<br /> <span class="variable-name">Req</span>:<span class="type">ok</span>({<span class="string">"text/html"</span>, <span class="variable-name">Msg</span>});</pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com3tag:blogger.com,1999:blog-9704902.post-52372132430708780252010-01-26T13:55:00.003+07:002010-01-26T14:21:29.204+07:00Git กับ Remote Branchสรุปไว้กันลืม<br /><br />สมมติว่าเรา clone git มาจาก Remote<br />โดย default เราจะได้ local branch ที่ชื่อ "master" ที่ track กับ remote branch ที่ชื่อ "master" <br /><pre class="hl"><br />$ git branch<br />* master<br /></pre><br />แต่ถ้าเราสั่งให้ show branch ทั้งหมด<br /><pre class="hl"><br />$ git branch -a<br />* master<br /> origin/HEAD<br /> origin/activity-calendar<br /> origin/master<br /> origin/pilot<br /> origin/pilot_20100128<br /> origin/user-admin<br /></pre><br />จะเห็นว่า git ทำการ fetch branch ทั้งหมดมาให้เรา เพียงแต่มัน map ขึ้นมาเป็น local branch ให้เราเฉพาะแค่ master<br /><br />ถ้าเราสั่ง git fetch สิ่งที่เกิดก็คือ git จะทำการดึงข้อมูลการเปลี่ยนแปลงทุกอย่างของ server มาที่เครื่องเรา แต่จะยังไม่ update Working copy ให้เรา<br /><br />ส่วน git pull ก็เทียบได้กับการสั่ง git fetch แล้วตามด้วย git merge โดยจะ merge จะเฉพาะ current HEAD ของเรา (ถ้า HEAD คือ master ก็จะ merge เฉพาะ master)<br /><br />ที่นี้ถ้าเราต้องการ track remote branch บนเครื่องเรา สิ่งที่ต้องทำก็คือ<br /><pre class="hl"><br />$ git checkout -b pilot origin/pilot<br />Branch pilot set up to track remote branch refs/remotes/origin/pilot.<br />Switched to a new branch "pilot"<br /></pre><br /><br />ปัญหาที่เกิดบ่อยๆก็คือ เราสั่ง track remote branch โดยตั้งชื่อ local branch ไม่ตรงกับ remote branch เช่น<br /><pre class="hl"><br />$ git checkout -b myb1 origin/b1<br />Branch myb1 set up to track remote branch refs/remotes/origin/b1.<br />Switched to a new branch "myb1"<br /></pre><br />เวลาเราแก้ไข file ใน branch myb1 เสร็จแล้วอยาก push ไปที่ server <br /><pre class="hl"><br />$ vi MYFILE<br />…<br />$ git add MYFILE<br />$ git commit -m 'Add MYFILE'<br /><br />$ git push<br />Everything up-to-date<br /></pre><br />จะเห็นว่ามันไม่ยอม push commit ใหม่ของเราไปที่ remote repository<br />ที่เป็นเช่นนี้เพราะถ้าเราสั่ง git push โดยไม่มี parameter สิ่งที่ git ทำก็คือ มันจะ push เฉพาะชื่อ branch ที่ตรงกันของทั้งสองฝั่ง<br /><br />การแก้ไขก็คือ เปลี่ยนตัวแปร configuration push.defaut ให้เป็น tracking เพื่อที่จะให้มัน push เฉพาะ branch ที่มี tracking กับ remote branch โดยไม่สนใจเรื่องชื่อตรงกัน<br /><pre class="hl"><br />$ git push<br />Everything up-to-date<br />pphetra@[~/t2]<br />$ git config push.default tracking<br />pphetra@[~/t2]<br />$ git push<br />Counting objects: 5, done.<br />Delta compression using up to 2 threads.<br />Compressing objects: 100% (2/2), done.<br />Writing objects: 100% (3/3), 248 bytes, done.<br />Total 3 (delta 1), reused 0 (delta 0)<br />To git@github.com:pphetra/test.git<br /> 7d96d34..0dd66fa myb1 -> b1<br /></pre><br /><br />ที่นี้ถ้าเราต้องการสร้าง branch ใหม่บน remote หล่ะ<br />เริ่มจากการสร้าง branch บน server<br /><pre class="hl"><br />$ git push origin origin:refs/heads/b4<br />Total 0 (delta 0), reused 0 (delta 0)<br />To git@github.com:pphetra/test.git<br /> * [new branch] origin/HEAD -> b4<br />$ gitx<br /><br />$ git branch -a<br />* b1<br /> b2<br /> master<br /> myb1<br /> remotes/origin/HEAD -> origin/master<br /> remotes/origin/b1<br /> remotes/origin/b2<br /> remotes/origin/b4<br /> remotes/origin/master<br /></pre><br />จากนั้นก็สั่ง checkout ตามปกติได้เลย<br /><pre class="hl"><br />$ git checkout -b b4 origin/b4<br />Branch b4 set up to track remote branch b4 from origin.<br />Switched to a new branch 'b4'<br /></pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com2tag:blogger.com,1999:blog-9704902.post-33497628595978761362010-01-21T09:55:00.006+07:002010-01-21T10:47:42.912+07:00เล่น LegoLego เป็นของเล่นที่ดี แต่ราคาแพง สมัยเด็กๆจำได้ว่าอยากได้มาก ต้องรอจนมัธยมแล้ว แม่ถึงซื้อให้เล่น 1 ชุด โดยมีข้อแม้ว่าซื้อให้ ผมกับน้องสองคน ไม่ใช่ของใครคนใดคนหนึ่ง<br /><br />พอโตขึ้นมามีลูกของตัวเอง ช่วงน้องปัณณ์อายุ 4 ขวบ อาเก่งกับอาหนึ่งซื้อ Lego ให้เป็นของขวัญ, ปรากฎว่าน้องปัณณ์ชอบของเล่นชิ้นนั้นมาก นั่นต่อ Lego ตามจินตนาการไปเรื่อย, หน้าที่พ่อก็เลยต้องคอยหา Input เข้าไปเรื่อยๆ เพื่อให้เกิดความท้าทายและไม่น่าเบื่อ (ตามกฎของ Flow)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/S1fKJHg5m2I/AAAAAAAABMw/C-b1BeDJK3E/s1600-h/flow_graph.jpg"><img style="cursor:pointer; cursor:hand;width: 400px; height: 391px;" src="http://3.bp.blogspot.com/_I3r3qYlfei4/S1fKJHg5m2I/AAAAAAAABMw/C-b1BeDJK3E/s400/flow_graph.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5429030133803096930" /></a><br /><br />ในกรณีของ Lego นี้, input มีอยู่สองสามแบบก็คือ<br />1. ซื้อชุดใหม่ให้เขา ให้เขาได้ตื่นเต้นกับแบบใหม่ๆ อันนี้ทำบ่อยๆไม่ดีแน่ เพราะราคามันสูงมากและถ้าได้มาง่ายไป มันจะไม่มีค่าเท่าไร<br />2. หา MOC (<a href="http://www.brickshelf.com/cgi-bin/customview.cgi?include=MOC">My Own Creation</a>) ที่คนอื่นสร้างไว้มาให้เขาดู เขาจะได้เกิดแรงบันดาลใจ และเรียนรู้ว่ามันมีความเป็นไปได้ในการต่อเยอะแยะไปหมด<br />3. หาเนื้อหาใหม่ๆ ที่ทำให้เขาใช้ Lego เป็นเครื่องมือในการหมกมุ่นกับเนื้อหานั้นๆ เช่นช่วงวันเด็กผมพาไปปัณณ์ไปดูงาน Lego ที่เขาเอา Lego มาต่อเป็นเมืองจำลอง กลับบ้านมาก็เลยมีการทำฉากจำลองต่างๆกันบ้าง หรือแนะนำเกมส์ Spore ให้เขาเล่น ซึ่งพอเขาเล่น Lego เขาก็จะพยายามต่อเป็นสัตว์ประหลาดในเกมส์<br /><br />อันนี้เป็นผลงาน MOC ของลูกชาย ที่ต่อเมื่อคืนนี้<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/S1fNNuydrUI/AAAAAAAABNA/-6oaokDAKMU/s1600-h/Screen+shot+2010-01-21+at+9.54.09+AM.png"><img style="cursor:pointer; cursor:hand;width: 379px; height: 400px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/S1fNNuydrUI/AAAAAAAABNA/-6oaokDAKMU/s400/Screen+shot+2010-01-21+at+9.54.09+AM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5429033511600106818" /></a><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_I3r3qYlfei4/S1fNNClYKvI/AAAAAAAABM4/Z9tuhXgebfU/s1600-h/Screen+shot+2010-01-21+at+9.53.49+AM.png"><img style="cursor:pointer; cursor:hand;width: 371px; height: 400px;" src="http://2.bp.blogspot.com/_I3r3qYlfei4/S1fNNClYKvI/AAAAAAAABM4/Z9tuhXgebfU/s400/Screen+shot+2010-01-21+at+9.53.49+AM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5429033499734059762" /></a><br /><br />ส่วนของพ่อได้ลำนี้ออกมา<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_I3r3qYlfei4/S1fN9QpncvI/AAAAAAAABNI/6d-bNm629D8/s1600-h/Screen+shot+2010-01-21+at+10.45.28+AM.png"><img style="cursor:pointer; cursor:hand;width: 394px; height: 400px;" src="http://2.bp.blogspot.com/_I3r3qYlfei4/S1fN9QpncvI/AAAAAAAABNI/6d-bNm629D8/s400/Screen+shot+2010-01-21+at+10.45.28+AM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5429034328143655666" /></a><br /><br />หลังจากที่ลูกหมกมุ่นกับ Lego ได้เกือบ 1 ปี ตอนนี้ต่อมเก็บกดของพ่อเด็กเริ่มแตกแล้ว อันนี้เป็นสิ่งที่น่ากลัวมาก เพราะเราเป็นคนถือกระเป๋าเงิน, ลูกติด แต่มันไม่มีเงิน มันก็เลยซื้อไม่ได้ แต่ถ้าพ่อติด และพ่อมีเงิน อันนี้สิน่าหวาดเสียวPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com2tag:blogger.com,1999:blog-9704902.post-44494637343349290362010-01-19T10:51:00.002+07:002010-01-19T11:04:57.240+07:00MultiMethod with Visitor Patternต่อจากเมื่อวาน <a href="http://pphetra.blogspot.com/2010/01/multimethod.html">Link</a><br />อาจารย์โป้งได้อธิบายให้ฟังด้วยว่า วิธีหนึ่งที่เขานิยมใช้ implement multiple dispatch ใน Language ที่ support แต่ single dispatch ก็คือ <a href="http://en.wikipedia.org/wiki/Visitor_pattern">Visitor Pattern</a> (อาจารย์โป้งไม่ได้ใช้วิธีนี้)<br /><br />ผมก็เลยเอาตัวอย่างเมื่อวานมาลองเขียนดู<br />เริ่มแรกก็คือ ให้เจ้า concrete animal ของเรามี accept method ที่รับ Visitor interface ก่อน<br /><pre class="hl"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="type">Animal</span> {<br /> <span class="keyword">public</span> <span class="type">void</span> <span class="function-name">accept</span>(<span class="type">IVisitor</span> <span class="variable-name">visitor</span>);<br />}<br /><br /><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Cat</span> <span class="keyword">implements</span> <span class="type">Animal</span> {<br /><br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> accept(<span class="type">IVisitor</span> <span class="variable-name">visitor</span>) {<br /> visitor.visit(<span class="keyword">this</span>);<br /> }<br /> <br />}<br /><br /><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Dog</span> <span class="keyword">implements</span> <span class="type">Animal</span> {<br /><br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> accept(<span class="type">IVisitor</span> <span class="variable-name">visitor</span>) {<br /> visitor.visit(<span class="keyword">this</span>);<br /> }<br /> <br />}</pre><br />จากนั้นก็ สร้าง Visitor Interface ที่มี method visit ครบทุก concrete animal ของเรา<br /><pre class="hl"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="type">IVisitor</span> {<br /> <span class="keyword">public</span> <span class="type">void</span> <span class="function-name">visit</span>(<span class="type">Cat</span> <span class="variable-name">cat</span>);<br /> <span class="keyword">public</span> <span class="type">void</span> <span class="function-name">visit</span>(<span class="type">Dog</span> <span class="variable-name">dog</span>);<br />}</pre><br />ทดลอง feed อาหารโดยใช้ pattern ใหม่นี้<br /><pre class="hl"><span class="type">Cat</span> <span class="variable-name">cat</span> = <span class="keyword">new</span> <span class="type">Cat</span>();<br /><span class="type">Dog</span> <span class="variable-name">dog</span> = <span class="keyword">new</span> <span class="type">Dog</span>();<br /><br /><span class="type">IVisitor</span> <span class="variable-name">bone</span> = <span class="keyword">new</span> <span class="type">IVisitor</span>() {<br /> <br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> visit(<span class="type">Cat</span> <span class="variable-name">cat</span>) {<br /> System.out.println(<span class="string">"I don't like this. Meaw Meaw!!!"</span>);<br /> <br /> }<br /> <br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> visit(<span class="type">Dog</span> <span class="variable-name">dog</span>) {<br /> System.out.println(<span class="string">"I 'm full. Hong Hong!!!"</span>);<br /> <br /> }<br /> <br />};<br /> <br /><span class="type">IVisitor</span> <span class="variable-name">fish</span> = <span class="keyword">new</span> <span class="type">IVisitor</span>() {<br /> <br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> visit(<span class="type">Cat</span> <span class="variable-name">cat</span>) {<br /> System.out.println(<span class="string">"I like this. Meaw Meaw!!!"</span>);<br /> <br /> }<br /> <br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> visit(<span class="type">Dog</span> <span class="variable-name">dog</span>) {<br /> System.out.println(<span class="string">"Yes, I can eat this. Hong Hong!!!"</span>);<br /> <br /> }<br /> <br />};<br /><br />cat.accept(bone);<br />dog.accept(bone);<br /> <br />cat.accept(fish);<br />dog.accept(fish);</pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-3444503835134968742010-01-18T10:01:00.002+07:002010-01-18T10:06:00.540+07:00MultiMethodเมื่อวันเสาร์ไป compkucamp มา เจออาจารย์โป้งเข้าก็เลยถามว่า ช่วงนี้ทำอะไรอยู่, อาจารย์โป้งก็ตอบว่ากำลัง implement <a href="http://en.wikipedia.org/wiki/Multiple_dispatch">multimethod</a> บน c++ อยู่ ว่าแล้วก็ควักโน๊ตบุ๊คออกมาแสดง อาจารย์โป้งใช้ software ได้ผสมปนเปมาก เริ่มจากเปิด microsoft visual c++ ขึ้นมา จากนั้นก็ switch ไปใช้ terminal บน mac เพื่อใช้ django generate project files จากนั้นก็ show file content ให้ดูโดยใช้ textmate <br /><br />หลังจากกลับมาบ้าน และส่งลูกเข้านอนหมดแล้ว เพื่อแก้ข้อสงสัยที่ฟังมา ก็เลยต้องเข้า wikipedia ไปหาอ่านเรื่อง <a href="http://en.wikipedia.org/wiki/Multiple_dispatch">Multimethod หรือ Multiple Dispatch</a> บ้าง<br /><br />ในการทำความเข้าใจกับเรื่องนี้ เราก็ควรจะเริ่มจาก basic สุดก่อนก็คือ <a href="http://en.wikipedia.org/wiki/Single_dispatch">Single Dispatch</a> ซึ่งใช้ใน Java, C++, Smalltalk, Objective-C<br /><br />ลองดู code นี้<br /><pre class="hl"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="type">Animal</span> {<br /> <br /> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="type">void</span> <span class="function-name">feed</span>(<span class="type">Food</span> <span class="variable-name">food</span>);<br /> <br />}<br /><br /><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="type">Food</span> {<br /><br />}<br /><br /><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Fish</span> <span class="keyword">extends</span> <span class="type">Food</span> {<br /><br />}<br /><br /><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Bone</span> <span class="keyword">extends</span> <span class="type">Food</span> {<br /><br />}<br /><br /><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Dog</span> <span class="keyword">extends</span> <span class="type">Animal</span> {<br /><br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> feed(<span class="type">Food</span> <span class="variable-name">food</span>) {<br /> System.out.println(<span class="string">"I 'm full. Hong Hong!!!"</span>);<br /> }<br /><br />}<br /><br /><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Cat</span> <span class="keyword">extends</span> <span class="type">Animal</span> {<br /><br /> @Override<br /> <span class="keyword">public</span> <span class="type">void</span> feed(<span class="type">Food</span> <span class="variable-name">food</span>) {<br /> System.out.println(<span class="string">"I 'm full. Meaw Meaw!!!"</span>);<br /> }<br /><br /> <span class="keyword">public</span> <span class="type">void</span> <span class="function-name">feed</span>(<span class="type">Bone</span> <span class="variable-name">food</span>) {<br /> System.out.println(<span class="string">"I don't like this. Meaw Meaw!!!"</span>);<br /> }<br /><br />}</pre><br /><br /><br />ถ้าลอง run แบบนี้ดู<br /><pre class="hl"><span class="keyword">public</span> <span class="keyword">class</span> <span class="type">Runme</span> {<br /><br /> <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">void</span> <span class="function-name">main</span>(<span class="type">String</span>[] <span class="variable-name">args</span>) {<br /> <span class="type">Animal</span> <span class="variable-name">a</span> = <span class="keyword">new</span> <span class="type">Dog</span>();<br /> <span class="type">Animal</span> <span class="variable-name">b</span> = <span class="keyword">new</span> <span class="type">Cat</span>();<br /> <br /> <span class="type">Bone</span> <span class="variable-name">bone</span> = <span class="keyword">new</span> <span class="type">Bone</span>();<br /> a.feed(bone);<br /> b.feed(bone);<br /><br /> }<br /><br />}</pre><br /><br />กรณีที่เป็น single dispatch ผลลัพท์ที่ได้ก็คือ<br /><pre class="hl">I 'm full. Hong Hong!!!<br />I 'm full. Meaw Meaw!!!</pre><br />จะเห็นว่า single dispatch จะตัดสินใจเลือก method โดยดูแค่ว่าจะเลือกให้ class ไหนรับผิดชอบในการ handle การ call, โดยไม่ได้สนใจ type ของ arguments<br />ส่วน multiple dispatch มันจะเลือก method โดยดู type ของ arguments ด้วย <br />ถ้าทดลองนำ code ข้างบน ไป run ใน groovy ซึ่งเป็น multiple dispatch ผลลัพท์ทีได้ก็คือ<br /><pre class="hl">I 'm full. Hong Hong!!!<br />I don't like this. Meaw Meaw!!!</pre>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com0tag:blogger.com,1999:blog-9704902.post-34023668365153508762010-01-14T10:26:00.004+07:002010-01-14T19:20:48.930+07:00เรียนสีน้ำเมื่อวานที่ Opendream มีการจัดกิจกรรมเรียนวาดภาพสีน้ำครั้งแรก โดยมีน้องแพ็ครับหน้าที่เป็นผู้สอน, course แรกเริ่มด้วยการแจกรูปลายเส้นขนมเค็ก พร้อมกับให้ดูภาพจริงบน computer จากนั้นก็สั่งสั้นๆว่า "ระบาย"<br /><br />ผู้เรียนทั้งหลายก็ก้มหน้าก้มตาระบาย (บางคนก็ประทัวงบ้างว่า ยังไม่สอนเลย จะระบายได้อย่างไร) <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/S06Q5p6A80I/AAAAAAAABMg/DBWmdpHqeyc/s1600-h/56926161.jpg"><img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_I3r3qYlfei4/S06Q5p6A80I/AAAAAAAABMg/DBWmdpHqeyc/s320/56926161.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5426433921204155202" /></a><br /><br />ผมชอบวิธีเรียนแบบนี้นะ ทักษะการระบายสีคือการปฎิบัติ อยากรู้ก็ต้องระบาย จะมามัวเรียนว่าจับพู่กันอย่างไร, หรือเรียนรู้ basic introduction ของสีน้ำ มันก็ไช่ที่<br /><br />ผมเคยหัดเขียนสีน้ำ เมื่อตอนยังหนุ่มๆเหมือนกัน สมัยนั้นพบว่า อุปสรรคสำคัญของคนที่อยากจะเรียนสีน้ำ ก็คือ "ความกลัว" กลัวว่าจะวาดหรือระบายออกมาไม่สวย. ก็เลยไม่กล้าลงมือทำ น่าสนใจตรงที่ว่า ความกลัว นั้นมันเกิดมาได้อย่างไร. เป็นผลจากการศึกษาหรือ culture หรือเป็น Generalize ของมนุษย์<br /><br />เพื่อเป็นกำลังใจให้น้องๆที่ชอบคาดหวังกับตัวเอง ก็เลยไปค้นรูปสีน้ำรูปแรก(18 ปีแล้ว) มาให้ดู <br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_I3r3qYlfei4/S06QLelWskI/AAAAAAAABMQ/JhdyEy44LlI/s1600-h/Screen+shot+2010-01-14+at+10.25.20+AM.png"><img style="cursor:pointer; cursor:hand;width: 241px; height: 320px;" src="http://4.bp.blogspot.com/_I3r3qYlfei4/S06QLelWskI/AAAAAAAABMQ/JhdyEy44LlI/s320/Screen+shot+2010-01-14+at+10.25.20+AM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5426433127890727490" /></a><br />ถ้าคาดหวังไว้สูงเกินจริง หรือมีความคิดลบกับตัวเอง ก็คงเลิกไปแล้ว<br />แต่ถ้าวาดไปเรื่อยๆ ไม่กลัว สักวัน(ของผมนี่หลายปีอยู่) มันก็จะพอดูได้ขึ้นมา (อันนี้ก็ราวๆ 13 ปีแล้วมั้ง)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_I3r3qYlfei4/S06QMZeMzbI/AAAAAAAABMY/tMW6XCsUcp8/s1600-h/Screen+shot+2010-01-14+at+10.25.44+AM.png"><img style="cursor:pointer; cursor:hand;width: 320px; height: 230px;" src="http://2.bp.blogspot.com/_I3r3qYlfei4/S06QMZeMzbI/AAAAAAAABMY/tMW6XCsUcp8/s320/Screen+shot+2010-01-14+at+10.25.44+AM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5426433143698410930" /></a><br /><br />ประเด็นที่สำคัญของการระบายสีน้ำสำหรับผม ก็คือ ความสุขอยู่ที่ระหว่างการระบาย ถ้าไป focus ผิดที่ก็จะไปวางความสุขไว้ที่ผลลัพท์ อยากเห็นผลลัพท์ที่สวยงาม จริงๆแล้ว ผลลัพท์ที่สวยงามเป็นแค่ผลพลอยได้<br /><br />Quote ที่ตรงที่สุดสำหรับเรื่องนี้ คือ<br /><blockquote><br />Experience is what you get when you didn't get what you wanted.<br /></blockquote><br />เวลาลงสี จะเห็นได้ชัดเลยว่า มันออกมาไม่เหมือนที่เราอยากได้PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com3tag:blogger.com,1999:blog-9704902.post-72515487972477679242010-01-07T13:21:00.003+07:002010-01-07T13:28:25.061+07:00เล่นน้ำฝนเมื่อวานฝนตกหนัก โชคดีที่หนีออกจาก Opendream ตั้งแต่บ่ายแก่ๆ ก็เลยไม่ต้องผจญกรรม(รถติด)มากนัก<br /><br />ไปถึงบ้าน ฝนกำลังกระหน่ำ ก็เลยชวนลูกชายเบอร์สองออกไปเล่นน้ำฝนกัน (คนแรกไม่ชวน เพราะว่าพ่อพาขี่จักรยานตากฝนไปโรงเรียนบ่อยแล้ว)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_I3r3qYlfei4/S0V-Ysr6GMI/AAAAAAAABLU/LhFhO2BLFbs/s1600-h/Screen+shot+2010-01-07+at+1.20.15+PM.png"><img style="cursor:pointer; cursor:hand;width: 196px; height: 400px;" src="http://4.bp.blogspot.com/_I3r3qYlfei4/S0V-Ysr6GMI/AAAAAAAABLU/LhFhO2BLFbs/s400/Screen+shot+2010-01-07+at+1.20.15+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5423880289014913218" /></a><br /><br />เล่นได้พักเดียว ฟ้าผ่าเปรี้ยง เจ้าลูกชายวิ่งแนบเข้าบ้านPPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com1tag:blogger.com,1999:blog-9704902.post-75856021866244634522010-01-05T12:56:00.003+07:002010-01-05T13:20:50.233+07:00Dynamically generate code in Erlangปัจจุบัน application framework ทั้งหลาย พยายามจะทำให้ชีวิตเราง่ายขึ้น ด้วยการลด noise ที่เราไม่จำเป็นต้องเห็น, generate code ที่จำเป็นต้องใช้ให้เรา<br /><br /><a href="http://www.chicagoboss.org/">Chicago Boss</a> Framework ก็อยู่ในกระแสนี้เช่นกัน ตัว Relation Mapping layer ของมันก็พยายามจะลดรูปให้เหลือน้อยที่สุด, คำถามสำหรับผมก็คือ ใน Erlang นี่เขาใช้เทคนิคอะไรมาช่วย generate code หรือทำ magic บ้าง<br /> <br />ลองดูตัวอย่างการใช้งานก่อน เริ่มด้วยการ define Domain model<br /><pre class="hl"><span class="preprocessor">-module</span>(blog_post, [<span class="variable-name">Id</span>, <span class="variable-name">Title</span>, <span class="variable-name">Text</span>, <span class="variable-name">AuthorId</span>]).<br /><span class="preprocessor">-compile</span>(export_all).<br /><span class="preprocessor">-belongs_to</span>(author).<br /><br /><span class="preprocessor">-module</span>(author, [<span class="variable-name">Id</span>, <span class="variable-name">Name</span>]).<br /><span class="preprocessor">-compile</span>(export_all).<br /><span class="preprocessor">-has_many</span>(blog_posts).</pre><br />เทคนิคแรกที่เขาใช้ก็คือ <a href="http://ftp.sunet.se/pub/lang/erlang/workshop/2003/paper/p29-carlsson.pdf">Parameterized Modul</a>e ซึ่งช่วยให้ Module มีพฤติกรรมในลักษณะ OOP ได้ <br />ลองดู code ตอนที่เรา new instance domain ของเรา<br /><pre class="hl">FakeAuthor = (<span class="type">author</span>:<span class="type">new</span>(id, <span class="string">"YOUR NAME"</span>)):<span class="type">save</span>(),<br /><span class="variable-name">BlogPost</span> = <span class="type">blog_post</span>:<span class="type">new</span>(id,<br /> <span class="string">"BLOG TITLE"</span>, <br /> <span class="string">"BLOG CONTENT"</span>, <br /> <span class="variable-name">FakeAuthor</span>:<span class="type">id</span>()),<br /><span class="variable-name">SavedBlogPost</span> = <span class="variable-name">BlogPost</span>:<span class="type">save</span>(),</pre><br />จะเห็นว่า module ของเรามี function "save", "getter"(ตรงที่ get id จาก fakeauthor) เพิ่มขึ้นมาให้เองโดยที่เราไม่ต้องเขียน คำถามก็คือ เขาใช้เทคนิคอะไรในการ generate code ส่วนนี้<br /><br />เริ่มแรกสุด code ในส่วน module นี้จะไม่ load ขึ้นมาผ่านกลไกปกติ แต่จะทำผ่านกลไกของตัวเอง โดยเริ่มต้น มันจะทำการ parse erlang file โดยใช้ function <a href="http://ftp.sunet.se/pub/lang/erlang/doc/man/epp.html">epp:parse_file</a> ผลที่ได้เราเรียกว่า Form <br /><br />ทดลองใช้ epp:parse_file กับ <a href="http://iporsut.wordpress.com/2010/01/01/เอิร์ลแลงerlang-concurrent-programming-ตอน-1-processes/#comment-70">ตัวอย่างโปรแกรมที่น้องป้อเขียน</a> <br /><pre class="hl"><span class="preprocessor">-module</span>(p).<br /><span class="preprocessor">-export</span>([<span class="type">start/0</span>, <span class="type">say_something/2</span>]).<br /><span class="function-name">say_something</span>(<span class="variable-name">What</span>, 0) -><br /> done;<br /><span class="function-name">say_something</span>(<span class="variable-name">What</span>, <span class="variable-name">Times</span>) -><br /> <span class="type">io</span>:<span class="type">format</span>(<span class="string">"~p~n"</span>, [<span class="variable-name">What</span>]),<br /> <span class="type">say_something</span>(<span class="variable-name">What</span>, <span class="variable-name">Times</span> - 1).<br /><span class="function-name">start</span>() -><br /> <span class="builtin">spawn</span>(tut14, say_something, [hello, 3]),<br /> <span class="builtin">spawn</span>(tut14, say_something, [goodbye, 3]).</pre><br /><br />จะได้ Form ที่มีหน้าตาประมาณนี้ <br /><pre class="hl">{ok,[{attribute,1,file,{<span class="string">"p.erl"</span>,1}},<br /> {attribute,1,module,p},<br /> {attribute,2,export,[{start,0},{say_something,2}]},<br /> {function,3,say_something,2,<br /> [{clause,3,<br /> [{var,3,<span class="string">'What'</span>},{integer,3,0}],<br /> [],<br /> [{atom,4,done}]},<br /> {clause,5,<br /> [{var,5,<span class="string">'What'</span>},{var,5,<span class="string">'Times'</span>}],<br /> [],<br /> [{call,6,<br /> {remote,6,{atom,6,io},{atom,6,format}},<br /> [{string,6,<span class="string">"~p~n"</span>},{cons,6,{var,...},{...}}]},<br /> {call,7,<br /> {atom,7,say_something},<br /> [{var,7,<span class="string">'What'</span>},{op,7,<span class="string">'-'</span>,...}]}]}]},<br /> {function,8,start,0,<br /> [{clause,8,[],[],<br /> [{call,9,<br /> {atom,9,spawn},<br /> [{atom,9,tut14},<br /> {atom,9,say_something},<br /> {cons,9,{...},...}]},<br /> {call,10,<br /> {atom,10,spawn},<br /> [{atom,10,tut14},<br /> {atom,10,say_something},<br /> {cons,10,...}]}]}]},<br /> {eof,11}]}</pre><br />จะเห็นว่ามีลักษณะเป็น Abstract Syntax Tree + Meta Data<br /><br />พอได้ form มา เจ้า ChicagoBoss ก็จะทำการแทรก,แปลง code ให้เป็นไปตามต้องการ โดยมันจะใช้ function ใน library <a href="http://erlang.mirror.su.se/documentation/doc-5.7.3/lib/syntax_tools-1.6.3/doc/html/erl_syntax.html">erl_syntax</a> ช่วยในการ generate Form<br /><br />ตัวอย่าง code ในส่วนที่สร้าง getter<br /><pre class="hl"><span class="function-name">parameter_getter_forms</span>(<span class="variable-name">Parameters</span>) -><br /> <span class="type">lists</span>:<span class="type">map</span>(<span class="keyword">fun</span>(<span class="variable-name">P</span>) -><span class="function-name"> </span><br /> <span class="type">erl_syntax</span>:<span class="type">add_precomments</span>([<span class="type">erl_syntax</span>:<span class="type">comment</span>(<br /> [<span class="type">lists</span>:<span class="type">concat</span>([<span class="string">"% @spec "</span>, <span class="type">parameter_to_colname</span>(<span class="variable-name">P</span>), <span class="string">"() -> "</span>, <span class="variable-name">P</span>]),<br /> <span class="type">lists</span>:<span class="type">concat</span>([<span class="string">"% @doc Returns the value of `"</span>, <span class="variable-name">P</span>, <span class="string">"'"</span>])])],<br /> <span class="type">erl_syntax</span>:<span class="type">function</span>(<br /> <span class="type">erl_syntax</span>:<span class="type">atom</span>(<span class="type">parameter_to_colname</span>(<span class="variable-name">P</span>)),<br /> [<span class="type">erl_syntax</span>:<span class="type">clause</span>([], none, [<span class="type">erl_syntax</span>:<span class="type">variable</span>(<span class="variable-name">P</span>)])]))<br /> <span class="keyword">end</span>, <span class="variable-name">Parameters</span>).<br /><br /><span class="function-name">parameter_setter_forms</span>(<span class="variable-name">ModuleName</span>, <span class="variable-name">Parameters</span>) -><br /> <span class="type">lists</span>:<span class="type">map</span>(<br /> <span class="keyword">fun</span>(<span class="variable-name">P</span>) -><br /> <span class="type">erl_syntax</span>:<span class="type">add_precomments</span>([<span class="type">erl_syntax</span>:<span class="type">comment</span>(<br /> [<br /> <span class="type">lists</span>:<span class="type">concat</span>([<span class="string">"% @spec "</span>, <span class="type">parameter_to_colname</span>(<span class="variable-name">P</span>), <span class="string">"( "</span>, <span class="variable-name">P</span>, <span class="string">"::"</span>, <br /> <span class="keyword">case</span> <span class="type">lists</span>:<span class="type">suffix</span>(<span class="string">"Time"</span>, <span class="builtin">atom_to_list</span>(<span class="variable-name">P</span>)) <span class="keyword">of</span> <br /> true -><span class="function-name"> </span><span class="string">"tuple()"</span>;<br /> false -><span class="function-name"> </span><span class="string">"string()"</span><br /> <span class="keyword">end</span>, <span class="string">" ) -> "</span>, <span class="type">inflector</span>:<span class="type">camelize</span>(<span class="builtin">atom_to_list</span>(<span class="variable-name">ModuleName</span>))]),<br /> <span class="type">lists</span>:<span class="type">concat</span>([<span class="string">"% @doc Set the value of `"</span>, <span class="variable-name">P</span>, <span class="string">"'."</span>])])],<br /> <span class="type">erl_syntax</span>:<span class="type">function</span>(<br /> <span class="type">erl_syntax</span>:<span class="type">atom</span>(<span class="type">parameter_to_colname</span>(<span class="variable-name">P</span>)),<br /> [<span class="type">erl_syntax</span>:<span class="type">clause</span>([<span class="type">erl_syntax</span>:<span class="type">variable</span>(<span class="string">"NewValue"</span>)], none,<br /> [<br /> <span class="type">erl_syntax</span>:<span class="type">application</span>(<br /> <span class="type">erl_syntax</span>:<span class="type">atom</span>(<span class="variable-name">ModuleName</span>),<br /> <span class="type">erl_syntax</span>:<span class="type">atom</span>(new),<br /> <span class="type">lists</span>:<span class="type">map</span>(<br /> <span class="keyword">fun</span><br /> (<span class="variable-name">Param</span>) <span class="keyword">when</span> <span class="variable-name">Param</span> =:= <span class="variable-name">P</span> -><br /> <span class="type">erl_syntax</span>:<span class="type">variable</span>(<span class="string">"NewValue"</span>);<br /> (<span class="variable-name">Other</span>) -><br /> <span class="type">erl_syntax</span>:<span class="type">variable</span>(<span class="variable-name">Other</span>)<br /> <span class="keyword">end</span>, <span class="variable-name">Parameters</span>))<br /> ])]))<br /> <span class="keyword">end</span>, <span class="variable-name">Parameters</span>).</pre><br /><br />พอ transform code เสร็จ ก็จะทำการ compile เป็น binary ด้วย function <a href="http://ftp.sunet.se/pub//lang/erlang/doc/man/compile.html">compile:forms</a><br /><br />compile เสร็จก็จัดการ load เข้า runtime โดยใช้ function <a href="http://ftp.sunet.se/pub/lang/erlang/doc/man/code.html">code:load_binary</a>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com2tag:blogger.com,1999:blog-9704902.post-61465748990462291532010-01-04T11:19:00.003+07:002010-01-04T11:25:51.649+07:00P07 กับ List Moandเห็นน้องป้อทำโจทย์ <a href="http://iporsut.wordpress.com/2010/01/03/แก้โจทย์-l-99ninety-nine-lisp-problems-p07"/>P07 ของ Ninety-Nine Lisp Problem</a> ก็เลยกลับไปอ่าน<a href="http://pphetra.blogspot.com/2007/01/ninety-nine-lisp-problem-p07.html">วิธีทำของตัวเอง</a> แล้วก็พบว่าถึงแม้เวลาจะผ่านมา 4 ปีแล้ว ตัวเองก็ยังไม่เคยเข้าใจคำตอบของ Conor McBride เลย วันนี้ก็เลยได้ฤกษ์ทำความเข้าใจกับ List Monad ที่ Conor ใช้ตอบคำถามผม<br /><br />เริ่มด้วยคำอธิบาย Monad ที่บอกว่า Monad คือ "an abstract datatype of actions" ซึ่งมีนิยามง่ายๆแค่นี้<br /><pre class="hl"><span class="keyword">class</span> <span class="type">Monad</span> m <span class="keyword">where</span><br /> (<span class="variable-name">>>=</span>) <span class="variable-name">::</span> m a <span class="variable-name">-></span> (a <span class="variable-name">-></span> m b) <span class="variable-name">-></span> m b<br /> return <span class="variable-name">::</span> a <span class="variable-name">-></span> m a</pre><br /><br />มันคือ pattern ฉนั้นอย่าพึ่งไปพยายามจินตนาการว่า มันทำอะไรได้บ้าง<br />หันกลับมาดู context ของ List บ้าง เรารู้ว่า type ของ List คือ <code>[a]</code> จะเห็นว่า type <code>[a]</code> มันสามารถมองเป็น <code>m a</code> ได้ (เพราะ type constructor มันมี free variable 1 ตัวเหมือนกัน) ฉนั้นเราลองมา implement ให้ List เป็น Monad กัน<br /><br />เริ่มจาก implementation ที่ง่ายที่สุดก่อน ก็คือ return, return มี type เป็น <code>a -> m a</code> ฉนั้นกรณีของ List, คำสั่ง <code>return 4</code> ก็ควรได้ค่า <code>[4]</code> ออกมา<br /><pre class="hl"><span class="keyword">instance</span> <span class="type">Monad</span> <span class="type">[]</span> <span class="keyword">where</span><br /> return x <span class="variable-name">=</span> [x]</pre><br />ตัวถัดมาก็คือ function <code>>>=</code> นิยามของ type มันคือ <code>m a -> (a -> m b) -> m b</code> <br />เปลี่ยน <code>m a</code> ให้เป็น <code>[a]</code> จะได้ <code>[a] -> (a -> [b]) -> [b]</code> จะเห็นว่า definition มันไกล้เคียงกับ map function ที่มี type เป็น <code>[a] -> (a -> b) -> [b]</code> ดังนั้นเราสามารถ implement <code>>>=</code> โดยใช้ map และ concat <br />หน้าตาออกมาดังนี้<br /><pre class="hl"><span class="keyword">instance</span> <span class="type">Monad</span> <span class="type">[]</span> <span class="keyword">where</span><br /> return x <span class="variable-name">=</span> [x]<br /> xs <span class="variable-name">>>=</span> f <span class="variable-name">=</span> concat (map f xs)</pre><br />ได้นิยามของ List ในมุมมองของ Monad ออกมาอย่างงงๆ<br />แล้วมันมีประโยชน์อะไรหล่ะ ที่ทำ List type ให้เป็น instance ของ Monad type<br /><br />ลองมาหัดใช้ List Monad ทำโจทย์ <br /><blockquote>ถ้าให้ list ของ [1..10] มาให้หาตัวเลข 2 ตัวที่ผลคูณมีค่า = 16</blockquote><br />ถ้าใช้ List Monad ก็จะเขียนแบบนี้<br /><pre class="hl"><span class="function-name">guard</span> <span class="type">True</span> xs <span class="variable-name">=</span> xs<br /><span class="function-name">guard</span> <span class="type">False</span> xs <span class="variable-name">=</span> <span class="type">[]</span><br /><br /><span class="function-name">solve</span> <span class="variable-name">=</span> <span class="keyword">do</span><br /> x <span class="variable-name"><-</span> [1<span class="variable-name">..</span>10]<br /> y <span class="variable-name"><-</span> [x<span class="variable-name">..</span>10]<br /> guard (x <span class="variable-name">*</span> y <span class="variable-name">==</span> 16) (return (x,y))</pre><br />ซึ่งมี form ที่ไกล้เคียงกับ solution ที่ใช้ List comprehension<br /><pre class="hl">[(x,y) <span class="variable-name">|</span> x <span class="variable-name"><-</span> [1<span class="variable-name">..</span>10], y <span class="variable-name"><-</span> [x<span class="variable-name">..</span>10], x <span class="variable-name">*</span> y <span class="variable-name">==</span> 16]</pre><br />กลับมาที่ solution ที่ McBride เขียนตอบผม<br /><pre class="hl"><span class="function-name">flat1</span> <span class="variable-name">::</span> <span class="type">Store</span> a <span class="variable-name">-></span> a<br /><span class="function-name">flat1</span> (<span class="type">E</span> a) <span class="variable-name">=</span> return a<br /><span class="function-name">flat1</span> (<span class="type">S</span> xs) <span class="variable-name">=</span> xs <span class="variable-name">>>=</span> flat1</pre><br />ถึงตอนนี้พอจะรู้เรื่องกับคำอธิบายของเขาแล้ว<br /><blockquote>Your (flat xs) on a list of stores becomes my (xs >>= flat1), systematically lifting the operation on a single store to lists of them and concatenating the results. The return operation makes a singleton from an element. This way of working with lists by singleton and concatenation is exactly the monadic structure which goes with the list type, so you get it from the library by choosing to work with list types. <b>In Haskell, when you choose a typed representation for data, you are not only choosing a way of containing the data but also a way to structure the computations you can express on that data</b></blockquote>PPhetrahttp://www.blogger.com/profile/17730887203314080442noreply@blogger.com1