<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9704902</id><updated>2011-11-19T21:50:54.643+07:00</updated><category term='ruby'/><category term='javascript'/><category term='ofbiz'/><category term='erlang'/><category term='clojure'/><category term='html5'/><category term='cluster'/><category term='clisp'/><category term='maven'/><category term='gwt'/><category term='art'/><category term='terracotta'/><category term='birt'/><category term='bike'/><category term='่dojo'/><category term='MDA'/><category term='grails'/><category term='จาวาเร็วส์'/><category term='git'/><category term='phychology'/><category term='python'/><category term='csp'/><category term='mochiweb'/><category term='haskell'/><category term='rails'/><category term='kv'/><category term='seaside'/><category term='ศาสดาอาฆาต'/><category term='learning'/><category term='rant'/><category term='kids'/><category term='tapestry'/><category term='linux'/><category term='springframework'/><category term='scheme'/><category term='hibernate'/><category term='emacs'/><category term='scala'/><category term='java'/><category term='webmachine'/><category term='programming'/><category term='smalltalk'/><category term='scm'/><category term='lucene'/><category term='xmlrpc'/><category term='lisp'/><category term='oop'/><category term='chart'/><category term='flex'/><category term='mq'/><category term='dojo'/><category term='่javascript'/><category term='groovy'/><category term='icu4j'/><category term='barcampbangkok'/><category term='ejaberd'/><category term='orangegears'/><category term='foss'/><category term='fun'/><category term='testing'/><category term='parser'/><category term='vcs'/><category term='extjs'/><category term='วาดรูป'/><title type='text'>pphetra</title><subtitle type='html'>Javaเร็วส์, Javascript, Erlang, Python, Ruby, Clojure, Groovy, เลี้ยงลูก, วาดภาพ</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default?start-index=101&amp;max-results=100'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>855</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9704902.post-1767933151027024985</id><published>2011-05-14T21:03:00.003+07:00</published><updated>2011-05-14T21:13:53.252+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ศาสดาอาฆาต'/><title type='text'>เอาเข้าไป</title><content type='html'>Opendream ได้ Notice จาก web site ชื่อดัง&lt;br /&gt;&lt;br /&gt;&gt; ผมคงไม่สามารถร่วมงานกับ OD ที่สนิทกับ Roofimon และ Pphetra&lt;br /&gt;&gt; ได้อีกแล้วน่ะครับ สองคนนี้เกรียนเกินไป&lt;br /&gt;&gt; &lt;br /&gt;&gt; ผมไม่มีปัญหาอะไรกับ OD นะครับ และเคารพในการตัดสินใจของ OD&lt;br /&gt;&gt; ที่จะร่วมงานกับสาวกจาวาทั้งสอง (ว่าจะมีประโยชน์กับ OD เอง)&lt;br /&gt;&gt; แต่ Blognone คงจะไม่สามารถเอื้อประโยชน์กับ OD&lt;br /&gt;&gt; ที่มีสองคนนี้อยู่ร่วมงานด้วยได้อีกต่อไปครับ&lt;br /&gt;&gt; ทั้งในแง่การโพสต์รับสมัครงาน และกิจกรรมอื่นๆ ในอนาคต&lt;br /&gt;&gt; &lt;br /&gt;&gt; นอกจากนี้ งานส่วนตัวในอนาคตของผมก็คงจะไม่ร่วมงานที่มี OD&lt;br /&gt;&gt; เป็นพาร์ทเนอร์อีกเช่นกัน ไม่ว่าจะเป็น CC หรืออื่นๆ นะครับ&lt;br /&gt;&lt;br /&gt;กลุ่ม CC ก็ได้กับเขาด้วย&lt;br /&gt;&lt;br /&gt;&gt; เรียนทุกท่าน&lt;br /&gt;&lt;br /&gt;&gt; ผมขอถอนตัวจากทีม CC Thailand ครับ ด้วยเหตุผลว่า CC Thailand มี&lt;br /&gt;&gt; OpenDream เป็นพาร์ทเนอร์ และผมมีนโยบายส่วนตัวที่จะไม่ทำงานร่วมกับ OD&lt;br /&gt;&gt; ซึ่งผมมีปัญหากับพนักงาน-สมาชิกบางคนของ OD ครับ&lt;br /&gt;&gt; (ไม่ได้มีปัญหาอะไรกับคุณเก่งนะครับ แต่เนื่องจากตัวองค์กร OD&lt;br /&gt;&gt; มีความสัมพันธ์กับพนักงานเหล่านี้ ซึ่งเคยว่าร้ายผม&lt;br /&gt;&gt; ก็คงต้องขอถอนตัวครับ)&lt;br /&gt;&lt;br /&gt;&gt; สุดท้ายขอบคุณทุกท่านที่ช่วยผลักดัน CC Thai&lt;br /&gt;&gt; ให้ประสบความสำเร็จขึ้นมาได้ครับ และอวยพรให้ก้าวหน้าต่อไป&lt;br /&gt;&lt;br /&gt;คาดการณ์ว่าในอนาคต ถ้าท่านฯยังไม่หายแค้น &lt;br /&gt;Opendream คงมีโอกาสโดนข่าวปล่อยเล่นงานแน่ๆ #สื่อเท่านั้นที่ครองโลก&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1767933151027024985?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1767933151027024985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1767933151027024985' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1767933151027024985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1767933151027024985'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2011/05/blog-post.html' title='เอาเข้าไป'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3487139741432360606</id><published>2011-02-02T12:23:00.004+07:00</published><updated>2011-02-02T12:41:40.641+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>git - อยาก commit รวมกับ commit ก่อนหน้านั้น</title><content type='html'>เรื่องมีอยู่ว่า หลังจากที่ผม commit code ไปเป็นชุด (แก้ feature A แต่พบว่า feature B มี bug ด้วย ก็เลยเกิด commit ไล่ๆกัน)&lt;br /&gt;แล้วเกิดพบว่า ลืม commit file ไปหนึ่ง file&lt;br /&gt;แต่อยากให้ commit นี้ไปรวมอยู่ใน commit ก่อนหน้านั้น&lt;br /&gt;กรณีที่เป็น case พึ่ง commit ไปหยกๆ เราก็สามารถใช้ commit --amend เข้ามาช่วยได้&lt;br /&gt;แต่ถ้าเป็นกรณีที่มี commit อื่นๆมาคั่นหล่ะ&lt;br /&gt;&lt;br /&gt;&lt;table style="width:auto;"&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/OcpUGAtxn11NUyfZfEyv_02K_8Qur-vvJwDLf80-tVE?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_I3r3qYlfei4/TUjrTDLDpII/AAAAAAAABZs/ykYtns8lBw4/s800/2011-02-02_1216.png" height="88" width="459" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-family:arial,sans-serif; font-size:11px; text-align:right"&gt;From &lt;a href="http://picasaweb.google.com/pphetra/Pphetra?authkey=Gv1sRgCLX1iv_q2PjXHA&amp;feat=embedwebsite"&gt;pphetra&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span"&gt;&lt;span style="font-weight:bold;"&gt;Note&lt;/span&gt;: ใช้ได้กับกรณีที่ยังไม่ push ขึ้น repository เท่านั้น&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;พระเอกของเรื่องนี้ก็คือ &lt;code&gt;git rebase --interaction&lt;/code&gt;&lt;br /&gt;ขั้นตอนก็คือ&lt;br /&gt;&lt;br /&gt;เนื่องจากเรามี code ที่ต้องการแก้ไข ค้างอยู่ ให้ทำการ stash เก็บไว้ก่อน&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ git stash&lt;br /&gt;Saved working directory and index state WIP on (no branch): b1b2bd6 fix bug: บางครั้งก็กด zoom ผลลัพท์จากการ serach ได้ บางครั้งก็ไม่ได้&lt;br /&gt;HEAD is now at b1b2bd6 fix bug: บางครั้งก็กด zoom ผลลัพท์จากการ serach ได้ บางครั้งก็ไม่ได้&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จากนั้นก็สั่ง rebase โดยกำหนด commit id ตัวก่อนหน้าตัวที่เราจะแก้&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ git rebase -i d1b9dc72a4a8f57b1dcb43c704494d8fcb639fd2&lt;br /&gt;&lt;br /&gt;pick 516712b ERP-1807 implement login panel.&lt;br /&gt;pick 38d8bda fix bug: duplicate primary key constraint 's name.&lt;br /&gt;pick d316ecd fix bug: บางครั้งก็กด zoom ผลลัพท์จากการ serach ได้ บางครั้งก็ไม่ได้&lt;br /&gt;&lt;br /&gt;# Rebase d1b9dc7..d316ecd onto d1b9dc7&lt;br /&gt;#&lt;br /&gt;# Commands:&lt;br /&gt;#  p, pick = use commit&lt;br /&gt;#  r, reword = use commit, but edit the commit message&lt;br /&gt;#  e, edit = use commit, but stop for amending&lt;br /&gt;#  s, squash = use commit, but meld into previous commit&lt;br /&gt;#  f, fixup = like "squash", but discard this commit's log message&lt;br /&gt;#&lt;br /&gt;# If you remove a line here THAT COMMIT WILL BE LOST.&lt;br /&gt;# However, if you remove everything, the rebase will be aborted.&lt;br /&gt;#&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เลือกเปลี่ยน commit ที่เป็นเป้าหมายในการ amend ให้เป็น 'edit'&lt;br /&gt;&lt;br /&gt;จากนั้นก็สั่ง &lt;code&gt;git stash pop&lt;/code&gt;&lt;br /&gt;แล้วก็เลือก commit --amend เข้าไป&lt;br /&gt;&lt;br /&gt;สุดท้ายก็สั่ง &lt;code&gt;git rebase --continue&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3487139741432360606?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3487139741432360606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3487139741432360606' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3487139741432360606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3487139741432360606'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2011/02/git-commit-commit.html' title='git - อยาก commit รวมกับ commit ก่อนหน้านั้น'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_I3r3qYlfei4/TUjrTDLDpII/AAAAAAAABZs/ykYtns8lBw4/s72-c/2011-02-02_1216.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4042883344032842969</id><published>2010-11-22T09:16:00.001+07:00</published><updated>2010-11-22T09:23:16.519+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>javascript: undefined &amp;&amp; null</title><content type='html'>&lt;object width="480" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/CDRa4pWj0X4?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/CDRa4pWj0X4?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4042883344032842969?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4042883344032842969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4042883344032842969' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4042883344032842969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4042883344032842969'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/11/javascript-undefined-null.html' title='javascript: undefined &amp;&amp; null'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2565876620985993640</id><published>2010-11-10T08:46:00.003+07:00</published><updated>2010-11-10T09:25:09.514+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='จาวาเร็วส์'/><title type='text'>ทบทวน drama เมื่อ 3 ปีก่อน</title><content type='html'>ขออนุญาติน้อง Deans4j นะ ที่ quote คำพูดบางอันมาออก &lt;br /&gt;เพราะพี่ว่า "ความจริงมันเปิดเผยแล้ว ทุกคนเข้าใจแล้วว่าอะไรเป็นอะไร"&lt;br /&gt;&lt;br /&gt;คุณ bow แกเขียน benchmark เทียบ Java, Python, Ruby&lt;br /&gt;บังเอิญคุณ bow แก้มี domain ที่ใช้อยู่ในเรื่อง "scientific computation"&lt;br /&gt;แล้วมีประสบการณ์ java ไม่พอที่จะ optimize&lt;br /&gt;เจ้า deans4j ก็เลยเข้าไปจุดระเบิด&lt;br /&gt;&lt;br /&gt;http://www.blognone.com/node/4385&lt;br /&gt;&lt;br /&gt;มีเรื่องมีราว&lt;br /&gt;เจ้าน้อง deans ก็ไปตั้งกระทู้ไว้ใน blog ตัวเอง&lt;br /&gt;&lt;br /&gt;http://deans4j.wordpress.com/2007/04/12/against-idiot/&lt;br /&gt;&lt;br /&gt;สมัยนั้นผมเป็นแฟน blognone และ markpeak&lt;br /&gt;เห็นเจ้า deans4j aggressive ขนาดนั้น ทนไม่ได้ก็เลยไป comment ไว้แบบนี้&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Dean4j, คุณมีความคิดด้านลบอยู่เต็มตัวเลยนะ&lt;br /&gt;&lt;br /&gt;ผมลองเล่าเรื่องผมให้ฟังแล้วกัน&lt;br /&gt;มีอยุ่ช่วงที่ผมรู้สึกว่า “คนเก่งๆหายไปไหนหมด”&lt;br /&gt;มีอยู่วันหนึ่งมีคน post ในพันธ์ทิพย์ว่า&lt;br /&gt;“ต้องการหาคนเป็น database ช่วยกันทำ project สนุกๆกัน”&lt;br /&gt;ผมก็เลย mail เล่าไปว่าเคยทำอะไร อย่างไร&lt;br /&gt;ปรากฎว่า mail ที่เขาตอบกลับมา นั้นทำให้ผมช็อค ไปเหมือนกัน&lt;br /&gt;เขาบอกว่า เขาเกลียดคนชนิดผมมากเลย คนที่ภูมิใจในตัวเองสูง&lt;br /&gt;ประโยคนั้นเป็นประโยคที่ดีมากเลย&lt;br /&gt;ทำให้ต้องกลับมาทบทวนตัวเองใหม่&lt;br /&gt;ซึ่งจากการทบทวนแล้ว ผมก็พบว่า สิ่งที่ผมเล่าอย่างธรรมดาๆนั้น&lt;br /&gt;สำหรับคนอื่นแล้วมันดู arrogance เกินไป&lt;br /&gt;&lt;br /&gt;ทำให้ผมต้องระมัดระวัง&lt;br /&gt;วิธีการเขียนหรือเรื่องต่างๆ ที่อาจทำให้คนอื่นเข้าใจผิดในเจตนา&lt;br /&gt;แต่ก็ไม่วายผิดผลาดอีก&lt;br /&gt;อย่างเมื่อปลายปีก่อน apple เขาจัดอบรมฟรี&lt;br /&gt;ผมก็ไปกับเขาด้วย&lt;br /&gt;ก็ไปเจอพี่คนหนึ่ง อัธยาศัยดีมาก&lt;br /&gt;กลับมาก็ mail คุยกัน&lt;br /&gt;พี่เขาก็ถามว่า ปัจจุบัน ทำอะไรอยู่บ้าง&lt;br /&gt;ผมก็เขียนเล่าเรื่องอย่างยาวเลย&lt;br /&gt;ผลเป็นอย่างไรรุ้ไหม&lt;br /&gt;พี่เขาเลิกติดต่อผมเลย&lt;br /&gt;ซึ่งผมเดาได้ไม่ยากเลยว่า&lt;br /&gt;มันต้องเกิดจากเนื้อความในจดหมาย&lt;br /&gt;ซึ่งมันน่าจะมีส่วนผสมของความ Proud มากไปหน่อย&lt;br /&gt;ทำให้คนอ่านรู้สึกว่าเราเป็นคน arrogance&lt;br /&gt;&lt;br /&gt;บางทีความอัดอั้นตันใจของเรา, ความภูมิใจที่ถุกเก็บกดของเรา&lt;br /&gt;ความรู้สึกไม่เต็มของเรา, ความรู้สึกไม่พอใจต่างๆ&lt;br /&gt;มันเผลอสะท้อนออกไปทางสิ่งที่เราเขียน&lt;br /&gt;โดยเราก็ไม่รู้ตัว&lt;br /&gt;&lt;br /&gt;สำหรับ post ที่คุณตอบในบทความ “BowDerKliene”&lt;br /&gt;ถ้ามองในแง่ fact แล้ว. เป็นการตอบที่ดีมาก&lt;br /&gt;แต่ถ้ามองในแง่ของท่าทีแล้ว&lt;br /&gt;มันมีน้ำหนักของการดูถูก และการท้าตีท้าต่อยมากไปหน่อย&lt;br /&gt;ค่อนข้าง arrogance สูง&lt;br /&gt;&lt;br /&gt;ส่วนเรื่อง FUD&lt;br /&gt;อย่างไง FUD ก็เป็นสิ่งที่หลีกเลี่ยงไม่ได้&lt;br /&gt;ตราบใดที่ คน ยังเป็น คน อยุ่&lt;br /&gt;การชักจูงคนให้เห็นอีกมุม ไม่สามารถใช้วิธีปะทะได้หรอก&lt;br /&gt;มันยิ่งทำให้เขาปฏิเสธมากขึ้นเท่านั้น&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;จากนั้น deans กับผม ก็เขียนจดหมายคุยกันหลังไมค์&lt;br /&gt;นี่คือจดหมายที่ผมตอบเขา&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;ผมกับคุณมีส่วนหนึ่งที่เหมือนกัน ก็คือ เรามีความโกรธเป็นเจ้าเรือน&lt;br /&gt;เรามีความคาดหวังสูงกับสิ่งรอบข้าง&lt;br /&gt;ซึ่งมันมักจะทำให้เราผิดหวังกับโมโหอยู่บ่อยๆ&lt;br /&gt;&lt;br /&gt;อย่าง mk บางทีตอนแรกคุณก็อาจจะรู้สึกว่า&lt;br /&gt;เฮ้ย คนนี้ น่าจะเป็นเพื่อนกับเราได้ เขามีเป้าหมายบางอย่างที่ตรงกับเรา &lt;br /&gt;แต่สุดท้ายคุณก็เริ่มพบว่า มันมีบางสิ่งที่มันไม่ตรงกันนัก&lt;br /&gt;ความคิดเห็น อุดมการณ์ การแสดงออก&lt;br /&gt;&lt;br /&gt;ส่วนเรื่องที่คุณผิดหวังกับวิธีคิดของเขา&lt;br /&gt;ผมขอให้มองเขาเป็นคนธรรมดาเหมือนเรานี่แหล่ะ&lt;br /&gt;มีการก้าวที่ผิดพลาด มี double standard (ทุกคนเป็นเหมือนกันหมด)&lt;br /&gt;มีความขัดแย้งในสิ่งที่เชื่อกับสิ่งที่ทำ&lt;br /&gt;สิ่งที่เขากำลังทำอยู่ ก็คือการเรียนรู้ชีิวิตของเขา &lt;br /&gt;&lt;br /&gt;พูดง่ายนะแต่ทำยาก&lt;br /&gt;อย่างผม สมัยก่อนฟังเพลงคาราบาว ชอบมาก&lt;br /&gt;แต่พอรู้ว่าชีวิตนักร้อง กับ เนื้อหาเพลงของเขา มันไม่ไปด้วยกัน&lt;br /&gt;ก็เลยเลิกฟัง ทำใจไม่ได้&lt;br /&gt;พวกอาการนี้ เวลาเกิดขึ้นแล้วมันแก้ไขให้กลับมาเหมือนเดิมยาก&lt;br /&gt;ปัจจุบัน ก็เลยต้องหาวิธีสกัด ไม่ให้เกิดความคาดหวังขึ้นมา&lt;br /&gt;จะได้ไม่ต้องผิดผวัง&lt;br /&gt;&lt;br /&gt;การปะทะเกิดขึ้นได้&lt;br /&gt;แต่อย่าให้อารมณ์ร้ายๆ มามีผลต่อชีวิตเรา&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;อันนี้คือบางส่วนที่เขา ตอบกลับมา&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;ประเด็นคือสิ่งที่ mk ปฏิบัติต่อผมก็น่ารังเกียจจริงๆ แล้วเต็มไปด้วยอคติ ความต้องการเอาชนะ เค้ามีปัญหาอะไรกับผม เค้าไม่เคยคิดจะเคลียร์ ผมเคยพยายามที่จะเคลียร์หลังไมค์ แต่เค้าก็ได้แต่หนีไป แถมมีแต่คำพูดขู่ และดูถูกผมซะยิ่งกว่า&lt;br /&gt;...&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ผมอยากให้พี่รับรู้ไว้ว่าไม่ใช่ผมที่เริ่มก่อน แต่เป็นเค้าที่เล่นนอกเกมสารพัดจนทำให้ผมเก็บอาการไม่อยู่ เค้าพยายาม discredit ผมหลายทาง เค้าเลือกที่จะประณามผมในที่สาธารณะ space ตัวเอง ทั้งๆ ที่ผมพยายามจะเคลียร์กับเค้าในที่ส่วนตัว จนที่สุดมันลงเอยอย่างนี้ผมจึงค่อยออกมา&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;เมื่อก่อนผมไม่เข้าใจที่ deans พูดหรอกนะ แต่ปัจจุบัน พฤติกรรมของ markpeak มันยืนยันตามคำพูดน้อง deans ทุกอย่าง&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ตัดภาพกลับมาปัจจุบัน&lt;br /&gt;หลังจากเรามี party "java เกรียน day"&lt;br /&gt;ทุกอย่างที่ผ่านมา 3 ปี ก็ชัดเจนแล้วสำหรับผม&lt;br /&gt;&lt;br /&gt;สุดท้ายผมก็ทำในสิ่งที่ผมแนะนำคนอื่นไม่ได้&lt;br /&gt;อารมณ์ร้ายๆ ก็มามีผลต่อชิวิตผม&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;ศาสดาเจ็บใจมาก ตามมาเหน็บผมอีกแล้ว, พวกเกรียนใน net นี่ คงรู้สึกว่าพูดอย่างไรก็ได้เพราะใน net มันไกลตีน หรือเราควรจะลดระยะดี&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;หวังว่าสุดท้าย ตีนผมคงจะไม่ปะทะปากใครนะ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2565876620985993640?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2565876620985993640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2565876620985993640' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2565876620985993640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2565876620985993640'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/11/drama-3.html' title='ทบทวน drama เมื่อ 3 ปีก่อน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5821224397002752840</id><published>2010-11-09T09:22:00.004+07:00</published><updated>2010-11-09T09:39:28.673+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>funciton form ใน javascript</title><content type='html'>หลังๆมา style การเขียน javascript ของผมมักจะตกไปอยู่ใน form แบบนี้&lt;br /&gt;(โลกของ javascript ไม่มี class, ทุกอย่างเป็น object)&lt;br /&gt;&lt;pre class="hl"&gt;myService = {&lt;br /&gt; sayHi: &lt;span class="keyword"&gt;function&lt;/span&gt;(msg) {&lt;br /&gt;    this.output(&lt;span class="string"&gt;"-"&lt;/span&gt; + msg + &lt;span class="string"&gt;"-"&lt;/span&gt;);&lt;br /&gt;  },&lt;br /&gt; &lt;br /&gt; output: &lt;span class="keyword"&gt;function&lt;/span&gt;(msg) {&lt;br /&gt;    alert(msg);&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;หลังๆ javascript code ที่เขียนมันเริ่มใหญ่ (ถึงตอนนี้ก็ราวๆ 70000 บรรทัดแล้ว)&lt;br /&gt;เวลา debug ก็เริ่มงงๆว่า ใครมันเป้นคนเรียก function ที่กูเขียนวะ&lt;br /&gt;&lt;br /&gt;ใน webkit มันมี function ที่ชื่อ console.trace()&lt;br /&gt;funciton นี้จะช่วย dump stacktrace ณ จุดที่เราใส่&lt;br /&gt;ลองทดลองใส่ console.dump ใน code ข้างบน ก่อนตำแหน่ง alert&lt;br /&gt;&lt;br /&gt;ผลลัพท์ที่ได้&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;:( ได้ anonymous ออกมาหมดเลย ตกลงก็เลยไม่รู้เลยว่าใครเรียก&lt;br /&gt;&lt;br /&gt;แต่ทุกปัญหามีทางออก วิธีแก้ก็คือ ปรับรูป form การประกาศ javascript เสียใหม่ เปลี่ยนเป็น&lt;br /&gt;&lt;pre class="hl"&gt;myService = {&lt;br /&gt; sayHi: &lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="function-name"&gt;sayHi&lt;/span&gt;(msg) {&lt;br /&gt;    this.output(&lt;span class="string"&gt;"-"&lt;/span&gt; + msg + &lt;span class="string"&gt;"-"&lt;/span&gt;);&lt;br /&gt;  },&lt;br /&gt; &lt;br /&gt; output: &lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="function-name"&gt;output&lt;/span&gt;(msg) {&lt;br /&gt;    console.trace();&lt;br /&gt;    alert(msg);&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทดลอง run ใหม่&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;ทีนี้ก็ต้องเลือกแล้ว จะเอา form เยิ่นเย้อ หรือเอาแบบที่เวลาไล่ stacktrace แล้วมันไล่ง่าย&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5821224397002752840?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5821224397002752840/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5821224397002752840' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5821224397002752840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5821224397002752840'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/11/funciton-form-javascript.html' title='funciton form ใน javascript'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_I3r3qYlfei4/TNizrVUQ6NI/AAAAAAAABXM/fAyVetOczWs/s72-c/Screen%2Bshot%2B2010-11-09%2Bat%2B9.31.41%2BAM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4064292762026943769</id><published>2010-10-27T21:54:00.005+07:00</published><updated>2010-10-27T22:25:31.600+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>ติดใจ repl ของ Clojure</title><content type='html'>ช่วงนี้ research หาลู่ทางรับงานจากอเมริกา &lt;br /&gt;โจทย์ก็คือไป crawl ดึงข้อมูล จาก medicare.gov&lt;br /&gt;ความยากอยู่ตรง site นั้นใช้ .net เขียนแบบ component based (stateful)&lt;br /&gt;การจะได้ข้อมูลมา ต้องฝ่าด่านตอบคำถาม จำนวน 4-5 หน้าเข้าไปก่อน&lt;br /&gt;ก็เลยต้องใช้ browser จริงๆเข้าไปดึงข้อมูล&lt;br /&gt;&lt;br /&gt;หวยก็เลยออกมาที่ Selenium + WebDriver&lt;br /&gt;แต่ครั้นจะเขียนด้วย java ก็ไม่สนุก&lt;br /&gt;เขียนด้วย ruby/python ก็เคยเขียนแล้ว&lt;br /&gt;ก็เลยเลือกใช้ Clojure&lt;br /&gt;&lt;br /&gt;Lisp มันมีข้อดีอย่างหนึ่งก็คือ &lt;br /&gt;มันมี &lt;a href="http://en.wikipedia.org/wiki/Read-eval-print_loop"&gt;REPL&lt;/a&gt; ทำให้เราสามารถเขียนโปรแกรมในลักษณะ incremental ได้&lt;br /&gt;เขียนไป ทดลองไป ปรับแต่งไป&lt;br /&gt;&lt;br /&gt;สภาพแวดล้อมที่เลือกใช้พัฒนาก็คือ&lt;br /&gt;emacs + &lt;a href="http://github.com/ninjudd/cake"&gt;cake&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ลองดูตัวอย่าง ตอนทดสอบได้ใน video นี้ &lt;br /&gt;(ดูช่วงต้นๆก็พอ, net/web มันช้า หลังๆหมดไปกับการรอมันโหลด &lt;br /&gt; ใครว่า web ราชการไทยช้า web ราชการฝรั่งก็ช้าเหมือนกัน)&lt;br /&gt;&lt;br /&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-c980ad1fe265d973" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v23.nonxt2.googlevideo.com/videoplayback?id%3Dc980ad1fe265d973%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1329856754%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D3BDDF0BC609A5532CA29EA09CEAB73E3AFE7A008.4D5F9B57B179A3162ADCDB44F2D6A28AC644F9A0%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Dc980ad1fe265d973%26offsetms%3D5000%26itag%3Dw160%26sigh%3DWBfEHJHY5yNGhOaJC523Rfl0XEQ&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v23.nonxt2.googlevideo.com/videoplayback?id%3Dc980ad1fe265d973%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1329856754%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D3BDDF0BC609A5532CA29EA09CEAB73E3AFE7A008.4D5F9B57B179A3162ADCDB44F2D6A28AC644F9A0%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Dc980ad1fe265d973%26offsetms%3D5000%26itag%3Dw160%26sigh%3DWBfEHJHY5yNGhOaJC523Rfl0XEQ&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Updated: video upload ผ่านทาง blogger ได้ความละเอียดต่ำเหลือเกิน&lt;br /&gt;ดูรูปประกอบแล้วกัน&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ตัวอย่าง code ที่ใช้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;send-key&lt;/span&gt; [by txt]&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; [elm (.findElement *driver* by)]&lt;br /&gt;    (.sendKeys elm (&lt;span class="builtin"&gt;into-array&lt;/span&gt; [txt]))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;click&lt;/span&gt; [by]&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; [elm (.findElement *driver* by)]&lt;br /&gt;    (.click elm)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;init-queue&lt;/span&gt; []&lt;br /&gt;  (&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;*queue*&lt;/span&gt; (LinkedBlockingQueue.)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;start-thread&lt;/span&gt; [driver func]&lt;br /&gt;  (.start&lt;br /&gt;   (Thread.&lt;br /&gt;    (&lt;span class="builtin"&gt;fn&lt;/span&gt; []&lt;br /&gt;      (&lt;span class="keyword"&gt;loop&lt;/span&gt; []&lt;br /&gt;        (&lt;span class="keyword"&gt;let&lt;/span&gt; [code (.take *queue*)]&lt;br /&gt;          (&lt;span class="keyword"&gt;binding&lt;/span&gt; [*driver* driver]&lt;br /&gt;            (func code)))&lt;br /&gt;        (&lt;span class="keyword"&gt;recur&lt;/span&gt;))))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;fetch&lt;/span&gt; [code]&lt;br /&gt;  (go &lt;span class="string"&gt;"https://www.medicare.gov/find-a-plan/questions/home.aspx"&lt;/span&gt;)&lt;br /&gt;  (send-key (by-css-selector &lt;span class="string"&gt;"div#zip-code-field &amp;gt; input"&lt;/span&gt;) code)&lt;br /&gt;  (click (by-css-selector &lt;span class="string"&gt;"input[type=submit][alternatetext=\"Find Plans\"]"&lt;/span&gt;)))&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4064292762026943769?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4064292762026943769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4064292762026943769' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4064292762026943769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4064292762026943769'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/10/repl-clojure.html' title='ติดใจ repl ของ Clojure'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_I3r3qYlfei4/TMhDW6XNa0I/AAAAAAAABXE/94InM0ul6U8/s72-c/Screen+shot+2010-10-27+at+10.19.47+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6450074438513227248</id><published>2010-10-26T08:33:00.002+07:00</published><updated>2010-10-26T08:58:07.066+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='extjs'/><title type='text'>เรียนรู้วิธีที่ ExtJS format date</title><content type='html'>ใน ExtJS เราใช้ function format ในการกำหนดรูปแบบการแสดงผลของ Date object&lt;br /&gt;ลองดูตัวอย่าง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;new Date().format("c")&lt;br /&gt;"2010-10-26T08:35:30+07:00"&lt;br /&gt;&lt;br /&gt;new Date().format("d/n/Y")&lt;br /&gt;"26/10/2010"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คำถามที่น่าสนใจก็คือ เขาใช้วิธีไหนในการ implement function นี้&lt;br /&gt;&lt;br /&gt;ลองเปิดไล่ดู code ใน file src/util/Date.js&lt;br /&gt;เริ่มจาก object ที่ชื่อ formatCodes&lt;br /&gt;&lt;pre class="hl"&gt;formatCodes : {&lt;br /&gt;        d: &lt;span class="string"&gt;"String.leftPad(this.getDate(), 2, '0')"&lt;/span&gt;,&lt;br /&gt;        D: &lt;span class="string"&gt;"Date.getShortDayName(this.getDay())"&lt;/span&gt;, &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;get localised short day name&lt;br /&gt;&lt;/span&gt;        j: &lt;span class="string"&gt;"this.getDate()"&lt;/span&gt;,&lt;br /&gt;        l: &lt;span class="string"&gt;"Date.dayNames[this.getDay()]"&lt;/span&gt;,&lt;br /&gt;        N: &lt;span class="string"&gt;"(this.getDay() ? this.getDay() : 7)"&lt;/span&gt;,&lt;br /&gt;        S: &lt;span class="string"&gt;"this.getSuffix()"&lt;/span&gt;,&lt;br /&gt;        w: &lt;span class="string"&gt;"this.getDay()"&lt;/span&gt;,&lt;br /&gt;        z: &lt;span class="string"&gt;"this.getDayOfYear()"&lt;/span&gt;,&lt;br /&gt;        W: &lt;span class="string"&gt;"String.leftPad(this.getWeekOfYear(), 2, '0')"&lt;/span&gt;,&lt;br /&gt;        F: &lt;span class="string"&gt;"Date.monthNames[this.getMonth()]"&lt;/span&gt;,&lt;br /&gt;        m: &lt;span class="string"&gt;"String.leftPad(this.getMonth() + 1, 2, '0')"&lt;/span&gt;,&lt;br /&gt;        M: &lt;span class="string"&gt;"Date.getShortMonthName(this.getMonth())"&lt;/span&gt;, &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;get localised short month name&lt;br /&gt;&lt;/span&gt;        n: &lt;span class="string"&gt;"(this.getMonth() + 1)"&lt;/span&gt;,&lt;br /&gt;        ....&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;คำถามที่ตามมาก็คือ ทำไมถึงเก็บ code ฝั่งขวาเป็น String&lt;br /&gt;&lt;br /&gt;ลองไล่จาก top down บ้าง, โดยย้อยกลับไปดู function format ว่า code ข้างในเป็นอย่างไร&lt;br /&gt;&lt;pre class='hl'&gt;Date.prototype.format = Date.prototype.dateFormat;&lt;br /&gt;&lt;br /&gt;    dateFormat : &lt;span class="keyword"&gt;function&lt;/span&gt;(format) {&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (Date.formatFunctions[format] == &lt;span class="keyword"&gt;null&lt;/span&gt;) {&lt;br /&gt;            Date.createFormat(format);&lt;br /&gt;        }&lt;br /&gt;        &lt;span class="keyword"&gt;return&lt;/span&gt; Date.formatFunctions[format].call(&lt;span class="keyword"&gt;this&lt;/span&gt;);&lt;br /&gt;    },&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จะเห็นว่า เมื่อได้รับ parameter เป็น format แล้ว, function มันจะพยายาม lookup หา function จากตาราง Date.formatFunctions ก่อน&lt;br /&gt;ถ้าไม่เจอ ก็จะไปเรียก Date.createFormat เพื่อสร้าง function ให้ก่อน&lt;br /&gt;&lt;br /&gt;ลองตามไปดู function createFormat บ้าง&lt;br /&gt;code ที่เป็นพระเอกของเราก็คือ บรรทัดสุดท้าย&lt;br /&gt;&lt;pre class="hl"&gt;    createFormat : &lt;span class="keyword"&gt;function&lt;/span&gt;(format) {&lt;br /&gt;        &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;code&lt;/span&gt; = [],&lt;br /&gt;            special = &lt;span class="keyword"&gt;false&lt;/span&gt;,&lt;br /&gt;            ch = &lt;span class="string"&gt;''&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;i&lt;/span&gt; = 0; i &amp;lt; format.length; ++i) {&lt;br /&gt;            ch = format.charAt(i);&lt;br /&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (!special &amp;amp;&amp;amp; ch == &lt;span class="string"&gt;"\\"&lt;/span&gt;) {&lt;br /&gt;                special = &lt;span class="keyword"&gt;true&lt;/span&gt;;&lt;br /&gt;            } &lt;span class="keyword"&gt;else&lt;/span&gt; if (special) {&lt;br /&gt;                special = &lt;span class="keyword"&gt;false&lt;/span&gt;;&lt;br /&gt;                code.push(&lt;span class="string"&gt;"'"&lt;/span&gt; + String.escape(ch) + &lt;span class="string"&gt;"'"&lt;/span&gt;);&lt;br /&gt;            } &lt;span class="keyword"&gt;else&lt;/span&gt; {&lt;br /&gt;                code.push(Date.getFormatCode(ch))&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        &lt;b&gt;Date.formatFunctions[format] = &lt;span class="keyword"&gt;new&lt;/span&gt; Function(&lt;span class="string"&gt;"return "&lt;/span&gt; + code.join(&lt;span class="string"&gt;'+'&lt;/span&gt;));&lt;/b&gt;&lt;br /&gt;    },&lt;/pre&gt;&lt;br /&gt;จะเห็นว่ามัน loop ไปตามแต่ละ character ที่อยู่ใน format string ที่เราส่งให้&lt;br /&gt;จากนั้นก็ไปเอาไป lookup ตาราง formatCodes ที่เราเห็นข้างบน ได้ค่ามาก็จัดการ แปะให้กลายเป็น anonymous function &lt;br /&gt;แล้วก็เก็บ cache ไว้ใน Date.formatFunctions&lt;br /&gt;&lt;br /&gt;ลอง dump Date.formatFunctions มาดู&lt;br /&gt;&lt;pre class="hl"&gt;Date.formatFunctions["d/n/Y"]&lt;br /&gt;function anonymous() { return String.leftPad(this.getDate(), 2, '0')+'/'+(this.getMonth() + 1)+'/'+this.getFullYear();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;พอเห็น code แบบนี้แล้ว การเพิ่มวันที่ไทยเข้าไปเองก็ไม่ยากแล้ว&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6450074438513227248?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6450074438513227248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6450074438513227248' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6450074438513227248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6450074438513227248'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/10/extjs-format-date.html' title='เรียนรู้วิธีที่ ExtJS format date'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-221358427902046460</id><published>2010-08-08T09:52:00.001+07:00</published><updated>2010-08-08T10:10:09.977+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='html5'/><title type='text'>export canvas to image</title><content type='html'>&lt;p&gt;เมื่อวานกลุ่ม Creative Hacker ได้มีโอกาสไปนั่งคุยกันเรื่อง HTML5. ประเด็นหนึ่งที่ผมสนใจติดตามต่อ ก็คือเรื่องที่เราสามารถ export canvas ของเราออกมาเป็น Image ได้เลย&lt;/p&gt;&lt;p&gt;สิ่งที่สงสัยก็คือ, image ที่มัน export ออกมามันอยู่ในรูปแบบอะไร. กลับมาบ้านก็เลยใช้ chrome ช่วยเฉลย&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0080ff; font-family: Menlo, monospace; font-size: 11px; white-space: pre-wrap;"&gt;$('#canvas')[0].toDataURL('image/png')&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Menlo, monospace; font-size: 11px; white-space: pre-wrap;"&gt;"&lt;span class="console-formatted-string source-code" style="color: #c41a16; font-family: Menlo, monospace; font-size: 11px; white-space: pre-wrap;"&gt;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAABkCAYAA...&lt;/span&gt;"&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Menlo, monospace; font-size: small;"&gt;&lt;span style="font-size: 11px; white-space: pre-wrap;"&gt;Update 2010-08-08 10:03&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Menlo, monospace; font-size: small;"&gt;&lt;span style="font-size: 11px; white-space: pre-wrap;"&gt;ใน firefox ถ้าต้องการให้ save ลงเป็น file เลยให้ทำดังนี้&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;var &lt;/span&gt;&lt;span class="variable-name"&gt;data&lt;/span&gt; = $(&lt;span class="string"&gt;'#canvas'&lt;/span&gt;)[0].toDataURL(&lt;span class="string"&gt;'image/png'&lt;/span&gt;);&lt;br /&gt;data.replace(&lt;span class="string"&gt;'image/png'&lt;/span&gt;, &lt;span class="string"&gt;'image/octet-stream'&lt;/span&gt;);&lt;br /&gt;document.location.href=data;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-221358427902046460?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/221358427902046460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=221358427902046460' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/221358427902046460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/221358427902046460'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/08/export-canvas-to-image.html' title='export canvas to image'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3150751527807080338</id><published>2010-07-12T09:18:00.004+07:00</published><updated>2010-07-12T09:57:59.834+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='่javascript'/><title type='text'>"new" operator in javascript</title><content type='html'>ช่วงนี้ได้มีโอกาสสอน javascript ก็เลยได้มีโอกาสทบทวนหลักการพื้นฐานของมัน &lt;br /&gt;ปกติผมมักจะใช้อย่างเดียว ไม่เคยอ่าน spec หรือศึกษารากฐานของมัน (คิดว่าหลายๆคนก็คงเป็นเช่นนี้เหมือนกัน)&lt;br /&gt;&lt;br /&gt;วันนี้จะพูดถึงประเด็นหนึ่งที่น่าสนใจก็คือ เรื่อง new operator&lt;br /&gt;&lt;br /&gt;ลองดู code นี้ ที่แสดงการสร้าง object ใหม่ๆด้วย new operator&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="function-name"&gt;Person&lt;/span&gt;(name) {&lt;br /&gt;  this.name = name;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;p1 = &lt;span class="keyword"&gt;new&lt;/span&gt; Person(&lt;span class="string"&gt;'pok'&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;โปรแกรมเมอร์ที่มาจากสาย OOP ทั้งหลายเห็น code นี้แล้วมักจะรู้สึกทะแม่งๆ ตรงที่ว่า &lt;br /&gt;เหตุใด function ธรรมดา ถึงเอาไปสร้าง object ได้&lt;br /&gt;(ใน javascript, function ที่ใช้นำไปสร้าง object เราเรียกมันว่า constructor)&lt;br /&gt;&lt;br /&gt;สาเหตุที่เราสงสัยแบบนี้ ก็เพราะว่า paradigm ของเราผูกอยู่กับ โลก OOP แบบ class กับ instance&lt;br /&gt;แต่ใน javascript นั้นโลกของมันไม่มี class, ทุกอย่างในโลกของ javascript คือ Object (มี object อยู่ type เดียวเท่านั้น)&lt;br /&gt;สิ่งที่ต่างกันก็คือ constructor ที่ใช้สร้าง Object นั้นๆ &lt;br /&gt;เช่น date object ก็เกิดจาก constructor ที่ชื่อ Date&lt;br /&gt;&lt;br /&gt;เพื่อให้เข้าใจถึง concept constructor เราลองดูขั้นตอนภายในของ new operator บ้างว่า จริงๆแล้วมันทำงานอย่างไร&lt;br /&gt;เริ่มด้วยการ inspect เจ้า p1 ที่สร้างจาก code ข้างบน&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZJYgm7I/AAAAAAAABVc/xoFRZ-0LTtg/s1600/p1.png"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้เราจะลองสร้าง p2 ที่มี structure เหมือนกับ p1 แต่ไม่ใช้ new operator ในการสร้าง&lt;br /&gt;เริ่มด้วย new Object ธรรมดาขึ้นมาก่อน (อย่างที่บอก ใน javascirpt ทุกอย่างคือ Object ธรรมดาๆ)&lt;br /&gt;&lt;pre class="hl"&gt;p2 = &lt;span class="keyword"&gt;new&lt;/span&gt; Object()&lt;/pre&gt;&lt;br /&gt;ลอง inspect ดู จะเห็นว่า มันคือ  object เปล่าๆ ซึ่งมี __proto__ ชี้ไปยัง root object&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_I3r3qYlfei4/TDqDZ7BSoiI/AAAAAAAABVs/gX1vhXkHWeA/s1600/newp2.png"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;สิ่งที่เราต้องทำก็คือ เปลี่ยน __proto__ reference ให้ชี้ไปยัง prototype ของ Person&lt;br /&gt;(function ทุก function ใน javascript จะมี property ที่ชื่อ prototype เกิดขึ้นโดยอัตโนมัติ)&lt;br /&gt;&lt;pre class="hl"&gt;p2.__proto__ = Person.prototype;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สุดท้ายก็ทำการเรียกใช้ function Person โดยเปลี่ยน scope ของ this ให้กลายเป็น p2 object ของเรา&lt;br /&gt;&lt;pre class="hl"&gt;Person.apply(p2, [&lt;span class="string"&gt;'pok'&lt;/span&gt;]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลอง inspect p2 ดู จะเห็นว่าหน้าตามันเหมือน p1 ที่สร้างจาก &lt;code&gt;new Person(...)&lt;/code&gt; แล้ว&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZluanvI/AAAAAAAABVk/qGOLqevKSpc/s1600/p2.png"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note: เราอาจเรียก function ที่เราใช้กับ &lt;code&gt;new&lt;/code&gt; operator ว่า Template function ก็ได้&lt;br /&gt;เพราะหน้าที่มันก็คือการ assign property ให้กับ object เปล่าๆที่ถูกสร้างขึ้นมาโดย new operator&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3150751527807080338?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3150751527807080338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3150751527807080338' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3150751527807080338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3150751527807080338'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/07/new-operator-in-javascript.html' title='&quot;new&quot; operator in javascript'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_I3r3qYlfei4/TDqDZJYgm7I/AAAAAAAABVc/xoFRZ-0LTtg/s72-c/p1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6685034108689026877</id><published>2010-04-29T12:00:00.002+07:00</published><updated>2010-04-29T12:02:01.736+07:00</updated><title type='text'>Good Designer</title><content type='html'>&amp;#3617;&amp;#3637;&amp;#3588;&amp;#3609;&amp;#3605;&amp;#3633;&amp;#3657;&amp;#3591;&amp;#3588;&amp;#3635;&amp;#3606;&amp;#3634;&amp;#3617;&amp;#3651;&amp;#3609; reddit (&amp;#3604;&amp;#3641;&amp;#3649;&amp;#3621;&amp;#3657;&amp;#3623;&amp;#3585;&amp;#3655;&amp;#3648;&amp;#3627;&amp;#3617;&amp;#3634;&amp;#3632;&amp;#3607;&amp;#3637;&amp;#3656;&amp;#3648;&amp;#3619;&amp;#3634;&amp;#3592;&amp;#3632;&amp;#3651;&amp;#3594;&amp;#3657;&amp;#3606;&amp;#3634;&amp;#3617;&amp;#3605;&amp;#3633;&amp;#3623;&amp;#3648;&amp;#3629;&amp;#3591;&amp;#3604;&amp;#3657;&amp;#3623;&amp;#3618;)&lt;br&gt;&lt;br&gt;&lt;div&gt;&lt;a class="title" href="http://www.reddit.com/r/web_design/comments/bx4t4/how_do_you_become_a_real_designer/" style="text-decoration:none"&gt;&lt;font size="5"&gt;&lt;font face="Arial"&gt;&lt;font color="#0C343D"&gt;How do you become a &amp;quot;real&amp;quot; designer ?&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/a&gt;&lt;/div&gt;&lt;br&gt;&amp;#3648;&amp;#3586;&amp;#3634;&amp;#3648;&amp;#3611;&amp;#3655;&amp;#3609;&amp;#3651;&amp;#3588;&amp;#3619; &amp;#3649;&amp;#3621;&amp;#3632;&amp;#3607;&amp;#3635;&amp;#3652;&amp;#3617;&amp;#3606;&amp;#3638;&amp;#3591;&amp;#3606;&amp;#3634;&amp;#3617;&amp;#3588;&amp;#3635;&amp;#3606;&amp;#3634;&amp;#3617;&amp;#3609;&amp;#3637;&amp;#3657;&lt;br&gt;&lt;br&gt;&lt;hr&gt;&lt;blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"&gt;&lt;font size="3"&gt;I&amp;#39;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&amp;#39;t have a problem with.&lt;/font&gt;&lt;br&gt;&lt;br&gt;&lt;font size="3"&gt;However, when I try to design a site myself the &lt;font color="#990000"&gt;result is rather boring and minimalist.&lt;/font&gt; It&amp;#39;s valid, accessible, SEO-friendly and so on, but it &lt;font color="#990000"&gt;lacks originality and doesn&amp;#39;t really stand out.&lt;/font&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;br&gt;&lt;div&gt;&amp;#3617;&amp;#3637;&amp;#3588;&amp;#3635;&amp;#3605;&amp;#3629;&amp;#3610;&amp;#3617;&amp;#3634;&amp;#3585;&amp;#3617;&amp;#3634;&amp;#3618;&amp;#3627;&amp;#3621;&amp;#3634;&amp;#3618;&amp;#3649;&amp;#3610;&amp;#3610; (&amp;#3649;&amp;#3609;&amp;#3656;&amp;#3609;&amp;#3629;&amp;#3609; &amp;#3588;&amp;#3635;&amp;#3606;&amp;#3634;&amp;#3617;&amp;#3617;&amp;#3633;&amp;#3609;&amp;#3611;&amp;#3621;&amp;#3634;&amp;#3618;&amp;#3648;&amp;#3611;&amp;#3636;&amp;#3604;&amp;#3586;&amp;#3609;&amp;#3634;&amp;#3604;&amp;#3609;&amp;#3633;&amp;#3657;&amp;#3609;)&lt;/div&gt;&lt;br&gt;&amp;#3610;&amp;#3634;&amp;#3591;&amp;#3588;&amp;#3609;&amp;#3585;&amp;#3655;&amp;#3623;&amp;#3656;&amp;#3634;&lt;br&gt;&lt;hr&gt;&lt;blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;A lot of design is &lt;/font&gt;&lt;font color="#990000"&gt;&lt;font size="3"&gt;obsessing&lt;/font&gt;&lt;/font&gt;&lt;font size="3"&gt; over the &lt;/font&gt;&lt;font color="#990000"&gt;&lt;font size="3"&gt;tiniest details&lt;/font&gt;&lt;/font&gt;&lt;font size="3"&gt; that have a &lt;/font&gt;&lt;font color="#990000"&gt;&lt;font size="3"&gt;larger impact&lt;/font&gt;&lt;/font&gt;&lt;font size="3"&gt; on the overall &amp;quot;feel&amp;quot; of the design than you might have anticipated.&lt;/font&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;br&gt;&lt;div&gt;&amp;#3629;&amp;#3633;&amp;#3609;&amp;#3609;&amp;#3637;&amp;#3657;&amp;#3585;&amp;#3655;&amp;#3609;&amp;#3656;&amp;#3634;&amp;#3626;&amp;#3609;&amp;#3651;&amp;#3592;&lt;/div&gt;&lt;hr&gt;&lt;blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;I think if I could sum up effective web design today in a single word, it would be &amp;ldquo;&lt;/font&gt;&lt;font color="#990000"&gt;&lt;font size="3"&gt;subtlety&lt;/font&gt;&lt;/font&gt;&lt;font size="3"&gt;&amp;rdquo;.&lt;/font&gt;&lt;/font&gt;&lt;br&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;Most boring designs have no subtlety, because there isn&amp;rsquo;t really any detail there.&lt;/font&gt;&lt;/font&gt;&lt;br&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;However, most really bad designs also have no subtlety, &lt;/font&gt;&lt;font color="#990000"&gt;&lt;font size="3"&gt;because everything is overdone&lt;/font&gt;&lt;/font&gt;&lt;font size="3"&gt;.&lt;/font&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;p style="margin-left:0px;margin-right:0px"&gt;&lt;/p&gt;&lt;hr&gt;&lt;font size="2"&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font color="#134F5C"&gt;(&amp;#3629;&amp;#3632;&amp;#3652;&amp;#3619;&amp;#3588;&amp;#3639;&amp;#3629; subtlety?, &amp;#3606;&amp;#3657;&amp;#3634;&amp;#3626;&amp;#3591;&amp;#3626;&amp;#3633;&amp;#3618;&amp;#3649;&amp;#3621;&amp;#3657;&amp;#3623; &amp;#3629;&amp;#3618;&amp;#3656;&amp;#3634;&amp;#3611;&amp;#3621;&amp;#3656;&amp;#3629;&amp;#3618;&amp;#3651;&amp;#3627;&amp;#3657;&amp;#3617;&amp;#3633;&amp;#3609;&amp;#3621;&amp;#3629;&amp;#3618;&amp;#3612;&amp;#3656;&amp;#3634;&amp;#3609;&amp;#3652;&amp;#3611;&amp;#3650;&amp;#3604;&amp;#3618;&amp;#3652;&amp;#3617;&amp;#3656;&amp;#3588;&amp;#3657;&amp;#3609;&amp;#3627;&amp;#3634;)&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;p&gt;&lt;/p&gt;&lt;br&gt;&lt;br&gt;&amp;#3649;&amp;#3621;&amp;#3657;&amp;#3623; Artistic &amp;#3627;&amp;#3621;&amp;#3656;&amp;#3632;&lt;br&gt;&lt;hr&gt;&lt;blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;Artistic ability&amp;nbsp;&lt;/font&gt;&lt;b style="font-style:normal"&gt;&lt;font size="3"&gt;is&lt;/font&gt;&lt;/b&gt;&lt;font size="3"&gt;&amp;nbsp;a prerequisite to becoming a&amp;nbsp;&lt;/font&gt;&lt;b style="font-style:normal"&gt;&lt;font size="3"&gt;great&lt;/font&gt;&lt;/b&gt;&lt;font size="3"&gt;&amp;nbsp;graphic designer. That said, artistic ability is a&amp;nbsp;&lt;/font&gt;&lt;i&gt;&lt;font size="3"&gt;skill&lt;/font&gt;&lt;/i&gt;&lt;font size="3"&gt;.&lt;/font&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;br&gt;skill &amp;#3588;&amp;#3639;&amp;#3629; &amp;#3607;&amp;#3633;&amp;#3585;&amp;#3625;&amp;#3632;, &amp;#3607;&amp;#3633;&amp;#3585;&amp;#3625;&amp;#3632;&amp;#3648;&amp;#3585;&amp;#3636;&amp;#3604;&amp;#3592;&amp;#3634;&amp;#3585;&amp;#3629;&amp;#3632;&amp;#3652;&amp;#3619;&lt;br&gt;&lt;hr&gt;&lt;blockquote class="webkit-indent-blockquote" style="border:none;margin:0 0 0 40px"&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;it&amp;#39;s &lt;font color="#990000"&gt;practise, practise, practise &lt;/font&gt;like painting, because every project is different, every client is different and you can&amp;#39;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.&lt;/font&gt;&lt;/font&gt;&lt;br&gt;&lt;br&gt;&lt;font face="verdana, arial, helvetica, sans-serif"&gt;&lt;font size="3"&gt;It&amp;#39;s about your&amp;nbsp;&lt;/font&gt;&lt;i&gt;&lt;font size="3"&gt;ideas&lt;/font&gt;&lt;/i&gt;&lt;font size="3"&gt;, ideas you can only get by having a massive arsenal of previous experience (personal is good, professional is better, you just need to have&amp;nbsp;&lt;/font&gt;&lt;i&gt;&lt;font size="3"&gt;finished&lt;/font&gt;&lt;/i&gt;&lt;font size="3"&gt;&amp;nbsp;a lot of work). &lt;font color="#990000"&gt;If you don&amp;#39;t have this, you don&amp;#39;t know what is possible and what you are capable of.&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;hr&gt;&lt;br&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6685034108689026877?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6685034108689026877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6685034108689026877' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6685034108689026877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6685034108689026877'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/04/reddit.html' title='Good Designer'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2680956322603230412</id><published>2010-04-22T13:43:00.003+07:00</published><updated>2010-04-22T13:56:26.551+07:00</updated><title type='text'>Evolution of programming languages</title><content type='html'>อ่านบทความ Functional Logic Programming ใน Communications of the ACM แล้วชอบที่เขาเกริ่นลำดับพัฒนาการของ programming language&lt;br /&gt;&lt;br /&gt;mnemonic instructions กับ symbolic labels ใน Assembly  เกิดขึ้นเพื่อที่จะซ่อน macine code กับ address&lt;br /&gt;arrays and expression ใน Fortran เกิดขึ้นเพื่อซ่อน registers&lt;br /&gt;structured programming เกิดขึ้นเพื่อซ่อน goto กับ jump labels&lt;br /&gt;encapsulate ใน OOP เกิดขึ้นเพื่อซ่อน representation of data&lt;br /&gt;&lt;br /&gt;declarative language(เช่น functional programming) เกิดขึ้นเพื่อซ่อน ลำดับการทำงาน, assignment และ control statements&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2680956322603230412?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2680956322603230412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2680956322603230412' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2680956322603230412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2680956322603230412'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/04/evolution-of-programming-languages.html' title='Evolution of programming languages'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7770417573265853623</id><published>2010-04-21T21:43:00.003+07:00</published><updated>2010-04-21T21:52:56.411+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bike'/><title type='text'>update เส้นทางขี่จักรยาน</title><content type='html'>เห็นเก่ง post &lt;a href="http://keng.ws/2010/04/my-bicycle-path/"&gt;เส้นทางขี่จักรยาน&lt;/a&gt;แล้วฉุกใจคิด&lt;br /&gt;เดิมคิดว่า ระยะทางจากบ้านไป opendream น่าจะประมาณ 5 กิโล&lt;br /&gt;วันนี้ก็เลยลอง plot แผนที่ดู&lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;สรุปว่าจากบ้านไป opendream = 10 KM&lt;br /&gt;มิน่าวันนี้ทำไมรู้สึกเพลียๆ (อากาศมันร้อนด้วย)&lt;br /&gt;&lt;br /&gt;ปล. เส้นสีฟ้าคือ ไปกลับ บ้าน-บริษัท Ingres&lt;br /&gt;เส้นสีแดงคือ เส้นกลับถ้าใช้เสือหมอบ (ถ้าใช้เสื้อภูเขา กลับเส้นฟ้า)&lt;br /&gt;เส้นสีเขียว คือ ไปกลับ บ้าน-opendream&lt;br /&gt;ทั้งฟ้าและแดง ระยะทางเท่าๆกัน = 5 KM&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7770417573265853623?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7770417573265853623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7770417573265853623' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7770417573265853623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7770417573265853623'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/04/update.html' title='update เส้นทางขี่จักรยาน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_I3r3qYlfei4/S88QSmFIwsI/AAAAAAAABU8/yey2FN41Qq8/s72-c/Screen+shot+2010-04-21+at+9.42.44+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6783223316016312433</id><published>2010-03-23T09:50:00.002+07:00</published><updated>2010-03-23T09:57:10.350+07:00</updated><title type='text'>ชอบแบบไหน</title><content type='html'>&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;สองรูปข้างบนแสดงการต่อวงจรแบบเดียวกัน&lt;br /&gt;คำถามคือ "แวบแรกที่เห็น คุณชอบแบบไหน?"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;สมมติฐานของผมก็คือ จากคำตอบนั้นน่าจะคาดเดาได้ด้วยว่า คุณน่าจะชอบ programming language ตัวไหน.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6783223316016312433?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6783223316016312433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6783223316016312433' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6783223316016312433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6783223316016312433'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/03/blog-post_23.html' title='ชอบแบบไหน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_I3r3qYlfei4/S6gs5Y6kvgI/AAAAAAAABTg/iAsyJVVfx-U/s72-c/Screen+shot+2010-03-23+at+9.30.59+AM.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7783117956243847442</id><published>2010-03-16T10:58:00.003+07:00</published><updated>2010-03-16T11:26:29.761+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='วาดรูป'/><category scheme='http://www.blogger.com/atom/ns#' term='kids'/><title type='text'>วาดรูป-ฝึกสังเกต</title><content type='html'>ช่วงนี้ลูกๆปิดเทอม เลยได้ฤกษ์พากันไปเที่ยว แน่นอนว่าหน้าร้อนอย่างนี้ จะมีที่ไหนดีไปกว่าทะเลอีก ระหว่างทางต้องผ่านพิพิธภัณฑ์สัตว์น้ำหว้ากอด้วย แน่นอนว่าไม่แวะไม่ได้&lt;br /&gt;&lt;br /&gt;ระหว่างเดินดูสัตว์น้ำทั้งหลาย ผมก็พยายามฝึกให้ลูกมองเห็นสิ่งเล็กๆน้อยๆต่างๆ โดยใช้วิธีตั้งคำถาม หรือให้เทียบเคียงกับตัวเอง เช่น พอเจอเจ้า&lt;a href="http://images.google.com/images?rls=en&amp;q=ปลากลับหัว&amp;oe=UTF-8"&gt;ปลากลับหัว &lt;/a&gt;ก็ชวนกันเดินด้วยมือแทนเท้า&lt;br /&gt;&lt;br /&gt;ไปเที่ยวคราวนี้ลูกเริ่มโตขึ้น(5 ขวบ) ก็เลยเพิ่มกลยุทธ์การเรียนรู้ด้วยการวาดรูป&lt;br /&gt;วิธีการก็คือ พ่อเป็นคนวาด ส่วนลูกทำหน้าที่บอกรายละเอียดว่าถูกต้องไหม นับจำนวนว่าครบไม่ครบ&lt;br /&gt;&lt;br /&gt;อันนี้คือ เต่าทะเล กับ ปลาดาว ที่วาดกัน ลูกต้องนับขาปลาดาว กับนับจำนวนลวดลายของเต่า&lt;br /&gt;&lt;br /&gt;&lt;table style="width:auto;"&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/PnYWWWgJZxFHZ6WHuxzsDw?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_I3r3qYlfei4/S58HUW8Tj9I/AAAAAAAABSw/QvC0pOr8kTY/s144/2010-03-16%2008.51.18.jpg" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-family:arial,sans-serif; font-size:11px; text-align:right"&gt;From &lt;a href="http://picasaweb.google.com/pphetra/714BannGroodHuahin?feat=embedwebsite"&gt;7-14 bann grood,huahin&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7783117956243847442?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7783117956243847442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7783117956243847442' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7783117956243847442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7783117956243847442'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/03/blog-post.html' title='วาดรูป-ฝึกสังเกต'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_I3r3qYlfei4/S58HUW8Tj9I/AAAAAAAABSw/QvC0pOr8kTY/s72-c/2010-03-16%2008.51.18.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3138797849586951053</id><published>2010-01-28T11:43:00.009+07:00</published><updated>2010-02-01T14:24:09.043+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>code &lt;- พิกัด -&gt; diagram</title><content type='html'>วันก่อนนั่งแกะ code ของ &lt;a href="http://bitbucket.org/justin/webmachine/wiki/Home"&gt;Webmachine&lt;/a&gt; เห็น code ส่วนนี้น่าสนใจดี&lt;br /&gt;&lt;br /&gt;อันนี้เป็น &lt;a href="http://bitbucket.org/justin/webmachine/wiki/BigHTTPGraph"&gt;diagram แสดง flow decision&lt;/a&gt; ของ webmachine &lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ถ้าดูที่ขอบของ diagram เขาจะแสดง&lt;span style="font-weight:bold;"&gt;เลขพิกัด&lt;/span&gt;ด้วย&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ดู &lt;a href="http://bitbucket.org/justin/webmachine/src/tip/src/webmachine_decision_core.erl"&gt;code ที่เขา implement จริง&lt;/a&gt; จะเห็นว่าเขาตั้งชื่อ function อ้างอิงเลขพิกัดและ version ของเอกสารด้วย&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;%% &lt;/span&gt;&lt;span class="comment"&gt;"Forbidden?"&lt;br /&gt;&lt;/span&gt;&lt;span class="function-name"&gt;decision&lt;/span&gt;(v3b7) -&amp;gt;&lt;br /&gt;    decision_test(resource_call(forbidden), true, 403, v3b6);&lt;br /&gt;&lt;span class="comment-delimiter"&gt;%% &lt;/span&gt;&lt;span class="comment"&gt;"Okay Content-* Headers?"&lt;br /&gt;&lt;/span&gt;&lt;span class="function-name"&gt;decision&lt;/span&gt;(v3b6) -&amp;gt;&lt;br /&gt;    decision_test(resource_call(valid_content_headers), true, v3b5, 501);&lt;br /&gt;&lt;span class="comment-delimiter"&gt;%% &lt;/span&gt;&lt;span class="comment"&gt;"Known Content-Type?"&lt;br /&gt;&lt;/span&gt;&lt;span class="function-name"&gt;decision&lt;/span&gt;(v3b5) -&amp;gt;&lt;br /&gt;    decision_test(resource_call(known_content_type), true, v3b4, 415);&lt;br /&gt;&lt;span class="comment-delimiter"&gt;%% &lt;/span&gt;&lt;span class="comment"&gt;"Req Entity Too Large?"&lt;br /&gt;&lt;/span&gt;&lt;span class="function-name"&gt;decision&lt;/span&gt;(v3b4) -&amp;gt;&lt;br /&gt;    decision_test(resource_call(valid_entity_length), true, v3b3, 413);&lt;br /&gt;&lt;span class="comment-delimiter"&gt;%% &lt;/span&gt;&lt;span class="comment"&gt;"OPTIONS?"&lt;br /&gt;&lt;/span&gt;&lt;span class="function-name"&gt;decision&lt;/span&gt;(v3b3) -&amp;gt;&lt;br /&gt;    &lt;span class="keyword"&gt;case&lt;/span&gt; method() &lt;span class="keyword"&gt;of&lt;/span&gt; &lt;br /&gt;        &lt;span class="string"&gt;'OPTIONS'&lt;/span&gt; -&amp;gt;&lt;br /&gt;            &lt;span class="variable-name"&gt;Hdrs&lt;/span&gt; = resource_call(options),&lt;br /&gt;            respond(200, &lt;span class="variable-name"&gt;Hdrs&lt;/span&gt;);&lt;br /&gt;        &lt;span class="variable-name"&gt;_&lt;/span&gt; -&amp;gt;&lt;br /&gt;            d(v3c3)&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3138797849586951053?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3138797849586951053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3138797849586951053' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3138797849586951053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3138797849586951053'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/code-diagram.html' title='code &lt;- พิกัด -&gt; diagram'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_I3r3qYlfei4/S2Z_LsjMgRI/AAAAAAAABNs/wQ_gZtV4Uog/s72-c/Screen+shot+2010-02-01+at+2.13.14+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-8549632227572012387</id><published>2010-01-28T09:57:00.003+07:00</published><updated>2010-01-28T10:17:33.946+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='mochiweb'/><title type='text'>Helloworld with Mochiweb</title><content type='html'>ใน erlang ถ้าคิดว่าจะใช้อะไรเป็น web server ดี ก็ต้องคิดถึง Yaws ก่อนเพื่อน&lt;br /&gt;แต่ในระยะหลังเริ่มได้ยิน mochiweb และเห็นมี project ใหม่ๆที่นิยมใช้มากขึ้นเรื่อยๆ ก็เลยได้ฤกษ์ทำการทดสอบแบบง่ายๆ โดยจะ implement simple helloworld&lt;br /&gt;&lt;br /&gt;เริ่มด้วยการ checkout source code ของ mochiweb ก่อน&lt;br /&gt;&lt;br /&gt;svn checkout http://mochiweb.googlecode.com/svn/trunk/ mochiweb&lt;br /&gt;cd mochiweb&lt;br /&gt;&lt;br /&gt;จากนั้นก็สั่ง &lt;code&gt;make&lt;/code&gt; เพื่อ compile code (แน่นอนว่าต้องติดตั้ง erlang รอไว้ก่อนแล้ว)&lt;br /&gt;&lt;br /&gt;เริ่มต้นสร้างโปรเจค โดยสั่ง&lt;br /&gt;&lt;br /&gt;escript /PATH_TO/mochiweb/scripts/new_mochiweb.erl helloworld&lt;br /&gt;&lt;br /&gt;จะได้ directory หน้าตาประมาณนี้&lt;br /&gt;&lt;br /&gt;&lt;pre class="hl"&gt;./helloworld/&lt;br /&gt;./helloworld/support/&lt;br /&gt;    run_tests.escript&lt;br /&gt;    include.mk&lt;br /&gt;    start.sh&lt;br /&gt;    start-dev.sh&lt;br /&gt;./helloworld/src/&lt;br /&gt;    skel_web.erl&lt;br /&gt;    skel_sup.erl&lt;br /&gt;    skel_deps.erl&lt;br /&gt;    skel_app.erl&lt;br /&gt;    skel.hrl&lt;br /&gt;    skel.erl&lt;br /&gt;    skel.app&lt;br /&gt;    Makefile&lt;br /&gt;./helloworld/priv/&lt;br /&gt;./helloworld/priv/www/&lt;br /&gt;    index.html&lt;br /&gt;    Makefile&lt;br /&gt;./helloworld/include/&lt;br /&gt;./helloworld/doc/&lt;br /&gt;./helloworld/deps/&lt;/pre&gt;&lt;br /&gt;file ที่เป็นหัวใจหลักก็คือ helloword_web.erl ทำหน้าที่เป็น process ที่ handle incoming request&lt;br /&gt;ให้แก้ไข function loop ดังนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;loop&lt;/span&gt;(&lt;span class="variable-name"&gt;Req&lt;/span&gt;, &lt;span class="variable-name"&gt;DocRoot&lt;/span&gt;) -&amp;gt;&lt;br /&gt;    &lt;span class="string"&gt;"/"&lt;/span&gt; ++ &lt;span class="variable-name"&gt;Path&lt;/span&gt; = &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="builtin"&gt;get&lt;/span&gt;(path),&lt;br /&gt;    &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="builtin"&gt;get&lt;/span&gt;(method) &lt;span class="keyword"&gt;of&lt;/span&gt;&lt;br /&gt;        &lt;span class="variable-name"&gt;Method&lt;/span&gt; &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="variable-name"&gt;Method&lt;/span&gt; =:= &lt;span class="string"&gt;'GET'&lt;/span&gt;; &lt;span class="variable-name"&gt;Method&lt;/span&gt; =:= &lt;span class="string"&gt;'HEAD'&lt;/span&gt; -&amp;gt;&lt;br /&gt;            &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="variable-name"&gt;Path&lt;/span&gt; &lt;span class="keyword"&gt;of&lt;/span&gt;&lt;br /&gt;                &lt;span class="string"&gt;"helloworld"&lt;/span&gt; -&amp;gt;&lt;br /&gt;                    &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="type"&gt;ok&lt;/span&gt;({&lt;span class="string"&gt;"text/html"&lt;/span&gt;, &lt;span class="string"&gt;"&amp;lt;!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"&amp;gt;&lt;br /&gt;                            &amp;lt;html&amp;gt;&lt;br /&gt;                            &amp;lt;head&amp;gt;&amp;lt;title &amp;gt;Welcome to mochiweb&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;                            &amp;lt;body&amp;gt;&lt;br /&gt;                                &amp;lt;h1&amp;gt;Helloworld&amp;lt;/h1&amp;gt;&lt;br /&gt;                            &amp;lt;/body&amp;gt;&lt;br /&gt;                            &amp;lt;/html&amp;gt;"&lt;/span&gt;});&lt;br /&gt;                &lt;span class="variable-name"&gt;_&lt;/span&gt; -&amp;gt;&lt;br /&gt;                    &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="type"&gt;serve_file&lt;/span&gt;(&lt;span class="variable-name"&gt;Path&lt;/span&gt;, &lt;span class="variable-name"&gt;DocRoot&lt;/span&gt;)&lt;br /&gt;            &lt;span class="keyword"&gt;end&lt;/span&gt;;&lt;br /&gt;        &lt;span class="string"&gt;'POST'&lt;/span&gt; -&amp;gt;&lt;br /&gt;            &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="variable-name"&gt;Path&lt;/span&gt; &lt;span class="keyword"&gt;of&lt;/span&gt;&lt;br /&gt;                &lt;span class="variable-name"&gt;_&lt;/span&gt; -&amp;gt;&lt;br /&gt;                    &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="type"&gt;not_found&lt;/span&gt;()&lt;br /&gt;            &lt;span class="keyword"&gt;end&lt;/span&gt;;&lt;br /&gt;        &lt;span class="variable-name"&gt;_&lt;/span&gt; -&amp;gt;&lt;br /&gt;            &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="type"&gt;respond&lt;/span&gt;({501, [], []})&lt;br /&gt;    &lt;span class="keyword"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สั่ง run server โดยใช้คำสั่ง start-dev.sh&lt;br /&gt;แล้วเรียก http://localhost:8000/helloworld &lt;br /&gt;&lt;br /&gt;ถ้าเห็น crash report แบบนี้ อย่าตกใจ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;=CRASH REPORT==== 28-Jan-2010::09:31:23 ===&lt;br /&gt;  crasher:&lt;br /&gt;    initial call: mochiweb_socket_server:acceptor_loop/1&lt;br /&gt;    pid: &lt;0.57.0&gt;&lt;br /&gt;    registered_name: []&lt;br /&gt;    exception error: bad argument&lt;br /&gt;      in function  erlang:universaltime_to_localtime/1&lt;br /&gt;         called as erlang:universaltime_to_localtime({{1969,12,31},&lt;br /&gt;                                                      {23,59,59}})&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;มันเป็น bug ใช้วิธี export TZ=GMT-7 แก้ปัญหาไปพลางๆก่อน&lt;br /&gt;&lt;br /&gt;ทดลองแก้ไข code แล้วสั่ง refresh browser จะเห็นว่ามันไม่ reload ให้&lt;br /&gt;ไม่ต้องเศร้าใจ ให้เปิด command prompt สั่ง make , ใน development mode, mochiweb จะตรวจดูว่า binary code เปลี่ยนหรือไม่ ถ้าเปลี่ยนมันจะ reload ให้เอง&lt;br /&gt;&lt;br /&gt;ลองเปลี่ยน code ให้ รับ url /helloworld/ANYNAME แล้วแสดง message Hello ANYNAME ดูบ้าง&lt;br /&gt;&lt;pre class="hl"&gt;                &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="variable-name"&gt;Path&lt;/span&gt; &lt;span class="keyword"&gt;of&lt;/span&gt;&lt;br /&gt;                &lt;span class="string"&gt;"helloworld/"&lt;/span&gt; ++ &lt;span class="variable-name"&gt;ID&lt;/span&gt; -&amp;gt;&lt;br /&gt;                    &lt;span class="variable-name"&gt;Msg&lt;/span&gt; = &lt;span class="type"&gt;io_lib&lt;/span&gt;:&lt;span class="type"&gt;format&lt;/span&gt;(&lt;span class="string"&gt;"&amp;lt;!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"&amp;gt;&lt;br /&gt;                            &amp;lt;html&amp;gt;&lt;br /&gt;                            &amp;lt;head&amp;gt;&amp;lt;title &amp;gt;Welcome to mochiweb&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;                            &amp;lt;body&amp;gt;&lt;br /&gt;                                Hello ~s&lt;br /&gt;                            &amp;lt;/body&amp;gt;&lt;br /&gt;                            &amp;lt;/html&amp;gt;"&lt;/span&gt;, [&lt;span class="variable-name"&gt;ID&lt;/span&gt;]),&lt;br /&gt;                    &lt;span class="variable-name"&gt;Req&lt;/span&gt;:&lt;span class="type"&gt;ok&lt;/span&gt;({&lt;span class="string"&gt;"text/html"&lt;/span&gt;, &lt;span class="variable-name"&gt;Msg&lt;/span&gt;});&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-8549632227572012387?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/8549632227572012387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=8549632227572012387' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8549632227572012387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8549632227572012387'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/helloworld-with-mochiweb.html' title='Helloworld with Mochiweb'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5237213243070878025</id><published>2010-01-26T13:55:00.003+07:00</published><updated>2010-01-26T14:21:29.204+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git กับ Remote Branch</title><content type='html'>สรุปไว้กันลืม&lt;br /&gt;&lt;br /&gt;สมมติว่าเรา clone git มาจาก Remote&lt;br /&gt;โดย default เราจะได้ local branch ที่ชื่อ "master" ที่ track กับ remote branch ที่ชื่อ "master" &lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git branch&lt;br /&gt;* master&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;แต่ถ้าเราสั่งให้ show branch ทั้งหมด&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git branch -a&lt;br /&gt;* master&lt;br /&gt;  origin/HEAD&lt;br /&gt;  origin/activity-calendar&lt;br /&gt;  origin/master&lt;br /&gt;  origin/pilot&lt;br /&gt;  origin/pilot_20100128&lt;br /&gt;  origin/user-admin&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จะเห็นว่า git ทำการ fetch branch ทั้งหมดมาให้เรา เพียงแต่มัน map ขึ้นมาเป็น local branch ให้เราเฉพาะแค่ master&lt;br /&gt;&lt;br /&gt;ถ้าเราสั่ง git fetch สิ่งที่เกิดก็คือ git จะทำการดึงข้อมูลการเปลี่ยนแปลงทุกอย่างของ server มาที่เครื่องเรา แต่จะยังไม่ update Working copy ให้เรา&lt;br /&gt;&lt;br /&gt;ส่วน git pull ก็เทียบได้กับการสั่ง git fetch แล้วตามด้วย git merge โดยจะ merge จะเฉพาะ current HEAD ของเรา (ถ้า HEAD คือ master ก็จะ merge เฉพาะ master)&lt;br /&gt;&lt;br /&gt;ที่นี้ถ้าเราต้องการ track remote branch บนเครื่องเรา สิ่งที่ต้องทำก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git checkout -b pilot origin/pilot&lt;br /&gt;Branch pilot set up to track remote branch refs/remotes/origin/pilot.&lt;br /&gt;Switched to a new branch "pilot"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ปัญหาที่เกิดบ่อยๆก็คือ เราสั่ง track remote branch โดยตั้งชื่อ local branch ไม่ตรงกับ remote branch เช่น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git checkout -b myb1 origin/b1&lt;br /&gt;Branch myb1 set up to track remote branch refs/remotes/origin/b1.&lt;br /&gt;Switched to a new branch "myb1"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เวลาเราแก้ไข file ใน branch myb1 เสร็จแล้วอยาก push ไปที่ server &lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ vi MYFILE&lt;br /&gt;…&lt;br /&gt;$ git add MYFILE&lt;br /&gt;$ git commit -m 'Add MYFILE'&lt;br /&gt;&lt;br /&gt;$ git push&lt;br /&gt;Everything up-to-date&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จะเห็นว่ามันไม่ยอม push commit ใหม่ของเราไปที่ remote repository&lt;br /&gt;ที่เป็นเช่นนี้เพราะถ้าเราสั่ง git push โดยไม่มี parameter สิ่งที่ git ทำก็คือ มันจะ push เฉพาะชื่อ branch ที่ตรงกันของทั้งสองฝั่ง&lt;br /&gt;&lt;br /&gt;การแก้ไขก็คือ เปลี่ยนตัวแปร configuration push.defaut ให้เป็น tracking เพื่อที่จะให้มัน push เฉพาะ branch ที่มี tracking กับ remote branch โดยไม่สนใจเรื่องชื่อตรงกัน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git push&lt;br /&gt;Everything up-to-date&lt;br /&gt;pphetra@[~/t2]&lt;br /&gt;$ git config push.default tracking&lt;br /&gt;pphetra@[~/t2]&lt;br /&gt;$ git push&lt;br /&gt;Counting objects: 5, done.&lt;br /&gt;Delta compression using up to 2 threads.&lt;br /&gt;Compressing objects: 100% (2/2), done.&lt;br /&gt;Writing objects: 100% (3/3), 248 bytes, done.&lt;br /&gt;Total 3 (delta 1), reused 0 (delta 0)&lt;br /&gt;To git@github.com:pphetra/test.git&lt;br /&gt;   7d96d34..0dd66fa  myb1 -&gt; b1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ที่นี้ถ้าเราต้องการสร้าง branch ใหม่บน remote หล่ะ&lt;br /&gt;เริ่มจากการสร้าง branch บน server&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git push origin origin:refs/heads/b4&lt;br /&gt;Total 0 (delta 0), reused 0 (delta 0)&lt;br /&gt;To git@github.com:pphetra/test.git&lt;br /&gt; * [new branch]      origin/HEAD -&gt; b4&lt;br /&gt;$ gitx&lt;br /&gt;&lt;br /&gt;$ git branch -a&lt;br /&gt;* b1&lt;br /&gt;  b2&lt;br /&gt;  master&lt;br /&gt;  myb1&lt;br /&gt;  remotes/origin/HEAD -&gt; origin/master&lt;br /&gt;  remotes/origin/b1&lt;br /&gt;  remotes/origin/b2&lt;br /&gt;  remotes/origin/b4&lt;br /&gt;  remotes/origin/master&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จากนั้นก็สั่ง checkout ตามปกติได้เลย&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git checkout -b b4 origin/b4&lt;br /&gt;Branch b4 set up to track remote branch b4 from origin.&lt;br /&gt;Switched to a new branch 'b4'&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5237213243070878025?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5237213243070878025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5237213243070878025' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5237213243070878025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5237213243070878025'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/git-remote-branch.html' title='Git กับ Remote Branch'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3349762859597876136</id><published>2010-01-21T09:55:00.006+07:00</published><updated>2010-01-21T10:47:42.912+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>เล่น Lego</title><content type='html'>Lego เป็นของเล่นที่ดี แต่ราคาแพง สมัยเด็กๆจำได้ว่าอยากได้มาก ต้องรอจนมัธยมแล้ว แม่ถึงซื้อให้เล่น 1 ชุด โดยมีข้อแม้ว่าซื้อให้ ผมกับน้องสองคน ไม่ใช่ของใครคนใดคนหนึ่ง&lt;br /&gt;&lt;br /&gt;พอโตขึ้นมามีลูกของตัวเอง ช่วงน้องปัณณ์อายุ 4 ขวบ อาเก่งกับอาหนึ่งซื้อ Lego ให้เป็นของขวัญ, ปรากฎว่าน้องปัณณ์ชอบของเล่นชิ้นนั้นมาก นั่นต่อ Lego ตามจินตนาการไปเรื่อย, หน้าที่พ่อก็เลยต้องคอยหา Input เข้าไปเรื่อยๆ เพื่อให้เกิดความท้าทายและไม่น่าเบื่อ (ตามกฎของ Flow)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/S1fKJHg5m2I/AAAAAAAABMw/C-b1BeDJK3E/s1600-h/flow_graph.jpg"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ในกรณีของ Lego นี้, input มีอยู่สองสามแบบก็คือ&lt;br /&gt;1. ซื้อชุดใหม่ให้เขา ให้เขาได้ตื่นเต้นกับแบบใหม่ๆ อันนี้ทำบ่อยๆไม่ดีแน่ เพราะราคามันสูงมากและถ้าได้มาง่ายไป มันจะไม่มีค่าเท่าไร&lt;br /&gt;2. หา MOC (&lt;a href="http://www.brickshelf.com/cgi-bin/customview.cgi?include=MOC"&gt;My Own Creation&lt;/a&gt;) ที่คนอื่นสร้างไว้มาให้เขาดู เขาจะได้เกิดแรงบันดาลใจ และเรียนรู้ว่ามันมีความเป็นไปได้ในการต่อเยอะแยะไปหมด&lt;br /&gt;3. หาเนื้อหาใหม่ๆ ที่ทำให้เขาใช้ Lego เป็นเครื่องมือในการหมกมุ่นกับเนื้อหานั้นๆ เช่นช่วงวันเด็กผมพาไปปัณณ์ไปดูงาน Lego ที่เขาเอา Lego มาต่อเป็นเมืองจำลอง กลับบ้านมาก็เลยมีการทำฉากจำลองต่างๆกันบ้าง หรือแนะนำเกมส์ Spore ให้เขาเล่น ซึ่งพอเขาเล่น Lego เขาก็จะพยายามต่อเป็นสัตว์ประหลาดในเกมส์&lt;br /&gt;&lt;br /&gt;อันนี้เป็นผลงาน MOC ของลูกชาย ที่ต่อเมื่อคืนนี้&lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ส่วนของพ่อได้ลำนี้ออกมา&lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;หลังจากที่ลูกหมกมุ่นกับ Lego ได้เกือบ 1 ปี ตอนนี้ต่อมเก็บกดของพ่อเด็กเริ่มแตกแล้ว อันนี้เป็นสิ่งที่น่ากลัวมาก เพราะเราเป็นคนถือกระเป๋าเงิน, ลูกติด แต่มันไม่มีเงิน มันก็เลยซื้อไม่ได้ แต่ถ้าพ่อติด และพ่อมีเงิน อันนี้สิน่าหวาดเสียว&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3349762859597876136?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3349762859597876136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3349762859597876136' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3349762859597876136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3349762859597876136'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/lego.html' title='เล่น Lego'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_I3r3qYlfei4/S1fKJHg5m2I/AAAAAAAABMw/C-b1BeDJK3E/s72-c/flow_graph.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4449463734334929036</id><published>2010-01-19T10:51:00.002+07:00</published><updated>2010-01-19T11:04:57.240+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>MultiMethod with Visitor Pattern</title><content type='html'>ต่อจากเมื่อวาน &lt;a href="http://pphetra.blogspot.com/2010/01/multimethod.html"&gt;Link&lt;/a&gt;&lt;br /&gt;อาจารย์โป้งได้อธิบายให้ฟังด้วยว่า วิธีหนึ่งที่เขานิยมใช้ implement multiple dispatch ใน Language ที่ support แต่ single dispatch ก็คือ &lt;a href="http://en.wikipedia.org/wiki/Visitor_pattern"&gt;Visitor Pattern&lt;/a&gt; (อาจารย์โป้งไม่ได้ใช้วิธีนี้)&lt;br /&gt;&lt;br /&gt;ผมก็เลยเอาตัวอย่างเมื่อวานมาลองเขียนดู&lt;br /&gt;เริ่มแรกก็คือ ให้เจ้า concrete animal ของเรามี accept method ที่รับ Visitor interface ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;interface&lt;/span&gt; &lt;span class="type"&gt;Animal&lt;/span&gt; {&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;accept&lt;/span&gt;(&lt;span class="type"&gt;IVisitor&lt;/span&gt; &lt;span class="variable-name"&gt;visitor&lt;/span&gt;);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Cat&lt;/span&gt; &lt;span class="keyword"&gt;implements&lt;/span&gt; &lt;span class="type"&gt;Animal&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; accept(&lt;span class="type"&gt;IVisitor&lt;/span&gt; &lt;span class="variable-name"&gt;visitor&lt;/span&gt;) {&lt;br /&gt;                visitor.visit(&lt;span class="keyword"&gt;this&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Dog&lt;/span&gt; &lt;span class="keyword"&gt;implements&lt;/span&gt; &lt;span class="type"&gt;Animal&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; accept(&lt;span class="type"&gt;IVisitor&lt;/span&gt; &lt;span class="variable-name"&gt;visitor&lt;/span&gt;) {&lt;br /&gt;                visitor.visit(&lt;span class="keyword"&gt;this&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;จากนั้นก็ สร้าง Visitor Interface ที่มี method visit ครบทุก concrete animal ของเรา&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;interface&lt;/span&gt; &lt;span class="type"&gt;IVisitor&lt;/span&gt; {&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;visit&lt;/span&gt;(&lt;span class="type"&gt;Cat&lt;/span&gt; &lt;span class="variable-name"&gt;cat&lt;/span&gt;);&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;visit&lt;/span&gt;(&lt;span class="type"&gt;Dog&lt;/span&gt; &lt;span class="variable-name"&gt;dog&lt;/span&gt;);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;ทดลอง feed อาหารโดยใช้ pattern ใหม่นี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="type"&gt;Cat&lt;/span&gt; &lt;span class="variable-name"&gt;cat&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Cat&lt;/span&gt;();&lt;br /&gt;&lt;span class="type"&gt;Dog&lt;/span&gt; &lt;span class="variable-name"&gt;dog&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Dog&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;&lt;span class="type"&gt;IVisitor&lt;/span&gt; &lt;span class="variable-name"&gt;bone&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;IVisitor&lt;/span&gt;() {&lt;br /&gt;        &lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; visit(&lt;span class="type"&gt;Cat&lt;/span&gt; &lt;span class="variable-name"&gt;cat&lt;/span&gt;) {&lt;br /&gt;            System.out.println(&lt;span class="string"&gt;"I don't like this. Meaw Meaw!!!"&lt;/span&gt;);&lt;br /&gt;            &lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; visit(&lt;span class="type"&gt;Dog&lt;/span&gt; &lt;span class="variable-name"&gt;dog&lt;/span&gt;) {&lt;br /&gt;            System.out.println(&lt;span class="string"&gt;"I 'm full. Hong Hong!!!"&lt;/span&gt;);&lt;br /&gt;            &lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;};&lt;br /&gt;                &lt;br /&gt;&lt;span class="type"&gt;IVisitor&lt;/span&gt; &lt;span class="variable-name"&gt;fish&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;IVisitor&lt;/span&gt;() {&lt;br /&gt;        &lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; visit(&lt;span class="type"&gt;Cat&lt;/span&gt; &lt;span class="variable-name"&gt;cat&lt;/span&gt;) {&lt;br /&gt;            System.out.println(&lt;span class="string"&gt;"I like this. Meaw Meaw!!!"&lt;/span&gt;);&lt;br /&gt;            &lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; visit(&lt;span class="type"&gt;Dog&lt;/span&gt; &lt;span class="variable-name"&gt;dog&lt;/span&gt;) {&lt;br /&gt;            System.out.println(&lt;span class="string"&gt;"Yes, I can eat this. Hong Hong!!!"&lt;/span&gt;);&lt;br /&gt;            &lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;cat.accept(bone);&lt;br /&gt;dog.accept(bone);&lt;br /&gt;                &lt;br /&gt;cat.accept(fish);&lt;br /&gt;dog.accept(fish);&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4449463734334929036?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4449463734334929036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4449463734334929036' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4449463734334929036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4449463734334929036'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/multimethod-with-visitor-pattern.html' title='MultiMethod with Visitor Pattern'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-344450383513496874</id><published>2010-01-18T10:01:00.002+07:00</published><updated>2010-01-18T10:06:00.540+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>MultiMethod</title><content type='html'>เมื่อวันเสาร์ไป compkucamp มา เจออาจารย์โป้งเข้าก็เลยถามว่า ช่วงนี้ทำอะไรอยู่, อาจารย์โป้งก็ตอบว่ากำลัง implement &lt;a href="http://en.wikipedia.org/wiki/Multiple_dispatch"&gt;multimethod&lt;/a&gt; บน c++ อยู่ ว่าแล้วก็ควักโน๊ตบุ๊คออกมาแสดง อาจารย์โป้งใช้ software ได้ผสมปนเปมาก เริ่มจากเปิด microsoft visual c++ ขึ้นมา จากนั้นก็ switch ไปใช้ terminal บน mac เพื่อใช้ django generate project files จากนั้นก็ show file content ให้ดูโดยใช้ textmate &lt;br /&gt;&lt;br /&gt;หลังจากกลับมาบ้าน และส่งลูกเข้านอนหมดแล้ว เพื่อแก้ข้อสงสัยที่ฟังมา ก็เลยต้องเข้า wikipedia ไปหาอ่านเรื่อง &lt;a href="http://en.wikipedia.org/wiki/Multiple_dispatch"&gt;Multimethod หรือ Multiple Dispatch&lt;/a&gt; บ้าง&lt;br /&gt;&lt;br /&gt;ในการทำความเข้าใจกับเรื่องนี้ เราก็ควรจะเริ่มจาก basic สุดก่อนก็คือ &lt;a href="http://en.wikipedia.org/wiki/Single_dispatch"&gt;Single Dispatch&lt;/a&gt; ซึ่งใช้ใน Java, C++, Smalltalk, Objective-C&lt;br /&gt;&lt;br /&gt;ลองดู code นี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;abstract&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Animal&lt;/span&gt; {&lt;br /&gt;        &lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;abstract&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;feed&lt;/span&gt;(&lt;span class="type"&gt;Food&lt;/span&gt; &lt;span class="variable-name"&gt;food&lt;/span&gt;);&lt;br /&gt;        &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;abstract&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Food&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Fish&lt;/span&gt; &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Food&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Bone&lt;/span&gt; &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Food&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Dog&lt;/span&gt; &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Animal&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; feed(&lt;span class="type"&gt;Food&lt;/span&gt; &lt;span class="variable-name"&gt;food&lt;/span&gt;) {&lt;br /&gt;                System.out.println(&lt;span class="string"&gt;"I 'm full. Hong Hong!!!"&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Cat&lt;/span&gt; &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Animal&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; feed(&lt;span class="type"&gt;Food&lt;/span&gt; &lt;span class="variable-name"&gt;food&lt;/span&gt;) {&lt;br /&gt;                System.out.println(&lt;span class="string"&gt;"I 'm full. Meaw Meaw!!!"&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;feed&lt;/span&gt;(&lt;span class="type"&gt;Bone&lt;/span&gt; &lt;span class="variable-name"&gt;food&lt;/span&gt;) {&lt;br /&gt;                System.out.println(&lt;span class="string"&gt;"I don't like this. Meaw Meaw!!!"&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ถ้าลอง run แบบนี้ดู&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Runme&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;static&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;main&lt;/span&gt;(&lt;span class="type"&gt;String&lt;/span&gt;[] &lt;span class="variable-name"&gt;args&lt;/span&gt;) {&lt;br /&gt;                &lt;span class="type"&gt;Animal&lt;/span&gt; &lt;span class="variable-name"&gt;a&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Dog&lt;/span&gt;();&lt;br /&gt;                &lt;span class="type"&gt;Animal&lt;/span&gt; &lt;span class="variable-name"&gt;b&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Cat&lt;/span&gt;();&lt;br /&gt;                &lt;br /&gt;                &lt;span class="type"&gt;Bone&lt;/span&gt; &lt;span class="variable-name"&gt;bone&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Bone&lt;/span&gt;();&lt;br /&gt;                a.feed(bone);&lt;br /&gt;                b.feed(bone);&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;กรณีที่เป็น single dispatch ผลลัพท์ที่ได้ก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;I 'm full. Hong Hong!!!&lt;br /&gt;I 'm full. Meaw Meaw!!!&lt;/pre&gt;&lt;br /&gt;จะเห็นว่า single dispatch จะตัดสินใจเลือก method โดยดูแค่ว่าจะเลือกให้ class ไหนรับผิดชอบในการ handle การ call, โดยไม่ได้สนใจ type ของ arguments&lt;br /&gt;ส่วน multiple dispatch มันจะเลือก method โดยดู type ของ arguments ด้วย &lt;br /&gt;ถ้าทดลองนำ code ข้างบน ไป run ใน groovy ซึ่งเป็น multiple dispatch ผลลัพท์ทีได้ก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;I 'm full. Hong Hong!!!&lt;br /&gt;I don't like this. Meaw Meaw!!!&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-344450383513496874?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/344450383513496874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=344450383513496874' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/344450383513496874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/344450383513496874'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/multimethod.html' title='MultiMethod'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3402366836515350876</id><published>2010-01-14T10:26:00.004+07:00</published><updated>2010-01-14T19:20:48.930+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='learning'/><title type='text'>เรียนสีน้ำ</title><content type='html'>เมื่อวานที่ Opendream มีการจัดกิจกรรมเรียนวาดภาพสีน้ำครั้งแรก โดยมีน้องแพ็ครับหน้าที่เป็นผู้สอน, course แรกเริ่มด้วยการแจกรูปลายเส้นขนมเค็ก พร้อมกับให้ดูภาพจริงบน computer จากนั้นก็สั่งสั้นๆว่า "ระบาย"&lt;br /&gt;&lt;br /&gt;ผู้เรียนทั้งหลายก็ก้มหน้าก้มตาระบาย (บางคนก็ประทัวงบ้างว่า ยังไม่สอนเลย จะระบายได้อย่างไร) &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/S06Q5p6A80I/AAAAAAAABMg/DBWmdpHqeyc/s1600-h/56926161.jpg"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ผมชอบวิธีเรียนแบบนี้นะ ทักษะการระบายสีคือการปฎิบัติ อยากรู้ก็ต้องระบาย จะมามัวเรียนว่าจับพู่กันอย่างไร, หรือเรียนรู้ basic introduction ของสีน้ำ มันก็ไช่ที่&lt;br /&gt;&lt;br /&gt;ผมเคยหัดเขียนสีน้ำ เมื่อตอนยังหนุ่มๆเหมือนกัน สมัยนั้นพบว่า อุปสรรคสำคัญของคนที่อยากจะเรียนสีน้ำ ก็คือ "ความกลัว" กลัวว่าจะวาดหรือระบายออกมาไม่สวย. ก็เลยไม่กล้าลงมือทำ น่าสนใจตรงที่ว่า ความกลัว นั้นมันเกิดมาได้อย่างไร. เป็นผลจากการศึกษาหรือ culture หรือเป็น Generalize ของมนุษย์&lt;br /&gt;&lt;br /&gt;เพื่อเป็นกำลังใจให้น้องๆที่ชอบคาดหวังกับตัวเอง ก็เลยไปค้นรูปสีน้ำรูปแรก(18 ปีแล้ว) มาให้ดู &lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;ถ้าคาดหวังไว้สูงเกินจริง หรือมีความคิดลบกับตัวเอง ก็คงเลิกไปแล้ว&lt;br /&gt;แต่ถ้าวาดไปเรื่อยๆ ไม่กลัว สักวัน(ของผมนี่หลายปีอยู่) มันก็จะพอดูได้ขึ้นมา (อันนี้ก็ราวๆ 13 ปีแล้วมั้ง)&lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ประเด็นที่สำคัญของการระบายสีน้ำสำหรับผม ก็คือ ความสุขอยู่ที่ระหว่างการระบาย ถ้าไป focus ผิดที่ก็จะไปวางความสุขไว้ที่ผลลัพท์ อยากเห็นผลลัพท์ที่สวยงาม จริงๆแล้ว ผลลัพท์ที่สวยงามเป็นแค่ผลพลอยได้&lt;br /&gt;&lt;br /&gt;Quote ที่ตรงที่สุดสำหรับเรื่องนี้ คือ&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Experience is what you get when you didn't get what you wanted.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;เวลาลงสี จะเห็นได้ชัดเลยว่า มันออกมาไม่เหมือนที่เราอยากได้&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3402366836515350876?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3402366836515350876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3402366836515350876' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3402366836515350876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3402366836515350876'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/blog-post_14.html' title='เรียนสีน้ำ'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_I3r3qYlfei4/S06Q5p6A80I/AAAAAAAABMg/DBWmdpHqeyc/s72-c/56926161.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7251548797247767924</id><published>2010-01-07T13:21:00.003+07:00</published><updated>2010-01-07T13:28:25.061+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>เล่นน้ำฝน</title><content type='html'>เมื่อวานฝนตกหนัก โชคดีที่หนีออกจาก Opendream ตั้งแต่บ่ายแก่ๆ ก็เลยไม่ต้องผจญกรรม(รถติด)มากนัก&lt;br /&gt;&lt;br /&gt;ไปถึงบ้าน ฝนกำลังกระหน่ำ ก็เลยชวนลูกชายเบอร์สองออกไปเล่นน้ำฝนกัน (คนแรกไม่ชวน เพราะว่าพ่อพาขี่จักรยานตากฝนไปโรงเรียนบ่อยแล้ว)&lt;br /&gt;&lt;br /&gt;&lt;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"&gt;&lt;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" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เล่นได้พักเดียว ฟ้าผ่าเปรี้ยง เจ้าลูกชายวิ่งแนบเข้าบ้าน&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7251548797247767924?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7251548797247767924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7251548797247767924' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7251548797247767924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7251548797247767924'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/blog-post_07.html' title='เล่นน้ำฝน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_I3r3qYlfei4/S0V-Ysr6GMI/AAAAAAAABLU/LhFhO2BLFbs/s72-c/Screen+shot+2010-01-07+at+1.20.15+PM.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7585602186624463452</id><published>2010-01-05T12:56:00.003+07:00</published><updated>2010-01-05T13:20:50.233+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>Dynamically generate code in Erlang</title><content type='html'>ปัจจุบัน application framework ทั้งหลาย พยายามจะทำให้ชีวิตเราง่ายขึ้น ด้วยการลด noise ที่เราไม่จำเป็นต้องเห็น, generate code ที่จำเป็นต้องใช้ให้เรา&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.chicagoboss.org/"&gt;Chicago Boss&lt;/a&gt; Framework ก็อยู่ในกระแสนี้เช่นกัน ตัว Relation Mapping layer ของมันก็พยายามจะลดรูปให้เหลือน้อยที่สุด, คำถามสำหรับผมก็คือ ใน Erlang นี่เขาใช้เทคนิคอะไรมาช่วย generate code หรือทำ magic บ้าง&lt;br /&gt; &lt;br /&gt;ลองดูตัวอย่างการใช้งานก่อน เริ่มด้วยการ define Domain model&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="preprocessor"&gt;-module&lt;/span&gt;(blog_post, [&lt;span class="variable-name"&gt;Id&lt;/span&gt;, &lt;span class="variable-name"&gt;Title&lt;/span&gt;, &lt;span class="variable-name"&gt;Text&lt;/span&gt;, &lt;span class="variable-name"&gt;AuthorId&lt;/span&gt;]).&lt;br /&gt;&lt;span class="preprocessor"&gt;-compile&lt;/span&gt;(export_all).&lt;br /&gt;&lt;span class="preprocessor"&gt;-belongs_to&lt;/span&gt;(author).&lt;br /&gt;&lt;br /&gt;&lt;span class="preprocessor"&gt;-module&lt;/span&gt;(author, [&lt;span class="variable-name"&gt;Id&lt;/span&gt;, &lt;span class="variable-name"&gt;Name&lt;/span&gt;]).&lt;br /&gt;&lt;span class="preprocessor"&gt;-compile&lt;/span&gt;(export_all).&lt;br /&gt;&lt;span class="preprocessor"&gt;-has_many&lt;/span&gt;(blog_posts).&lt;/pre&gt;&lt;br /&gt;เทคนิคแรกที่เขาใช้ก็คือ &lt;a href="http://ftp.sunet.se/pub/lang/erlang/workshop/2003/paper/p29-carlsson.pdf"&gt;Parameterized Modul&lt;/a&gt;e ซึ่งช่วยให้ Module มีพฤติกรรมในลักษณะ OOP ได้ &lt;br /&gt;ลองดู code ตอนที่เรา new instance domain ของเรา&lt;br /&gt;&lt;pre class="hl"&gt;FakeAuthor = (&lt;span class="type"&gt;author&lt;/span&gt;:&lt;span class="type"&gt;new&lt;/span&gt;(id, &lt;span class="string"&gt;"YOUR NAME"&lt;/span&gt;)):&lt;span class="type"&gt;save&lt;/span&gt;(),&lt;br /&gt;&lt;span class="variable-name"&gt;BlogPost&lt;/span&gt; = &lt;span class="type"&gt;blog_post&lt;/span&gt;:&lt;span class="type"&gt;new&lt;/span&gt;(id,&lt;br /&gt;        &lt;span class="string"&gt;"BLOG TITLE"&lt;/span&gt;, &lt;br /&gt;        &lt;span class="string"&gt;"BLOG CONTENT"&lt;/span&gt;, &lt;br /&gt;        &lt;span class="variable-name"&gt;FakeAuthor&lt;/span&gt;:&lt;span class="type"&gt;id&lt;/span&gt;()),&lt;br /&gt;&lt;span class="variable-name"&gt;SavedBlogPost&lt;/span&gt; = &lt;span class="variable-name"&gt;BlogPost&lt;/span&gt;:&lt;span class="type"&gt;save&lt;/span&gt;(),&lt;/pre&gt;&lt;br /&gt;จะเห็นว่า module ของเรามี function "save", "getter"(ตรงที่ get id จาก fakeauthor) เพิ่มขึ้นมาให้เองโดยที่เราไม่ต้องเขียน คำถามก็คือ เขาใช้เทคนิคอะไรในการ generate code ส่วนนี้&lt;br /&gt;&lt;br /&gt;เริ่มแรกสุด code ในส่วน module นี้จะไม่ load ขึ้นมาผ่านกลไกปกติ แต่จะทำผ่านกลไกของตัวเอง โดยเริ่มต้น มันจะทำการ parse erlang file โดยใช้ function &lt;a href="http://ftp.sunet.se/pub/lang/erlang/doc/man/epp.html"&gt;epp:parse_file&lt;/a&gt; ผลที่ได้เราเรียกว่า Form &lt;br /&gt;&lt;br /&gt;ทดลองใช้ epp:parse_file กับ &lt;a href="http://iporsut.wordpress.com/2010/01/01/เอิร์ลแลงerlang-concurrent-programming-ตอน-1-processes/#comment-70"&gt;ตัวอย่างโปรแกรมที่น้องป้อเขียน&lt;/a&gt; &lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="preprocessor"&gt;-module&lt;/span&gt;(p).&lt;br /&gt;&lt;span class="preprocessor"&gt;-export&lt;/span&gt;([&lt;span class="type"&gt;start/0&lt;/span&gt;, &lt;span class="type"&gt;say_something/2&lt;/span&gt;]).&lt;br /&gt;&lt;span class="function-name"&gt;say_something&lt;/span&gt;(&lt;span class="variable-name"&gt;What&lt;/span&gt;, 0) -&amp;gt;&lt;br /&gt;    done;&lt;br /&gt;&lt;span class="function-name"&gt;say_something&lt;/span&gt;(&lt;span class="variable-name"&gt;What&lt;/span&gt;, &lt;span class="variable-name"&gt;Times&lt;/span&gt;) -&amp;gt;&lt;br /&gt;    &lt;span class="type"&gt;io&lt;/span&gt;:&lt;span class="type"&gt;format&lt;/span&gt;(&lt;span class="string"&gt;"~p~n"&lt;/span&gt;, [&lt;span class="variable-name"&gt;What&lt;/span&gt;]),&lt;br /&gt;    &lt;span class="type"&gt;say_something&lt;/span&gt;(&lt;span class="variable-name"&gt;What&lt;/span&gt;, &lt;span class="variable-name"&gt;Times&lt;/span&gt; - 1).&lt;br /&gt;&lt;span class="function-name"&gt;start&lt;/span&gt;() -&amp;gt;&lt;br /&gt;    &lt;span class="builtin"&gt;spawn&lt;/span&gt;(tut14, say_something, [hello, 3]),&lt;br /&gt;    &lt;span class="builtin"&gt;spawn&lt;/span&gt;(tut14, say_something, [goodbye, 3]).&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จะได้ Form ที่มีหน้าตาประมาณนี้ &lt;br /&gt;&lt;pre class="hl"&gt;{ok,[{attribute,1,file,{&lt;span class="string"&gt;"p.erl"&lt;/span&gt;,1}},&lt;br /&gt;     {attribute,1,module,p},&lt;br /&gt;     {attribute,2,export,[{start,0},{say_something,2}]},&lt;br /&gt;     {function,3,say_something,2,&lt;br /&gt;               [{clause,3,&lt;br /&gt;                        [{var,3,&lt;span class="string"&gt;'What'&lt;/span&gt;},{integer,3,0}],&lt;br /&gt;                        [],&lt;br /&gt;                        [{atom,4,done}]},&lt;br /&gt;                {clause,5,&lt;br /&gt;                        [{var,5,&lt;span class="string"&gt;'What'&lt;/span&gt;},{var,5,&lt;span class="string"&gt;'Times'&lt;/span&gt;}],&lt;br /&gt;                        [],&lt;br /&gt;                        [{call,6,&lt;br /&gt;                               {remote,6,{atom,6,io},{atom,6,format}},&lt;br /&gt;                               [{string,6,&lt;span class="string"&gt;"~p~n"&lt;/span&gt;},{cons,6,{var,...},{...}}]},&lt;br /&gt;                         {call,7,&lt;br /&gt;                               {atom,7,say_something},&lt;br /&gt;                               [{var,7,&lt;span class="string"&gt;'What'&lt;/span&gt;},{op,7,&lt;span class="string"&gt;'-'&lt;/span&gt;,...}]}]}]},&lt;br /&gt;     {function,8,start,0,&lt;br /&gt;               [{clause,8,[],[],&lt;br /&gt;                        [{call,9,&lt;br /&gt;                               {atom,9,spawn},&lt;br /&gt;                               [{atom,9,tut14},&lt;br /&gt;                                {atom,9,say_something},&lt;br /&gt;                                {cons,9,{...},...}]},&lt;br /&gt;                         {call,10,&lt;br /&gt;                               {atom,10,spawn},&lt;br /&gt;                               [{atom,10,tut14},&lt;br /&gt;                                {atom,10,say_something},&lt;br /&gt;                                {cons,10,...}]}]}]},&lt;br /&gt;     {eof,11}]}&lt;/pre&gt;&lt;br /&gt;จะเห็นว่ามีลักษณะเป็น Abstract Syntax Tree + Meta Data&lt;br /&gt;&lt;br /&gt;พอได้ form มา เจ้า ChicagoBoss ก็จะทำการแทรก,แปลง code ให้เป็นไปตามต้องการ โดยมันจะใช้ function ใน library &lt;a href="http://erlang.mirror.su.se/documentation/doc-5.7.3/lib/syntax_tools-1.6.3/doc/html/erl_syntax.html"&gt;erl_syntax&lt;/a&gt; ช่วยในการ generate Form&lt;br /&gt;&lt;br /&gt;ตัวอย่าง code ในส่วนที่สร้าง getter&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;parameter_getter_forms&lt;/span&gt;(&lt;span class="variable-name"&gt;Parameters&lt;/span&gt;) -&amp;gt;&lt;br /&gt;    &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;map&lt;/span&gt;(&lt;span class="keyword"&gt;fun&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;) -&amp;gt;&lt;span class="function-name"&gt; &lt;/span&gt;&lt;br /&gt;                &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;add_precomments&lt;/span&gt;([&lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;comment&lt;/span&gt;(&lt;br /&gt;                        [&lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;concat&lt;/span&gt;([&lt;span class="string"&gt;"% @spec "&lt;/span&gt;, &lt;span class="type"&gt;parameter_to_colname&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;), &lt;span class="string"&gt;"() -&amp;gt; "&lt;/span&gt;, &lt;span class="variable-name"&gt;P&lt;/span&gt;]),&lt;br /&gt;                            &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;concat&lt;/span&gt;([&lt;span class="string"&gt;"% @doc Returns the value of `"&lt;/span&gt;, &lt;span class="variable-name"&gt;P&lt;/span&gt;, &lt;span class="string"&gt;"'"&lt;/span&gt;])])],&lt;br /&gt;                    &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;function&lt;/span&gt;(&lt;br /&gt;                        &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;atom&lt;/span&gt;(&lt;span class="type"&gt;parameter_to_colname&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;)),&lt;br /&gt;                        [&lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;clause&lt;/span&gt;([], none, [&lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;variable&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;)])]))&lt;br /&gt;        &lt;span class="keyword"&gt;end&lt;/span&gt;, &lt;span class="variable-name"&gt;Parameters&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span class="function-name"&gt;parameter_setter_forms&lt;/span&gt;(&lt;span class="variable-name"&gt;ModuleName&lt;/span&gt;, &lt;span class="variable-name"&gt;Parameters&lt;/span&gt;) -&amp;gt;&lt;br /&gt;    &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;map&lt;/span&gt;(&lt;br /&gt;        &lt;span class="keyword"&gt;fun&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;) -&amp;gt;&lt;br /&gt;                &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;add_precomments&lt;/span&gt;([&lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;comment&lt;/span&gt;(&lt;br /&gt;                        [&lt;br /&gt;                            &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;concat&lt;/span&gt;([&lt;span class="string"&gt;"% @spec "&lt;/span&gt;, &lt;span class="type"&gt;parameter_to_colname&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;), &lt;span class="string"&gt;"( "&lt;/span&gt;, &lt;span class="variable-name"&gt;P&lt;/span&gt;, &lt;span class="string"&gt;"::"&lt;/span&gt;, &lt;br /&gt;                                    &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;suffix&lt;/span&gt;(&lt;span class="string"&gt;"Time"&lt;/span&gt;, &lt;span class="builtin"&gt;atom_to_list&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;)) &lt;span class="keyword"&gt;of&lt;/span&gt; &lt;br /&gt;                                        true -&amp;gt;&lt;span class="function-name"&gt; &lt;/span&gt;&lt;span class="string"&gt;"tuple()"&lt;/span&gt;;&lt;br /&gt;                                        false -&amp;gt;&lt;span class="function-name"&gt; &lt;/span&gt;&lt;span class="string"&gt;"string()"&lt;/span&gt;&lt;br /&gt;                                    &lt;span class="keyword"&gt;end&lt;/span&gt;, &lt;span class="string"&gt;" ) -&amp;gt; "&lt;/span&gt;, &lt;span class="type"&gt;inflector&lt;/span&gt;:&lt;span class="type"&gt;camelize&lt;/span&gt;(&lt;span class="builtin"&gt;atom_to_list&lt;/span&gt;(&lt;span class="variable-name"&gt;ModuleName&lt;/span&gt;))]),&lt;br /&gt;                            &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;concat&lt;/span&gt;([&lt;span class="string"&gt;"% @doc Set the value of `"&lt;/span&gt;, &lt;span class="variable-name"&gt;P&lt;/span&gt;, &lt;span class="string"&gt;"'."&lt;/span&gt;])])],&lt;br /&gt;                    &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;function&lt;/span&gt;(&lt;br /&gt;                        &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;atom&lt;/span&gt;(&lt;span class="type"&gt;parameter_to_colname&lt;/span&gt;(&lt;span class="variable-name"&gt;P&lt;/span&gt;)),&lt;br /&gt;                        [&lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;clause&lt;/span&gt;([&lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;variable&lt;/span&gt;(&lt;span class="string"&gt;"NewValue"&lt;/span&gt;)], none,&lt;br /&gt;                                [&lt;br /&gt;                                    &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;application&lt;/span&gt;(&lt;br /&gt;                                        &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;atom&lt;/span&gt;(&lt;span class="variable-name"&gt;ModuleName&lt;/span&gt;),&lt;br /&gt;                                        &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;atom&lt;/span&gt;(new),&lt;br /&gt;                                        &lt;span class="type"&gt;lists&lt;/span&gt;:&lt;span class="type"&gt;map&lt;/span&gt;(&lt;br /&gt;                                            &lt;span class="keyword"&gt;fun&lt;/span&gt;&lt;br /&gt;                                                (&lt;span class="variable-name"&gt;Param&lt;/span&gt;) &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="variable-name"&gt;Param&lt;/span&gt; =:= &lt;span class="variable-name"&gt;P&lt;/span&gt; -&amp;gt;&lt;br /&gt;                                                    &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;variable&lt;/span&gt;(&lt;span class="string"&gt;"NewValue"&lt;/span&gt;);&lt;br /&gt;                                                (&lt;span class="variable-name"&gt;Other&lt;/span&gt;) -&amp;gt;&lt;br /&gt;                                                    &lt;span class="type"&gt;erl_syntax&lt;/span&gt;:&lt;span class="type"&gt;variable&lt;/span&gt;(&lt;span class="variable-name"&gt;Other&lt;/span&gt;)&lt;br /&gt;                                            &lt;span class="keyword"&gt;end&lt;/span&gt;, &lt;span class="variable-name"&gt;Parameters&lt;/span&gt;))&lt;br /&gt;                                ])]))&lt;br /&gt;        &lt;span class="keyword"&gt;end&lt;/span&gt;, &lt;span class="variable-name"&gt;Parameters&lt;/span&gt;).&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;พอ transform code เสร็จ ก็จะทำการ compile เป็น binary ด้วย function &lt;a href="http://ftp.sunet.se/pub//lang/erlang/doc/man/compile.html"&gt;compile:forms&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;compile เสร็จก็จัดการ load เข้า runtime โดยใช้ function &lt;a href="http://ftp.sunet.se/pub/lang/erlang/doc/man/code.html"&gt;code:load_binary&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7585602186624463452?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7585602186624463452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7585602186624463452' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7585602186624463452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7585602186624463452'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/dynamically-generate-code-in-erlang.html' title='Dynamically generate code in Erlang'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6146574899046229153</id><published>2010-01-04T11:19:00.003+07:00</published><updated>2010-01-04T11:25:51.649+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>P07 กับ List Moand</title><content type='html'>เห็นน้องป้อทำโจทย์ &lt;a href="http://iporsut.wordpress.com/2010/01/03/แก้โจทย์-l-99ninety-nine-lisp-problems-p07"/&gt;P07 ของ Ninety-Nine Lisp Problem&lt;/a&gt; ก็เลยกลับไปอ่าน&lt;a href="http://pphetra.blogspot.com/2007/01/ninety-nine-lisp-problem-p07.html"&gt;วิธีทำของตัวเอง&lt;/a&gt; แล้วก็พบว่าถึงแม้เวลาจะผ่านมา 4 ปีแล้ว ตัวเองก็ยังไม่เคยเข้าใจคำตอบของ Conor McBride เลย  วันนี้ก็เลยได้ฤกษ์ทำความเข้าใจกับ List Monad ที่ Conor ใช้ตอบคำถามผม&lt;br /&gt;&lt;br /&gt;เริ่มด้วยคำอธิบาย Monad ที่บอกว่า Monad คือ "an abstract datatype of actions" ซึ่งมีนิยามง่ายๆแค่นี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Monad&lt;/span&gt; m &lt;span class="keyword"&gt;where&lt;/span&gt;&lt;br /&gt;    (&lt;span class="variable-name"&gt;&amp;gt;&amp;gt;=&lt;/span&gt;) &lt;span class="variable-name"&gt;::&lt;/span&gt; m a &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; (a &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; m b) &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; m b&lt;br /&gt;    return &lt;span class="variable-name"&gt;::&lt;/span&gt; a &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; m a&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;มันคือ pattern ฉนั้นอย่าพึ่งไปพยายามจินตนาการว่า มันทำอะไรได้บ้าง&lt;br /&gt;หันกลับมาดู context ของ List บ้าง เรารู้ว่า type ของ List คือ &lt;code&gt;[a]&lt;/code&gt;  จะเห็นว่า type &lt;code&gt;[a]&lt;/code&gt; มันสามารถมองเป็น &lt;code&gt;m a&lt;/code&gt; ได้ (เพราะ type constructor มันมี free variable 1 ตัวเหมือนกัน) ฉนั้นเราลองมา implement ให้ List เป็น Monad กัน&lt;br /&gt;&lt;br /&gt;เริ่มจาก implementation ที่ง่ายที่สุดก่อน ก็คือ return, return มี type เป็น &lt;code&gt;a -&gt; m a&lt;/code&gt; ฉนั้นกรณีของ List, คำสั่ง &lt;code&gt;return 4&lt;/code&gt; ก็ควรได้ค่า &lt;code&gt;[4]&lt;/code&gt; ออกมา&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;instance&lt;/span&gt; &lt;span class="type"&gt;Monad&lt;/span&gt; &lt;span class="type"&gt;[]&lt;/span&gt; &lt;span class="keyword"&gt;where&lt;/span&gt;&lt;br /&gt;        return x &lt;span class="variable-name"&gt;=&lt;/span&gt; [x]&lt;/pre&gt;&lt;br /&gt;ตัวถัดมาก็คือ function &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt; นิยามของ type มันคือ &lt;code&gt;m a -&amp;gt; (a -&amp;gt; m b) -&amp;gt; m b&lt;/code&gt; &lt;br /&gt;เปลี่ยน &lt;code&gt;m a&lt;/code&gt; ให้เป็น &lt;code&gt;[a]&lt;/code&gt; จะได้ &lt;code&gt;[a] -&amp;gt; (a -&amp;gt; [b]) -&amp;gt; [b]&lt;/code&gt; จะเห็นว่า definition มันไกล้เคียงกับ map function ที่มี type เป็น &lt;code&gt;[a] -&amp;gt; (a -&amp;gt; b) -&amp;gt; [b]&lt;/code&gt; ดังนั้นเราสามารถ implement &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt; โดยใช้ map และ concat &lt;br /&gt;หน้าตาออกมาดังนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;instance&lt;/span&gt; &lt;span class="type"&gt;Monad&lt;/span&gt; &lt;span class="type"&gt;[]&lt;/span&gt; &lt;span class="keyword"&gt;where&lt;/span&gt;&lt;br /&gt;    return x &lt;span class="variable-name"&gt;=&lt;/span&gt; [x]&lt;br /&gt;    xs &lt;span class="variable-name"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; f &lt;span class="variable-name"&gt;=&lt;/span&gt; concat (map f xs)&lt;/pre&gt;&lt;br /&gt;ได้นิยามของ List ในมุมมองของ Monad ออกมาอย่างงงๆ&lt;br /&gt;แล้วมันมีประโยชน์อะไรหล่ะ ที่ทำ List type ให้เป็น instance ของ Monad type&lt;br /&gt;&lt;br /&gt;ลองมาหัดใช้ List Monad ทำโจทย์ &lt;br /&gt;&lt;blockquote&gt;ถ้าให้ list ของ [1..10] มาให้หาตัวเลข 2 ตัวที่ผลคูณมีค่า = 16&lt;/blockquote&gt;&lt;br /&gt;ถ้าใช้ List Monad ก็จะเขียนแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;guard&lt;/span&gt; &lt;span class="type"&gt;True&lt;/span&gt; xs &lt;span class="variable-name"&gt;=&lt;/span&gt; xs&lt;br /&gt;&lt;span class="function-name"&gt;guard&lt;/span&gt; &lt;span class="type"&gt;False&lt;/span&gt; xs &lt;span class="variable-name"&gt;=&lt;/span&gt; &lt;span class="type"&gt;[]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="function-name"&gt;solve&lt;/span&gt; &lt;span class="variable-name"&gt;=&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;  x &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; [1&lt;span class="variable-name"&gt;..&lt;/span&gt;10]&lt;br /&gt;  y &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; [x&lt;span class="variable-name"&gt;..&lt;/span&gt;10]&lt;br /&gt;  guard (x &lt;span class="variable-name"&gt;*&lt;/span&gt; y &lt;span class="variable-name"&gt;==&lt;/span&gt; 16) (return (x,y))&lt;/pre&gt;&lt;br /&gt;ซึ่งมี form ที่ไกล้เคียงกับ solution ที่ใช้ List comprehension&lt;br /&gt;&lt;pre class="hl"&gt;[(x,y) &lt;span class="variable-name"&gt;|&lt;/span&gt; x &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; [1&lt;span class="variable-name"&gt;..&lt;/span&gt;10], y &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; [x&lt;span class="variable-name"&gt;..&lt;/span&gt;10], x &lt;span class="variable-name"&gt;*&lt;/span&gt; y &lt;span class="variable-name"&gt;==&lt;/span&gt; 16]&lt;/pre&gt;&lt;br /&gt;กลับมาที่ solution ที่ McBride เขียนตอบผม&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;flat1&lt;/span&gt; &lt;span class="variable-name"&gt;::&lt;/span&gt; &lt;span class="type"&gt;Store&lt;/span&gt; a &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; a&lt;br /&gt;&lt;span class="function-name"&gt;flat1&lt;/span&gt; (&lt;span class="type"&gt;E&lt;/span&gt; a) &lt;span class="variable-name"&gt;=&lt;/span&gt; return a&lt;br /&gt;&lt;span class="function-name"&gt;flat1&lt;/span&gt; (&lt;span class="type"&gt;S&lt;/span&gt; xs) &lt;span class="variable-name"&gt;=&lt;/span&gt; xs &lt;span class="variable-name"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; flat1&lt;/pre&gt;&lt;br /&gt;ถึงตอนนี้พอจะรู้เรื่องกับคำอธิบายของเขาแล้ว&lt;br /&gt;&lt;blockquote&gt;Your (flat xs) on a list of stores becomes my (xs &gt;&gt;= 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. &lt;b&gt;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&lt;/b&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6146574899046229153?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6146574899046229153/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6146574899046229153' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6146574899046229153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6146574899046229153'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/p07-list-moand.html' title='P07 กับ List Moand'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5910081028429723507</id><published>2010-01-03T21:13:00.001+07:00</published><updated>2010-01-03T21:17:03.829+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>บ้านน้ำแข็ง</title><content type='html'>ช่วงปีใหม่ หนีร้อนไปอยู่บ้านย่า พอกลับมาถึงบ้านตัวเองพบว่า ตู้เย็นที่ defrost ไว้ น้ำในช่องน้ำแข็งไหลลงมาอยู่ในถาดแล้วกลายเป็นน้ำแข็งแผ่นใหญ่ไปแล้ว เห็นแล้วก็เลยได้ idea ชักชวนลูกชายให้มาทำบ้านน้ำแข็งของชาวเอสกิโมกัน &lt;br /&gt;&lt;br /&gt;เริ่มด้วยการสอนให้ลูกชายใช้ฆ้อนกับสิ่วตอกน้ำแข็งเป็นก้อนพอเหมาะ จากนั้นก็เรียงกันเป็นทรงกลม เชื่อมประสานก้อนน้ำแข็งด้วยเกลือ เสร็จแล้วก็ถ่ายรูปเพื่อให้(พ่อและ)ลูกได้ชื่นชมผลงาน&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/S0CmcMvyYsI/AAAAAAAABKo/d6m-2zzV6x0/s1600-h/Screen+shot+2010-01-03+at+9.12.14+PM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 234px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/S0CmcMvyYsI/AAAAAAAABKo/d6m-2zzV6x0/s320/Screen+shot+2010-01-03+at+9.12.14+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5422516954742284994" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5910081028429723507?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5910081028429723507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5910081028429723507' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5910081028429723507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5910081028429723507'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/blog-post.html' title='บ้านน้ำแข็ง'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_I3r3qYlfei4/S0CmcMvyYsI/AAAAAAAABKo/d6m-2zzV6x0/s72-c/Screen+shot+2010-01-03+at+9.12.14+PM.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2331761629262945489</id><published>2010-01-02T12:34:00.003+07:00</published><updated>2010-01-02T21:29:09.254+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>Parameterized Module</title><content type='html'>วันนี้นั่งดู code ของ erlang web framework ที่ชื่อ "&lt;a href="http://www.chicagoboss.org/"&gt;Chicago Boss&lt;/a&gt;" แล้วก็สงสัยว่า syntax ของ model/domain ของมัน ซึ่งดูไม่คุ้นตาเลย&lt;br /&gt;&lt;br /&gt;เริ่มจากตัว domain model code ก่อน, เจ้า chicago boss กำหนดให้เขียนแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="preprocessor"&gt;-module&lt;/span&gt;(blog_post, [&lt;span class="variable-name"&gt;Id&lt;/span&gt;, &lt;span class="variable-name"&gt;Title&lt;/span&gt;, &lt;span class="variable-name"&gt;Text&lt;/span&gt;, &lt;span class="variable-name"&gt;AuthorId&lt;/span&gt;]).&lt;br /&gt;&lt;span class="preprocessor"&gt;-compile&lt;/span&gt;(export_all).&lt;/pre&gt;&lt;br /&gt;เวลาเอาไปใช้งาน ก็จะใช้แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;BlogPost = &lt;span class="type"&gt;blog_post&lt;/span&gt;:&lt;span class="type"&gt;new&lt;/span&gt;(id, &lt;span class="string"&gt;"my title"&lt;/span&gt;, &lt;span class="string"&gt;"la la"&lt;/span&gt;, &lt;span class="variable-name"&gt;SomeAuthorId&lt;/span&gt;)&lt;br /&gt;&lt;span class="variable-name"&gt;BlogPost&lt;/span&gt;:&lt;span class="type"&gt;save&lt;/span&gt;()&lt;/pre&gt;&lt;br /&gt;เริ่มจาก บรรทัดนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="preprocessor"&gt;-module&lt;/span&gt;(blog_post, [&lt;span class="variable-name"&gt;Id&lt;/span&gt;, &lt;span class="variable-name"&gt;Title&lt;/span&gt;, &lt;span class="variable-name"&gt;Text&lt;/span&gt;, &lt;span class="variable-name"&gt;AuthorId&lt;/span&gt;]).&lt;/pre&gt;&lt;br /&gt;syntax การ declare แบบนี้ มีศัพท์เฉพาะ เรียกว่า Parameterize Module&lt;br /&gt;parameter ที่ตามหลัง module name (ในกรณีนี้ก็คือ Id, Title, Text, …) จะกลายเป็นตัวแปรที่สามารถ access ได้จากทุก function ที่อยู่ใน module นั้น&lt;br /&gt;&lt;br /&gt;ส่วนที่น่าสนใจถัดไปก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;BlogPost = &lt;span class="type"&gt;blog_post&lt;/span&gt;:&lt;span class="type"&gt;new&lt;/span&gt;(id, &lt;span class="string"&gt;"my title"&lt;/span&gt;, &lt;span class="string"&gt;"la la"&lt;/span&gt;, &lt;span class="variable-name"&gt;SomeAuthorId&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;function "new" คือ function ที่ erlang generate ให้เมื่อเรา define module ให้เป็น parameterized โดย parameters ที่เราผ่านค่าเข้าไปจะถูกนำไป initialize ค่าตัวแปรที่เราประกาศไว้ และค่าที่ return กลับมาจาก function new ก็คือ instance ของ module นั้นๆ &lt;br /&gt;&lt;br /&gt;หลังจาก new module instance แล้ว เราก็สามารถเรียกใช้ function ใน module instance นั้นๆ โดยใช้ syntax แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="variable-name"&gt;BlogPost&lt;/span&gt;:&lt;span class="type"&gt;save&lt;/span&gt;()&lt;/pre&gt;&lt;br /&gt;จะเห็นว่าเราเรียก function จาก instance ของ Module ซึ่งภายใน save function ถ้ามีการ access ตัวแปร Title, ตัวแปร Title ก็จะเป็นตัวแปรที่อยู่ใน scope ของ instance BlogPost (มันคือ OOP นั่นเอง)&lt;br /&gt;&lt;br /&gt;ถ้าใครอยากรู้ว่า Parameterized Module เขาออกแบบมาเพื่อช่วยแก้ปัญหาอะไร ขอแนะนำให้อ่าน &lt;a href="http://ftp.sunet.se/pub/lang/erlang/workshop/2003/paper/p29-carlsson.pdf"&gt;paper นี้&lt;/a&gt; เฉพาะส่วน introduction ก็พอ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2331761629262945489?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2331761629262945489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2331761629262945489' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2331761629262945489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2331761629262945489'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/parameterized-module.html' title='Parameterized Module'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5339920054274008011</id><published>2010-01-01T12:21:00.002+07:00</published><updated>2010-01-01T12:30:38.461+07:00</updated><title type='text'>Conversations with Architects</title><content type='html'>หลังจากมีลูกครบสามแล้ว นานๆทีถึงจะมีเวลาเข้าร้านหนังสือ เมื่อวานได้โอกาสดีแวบเข้าไปฉกหนังสือน่าอ่านมาหนึ่งเล่ม ชื่อว่า &lt;a href="http://www.se-ed.com/eshop/Products/Detail.aspx?No=9789740503026"&gt;"คุยกับต้นกล้าสถาปนิก - Conversations with Architects"&lt;/a&gt; เป็นหนังสือรวมสัมภาษณ์เหล่าสถาปนิกรุ่นใหม่ (เนื่องจากคนสัมภาษณ์อายุราว 60 แล้ว, นิยามของรุ่นใหม่ก็เลยอายุ ~40 up ทุกคนเลย) &lt;br /&gt;&lt;br /&gt;สิ่งที่ขอบสำหรับหนังสือเล่มนี้ก็คือ ผู้สัมภาษณ์ก็คือคุณ&lt;a href="http://th.wikipedia.org/wiki/นิธิ_สถาปิตานนท์"&gt;นิธิ สถาปิตานนท์&lt;/a&gt;  ซึ่งประสบความสำเร็จในวิชาชีพจนได้รับการยกย่องเป็นศิลปินแห่งชาติ สาขาศิลปะสถาปัตยกรรม เมื่อผู้สัมภาษณ์เก่งและมีประสบการณ์ขนาดนี้ บทและประเด็นสัมภาษณ์ก็เลยชวนอ่านยิ่งนัก&lt;br /&gt;&lt;br /&gt;อ่านวิชาชีพคนอื่นแล้วก็ต้องหันมามองตัวเอง เมื่อเทียบกับวิชาชีพอื่นๆแล้ว วิชาชีพ IT ยังเป็นอะไรที่ใหม่มากอยู่ สิ่งที่ผมอยากเห็นก็คือการรวมตัวกันเป็นชุมชน (Community of Practice) เพื่อที่จะแลกเปลี่ยน เรียนรู้ร่วมกัน, โดยการเกาะกลุ่มนั้นเกิดจาก passion ในการเรียนรู้ มากกว่าการเล็งผลประโยชน์ทางธุรกิจ&lt;br /&gt;&lt;br /&gt;เมื่อพูดถึง Comminuty of Practice แล้ว ก็เลยขอประชาสัมพันธ์ วันเสาร์ที่ 9 มกราคม 2553 นี้ &lt;a href="http://twitter.com/roofimon"&gt;@roofimon&lt;/a&gt; กับผม และเพื่อนๆ จะ&lt;a href="http://www.narisa.com/forums/index.php?showtopic=30218&amp;hl="&gt;จับเข่าคุยกันเรื่อง Functional Programming&lt;/a&gt; โดยสถานที่ได้รับความเอื้อเฟ้ือจาก &lt;a href="http://opendream.co.th/contact"&gt;Opendream&lt;/a&gt; ใครที่สนใจไม่เป็นทางการ เนื้อหาที่ได้จะน่าสนใจยิ่งนั้น  คุณสมบัติขั้นต่ำสำหรับผู้เข้าร่วมก็คือ ผู้เข้าร่วมต้องไม่อายที่จะถามคำถามที่ตัวเองสงสัย และไม่กลัวว่าคำถามนั้นจะเป็นคำถามที่แสดงความไม่รู้ของตัวเอง ( :) โลกนี้มีใครบ้างที่รู้อะไรจริงๆ)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5339920054274008011?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5339920054274008011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5339920054274008011' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5339920054274008011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5339920054274008011'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2010/01/conversations-with-architects.html' title='Conversations with Architects'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1145526324130597306</id><published>2009-09-22T12:47:00.001+07:00</published><updated>2009-09-22T12:49:07.024+07:00</updated><title type='text'></title><content type='html'>เมื่อวานเศร้ามาก อุตส่าห์นั่งปั่น code เพื่อเตรียม release แต่กลับพบว่า มันเปิดบน IE ไม่ได้ ช่วงหลังๆประมาทเพราะมีแต่ minor changed ทั้งนั้น เลยไม่ได้ทดสอบบน IE&lt;br /&gt;แต่ไม่เป็นไร git มีเครื่องมือที่เรียกว่า 'bisect' สำหรับหาว่า commit ไหนเป็นตัวปัญหา &lt;br /&gt;&lt;br /&gt;การใช้งานเริ่มด้วยคำสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git bisect start&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จากนั้นก็ mark ว่า revision ที่กำลังทำงานอยู่มันไม่ดี&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git bisect bad&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;บอกมันด้วยว่า revision ไหนที่มันยังดีอยู่&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git bisect good &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;git ก็จะจัดการ checkout revision ที่อยู่ตรงกลางระหว่าง bad กับ good ให้&lt;br /&gt;เราก็แค่บอกมันว่า revision ปัจจุบันที่ใช้งานอยู่มัน bad หรือ มัน good&lt;br /&gt;กรณีของผม ผมทดสอบแล้วยัง error อยู่ก็เลยสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git bisect bad&lt;br /&gt;Bisecting: 10 revisions left to test after this&lt;br /&gt;[2c6383f3664f50b2121ba5d455d595a60c3aa85c] prototype for flex chart component.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ทำไปเรื่อยๆ จนกว่าจะจับ commit ที่มีปัญหาได้ ซึ่งมันจะฟ้องว่า&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git bisect good&lt;br /&gt;4bc966d79eb3cd7ec10672863b6c97b26f401ea7 is first bad commit&lt;br /&gt;commit 4bc966d79eb3cd7ec10672863b6c97b26f401ea7&lt;br /&gt;Author: pphetra &lt;pphetra@861f4280-ad90-497b-9685-c03d5c67e785&gt;&lt;br /&gt;Date:   Thu Aug 6 05:58:20 2009 +0000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;หลังจากได้ revision ที่เป็นปัญหา ก็เป็นหน้าที่เราแล้วที่ต้องไปไล่ดู diff file ว่าอะไรที่ทำให้มันพัง&lt;br /&gt;&lt;br /&gt;ก่อนจะออกไปก็ให้ clear สถานะ git bisect ด้วยคำสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git bisect reset&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note: กรณีที่มี Makefile ให้ run จาก command line, เราสามารถใช้คำสั่ง &lt;code&gt;git bisect run&lt;/code&gt; ได้เลย&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1145526324130597306?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1145526324130597306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1145526324130597306' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1145526324130597306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1145526324130597306'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/09/code-release-ie-minor-changed-ie-git.html' title=''/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-8475181588085921008</id><published>2009-07-20T10:55:00.005+07:00</published><updated>2009-07-20T11:50:39.664+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='parser'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>ทำ DSL ด้วย parser combinator</title><content type='html'>ช่วงนี้ทำระบบ billing ด้วย Grails อยู่ มันมีโจทย์ว่า user ต้องสามารถกำหนด policy การคิดเงินค่าโทรศัทพ์ได้เอง โดยรูปแบบของการคิดเงินถ้าเขียนออกมาเป็น text ก็ประมาณนี้&lt;br /&gt;&lt;blockquote&gt;ถ้าเวลาที่โทรอยู่ในช่วงหนึ่งนาทีแรก ให้คิดเงินเต็มหนึ่งนาที ส่วนเวลาที่เกินให้ปัดที่หน่วยละ  6 วินาที โดยคิดหน่วยละ 10 % ของราคาต่อนาที&lt;/blockquote&gt;&lt;br /&gt;version แรกสุดที่ผมทำ หน้าตาออกมาประมาณนี้&lt;br /&gt;&lt;blockquote&gt;[minimum: 60, nextCharge: 6, rateFunc: { x -&gt; x * rate * 0.1}]&lt;/blockquote&gt;&lt;br /&gt;ไม่ต้องบอกก็รู้ตัวว่า ไม่มี user ที่ไหน config ได้แน่ มี closure หน้าตาประหลาดโผล่มาแบบนี้ (จริงๆแล้วมันคือ code ของ groovy ที่พร้อมจะ run นั่นเอง)&lt;br /&gt;&lt;br /&gt;version ที่สอง ผมก็เลยปรับให้มันกระเดียดไปทางภาษาคนมากขึ้น หน้าตาออกมาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;minimum 60 sec , nextcharge 6 sec : 10 %&lt;br /&gt;หรือจะใช้หน่วย minute ด้วยก็ได้&lt;br /&gt;minimum 1 min, nextcharge 6 sec : 10 %&lt;/pre&gt;&lt;br /&gt;เมื่อความต้องการเป็นแบบนี้ ก็ต้องหาวิธี implement, วิธีที่คุ้นเคยมากสุดก็คือใช้ Antlr เขียน Parser แต่บังเอิญเป็นคนขี้เบื่อ ก็เลยมองหาวิธีอื่นแทน ช่วงนี้ Scala กำลังมาแรง ประกอบกับเคยเห็นว่ามันทำ &lt;a href="http://en.wikipedia.org/wiki/Parser_combinator"&gt;Parser combinator&lt;/a&gt; ได้ด้วย ก็เลยหวยออกที่ Scala + Parser Combinator&lt;br /&gt;&lt;br /&gt;เริ่มแรกสุด parser ของเรา extends จาก StandardTokenParsers (มากับ Core ของ Scala อยู่แล้ว ไม่ใช่ external library)&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;RateParser&lt;/span&gt; &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;StandardTokenParsers&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;กำหนด delimiters และ reserved word&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;  lexical.delimiters ++= List(&lt;span class="string"&gt;":"&lt;/span&gt;, &lt;span class="string"&gt;"%"&lt;/span&gt;, &lt;span class="string"&gt;","&lt;/span&gt;)&lt;br /&gt;  lexical.reserved += (&lt;span class="string"&gt;"minimum"&lt;/span&gt;, &lt;span class="string"&gt;"nextcharge"&lt;/span&gt;, &lt;span class="string"&gt;"min"&lt;/span&gt;, &lt;span class="string"&gt;"minute"&lt;/span&gt;, &lt;span class="string"&gt;"sec"&lt;/span&gt;, &lt;span class="string"&gt;"second"&lt;/span&gt;, &lt;span class="string"&gt;"m"&lt;/span&gt;, &lt;span class="string"&gt;"s"&lt;/span&gt;)  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จากนั้นก็ define grammar&lt;br /&gt;ถ้าดู code จะเห็นว่าเรา define parser ย่อยๆเต็มไปหมด อันนี้แหล่ะคือความหมายของ combinator นั่นคือเราเขียน parser ใหญ่ๆ ด้วยการการเอา parser เล็กๆย่อยๆมาประกอบกันนั่นเอง&lt;br /&gt;&lt;pre class="hl"&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;rule&lt;/span&gt;: &lt;span class="type"&gt;Parser[RateStrategy]&lt;/span&gt; = minimum ~ nextcharge ~ percent ^^&lt;br /&gt;      { &lt;span class="keyword"&gt;case&lt;/span&gt; (&lt;span class="variable-name"&gt;min&lt;/span&gt; &lt;span class="variable-name"&gt;~&lt;/span&gt; &lt;span class="variable-name"&gt;next&lt;/span&gt;) &lt;span class="variable-name"&gt;~&lt;/span&gt; &lt;span class="variable-name"&gt;percent&lt;/span&gt; =&amp;gt; &lt;span class="keyword"&gt;new&lt;/span&gt; RateStrategy(min, next, percent)}&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;minimum&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = &lt;span class="string"&gt;"minimum"&lt;/span&gt; ~&amp;gt; unit &amp;lt;~ &lt;span class="string"&gt;","&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;nextcharge&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = &lt;span class="string"&gt;"nextcharge"&lt;/span&gt; ~&amp;gt; unit &amp;lt;~ &lt;span class="string"&gt;":"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;percent&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = numericLit &amp;lt;~ &lt;span class="string"&gt;"%"&lt;/span&gt; ^^ (_.toInt)&lt;br /&gt;  &lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;unit&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = minuteUnit | secondUnit&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;minuteUnit&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = numericLit &amp;lt;~ (&lt;span class="string"&gt;"min"&lt;/span&gt; | &lt;span class="string"&gt;"minute"&lt;/span&gt; | &lt;span class="string"&gt;"m"&lt;/span&gt;) ^^ (_.toInt * 60)&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;secondUnit&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = numericLit &amp;lt;~ (&lt;span class="string"&gt;"sec"&lt;/span&gt; | &lt;span class="string"&gt;"second"&lt;/span&gt; | &lt;span class="string"&gt;"s"&lt;/span&gt;) ^^ (_.toInt)&lt;/pre&gt;&lt;br /&gt;จะเห็นว่ามี operator หน้าตาแปลกๆ เต็มไปหมด ไม่ต้องตกใจ มาลองดูแบบง่ายสุดก่อน เริ่มที่การ parse หน่วยเวลากันก่อน&lt;br /&gt;จากโจทย์ของเขา จะเห็นว่าเราจะทำการ parse พวก "1 min", "60 sec"&lt;br /&gt;กรณี minute จะเห็นว่า code หน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;minuteUnit&lt;/span&gt;: &lt;span class="type"&gt;Parser[Int]&lt;/span&gt; = numericLit &amp;lt;~ (&lt;span class="string"&gt;"min"&lt;/span&gt; | &lt;span class="string"&gt;"minute"&lt;/span&gt; | &lt;span class="string"&gt;"m"&lt;/span&gt;) ^^ (_.toInt * 60)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="hl"&gt;def minuteUnit: Parser[Int]&lt;/pre&gt; ก็คือการ define method ที่ return parser ที่ return Integer (&lt;a href="http://en.wikipedia.org/wiki/Higher-order_function"&gt;Higher order function&lt;/a&gt;)&lt;br /&gt;&lt;pre class="hl"&gt;numericLit &lt;~ ("min" | "minute" | "m")&lt;/pre&gt; ก็คือ ระบุว่า ประโยคจะเริ่มต้นด้วย integer จากนั้นจะตามด้วย "min" หรือ "minute" หรือ "m"&lt;br /&gt;เครื่องหมาย "&lt;~" เป็น operator ที่ extend มาจาก operator "~" &lt;br /&gt;operator "~" มีความหมายว่า ถ้า parse argument ทางซ้ายสำเร็จ ก็ให้ทำ ทางขวาต่อ (chain)&lt;br /&gt;แต่ถ้าไม่สำเร็จ ก็ abort &lt;br /&gt;ส่วน "&lt;~" เป็นการเพิ่มความหมายว่า argument ท่ีอยู่ทางซ้ายถือเป็นตัวที่เราสนใจ ให้ ignore argument ที่อยู่ด้านขวาไปได้เลย&lt;br /&gt;Note: แน่นอน เมื่อมี "&lt;~" ก็เลยมี "~&gt;" ด้วย&lt;br /&gt;&lt;pre class="hl"&gt;^^ (_.toInt * 60)&lt;/pre&gt; เราเรียก transforms operator นั่นคือ ในกรณีนี้แทนที่จะ return String ตัวเลขนาทีไป เราจะเปลี่ยนให้มันเป็น integer ก่อน &lt;br /&gt;&lt;br /&gt;ความยุ่งยากอันถัดไปก็คือ ต้องให้มันเรียกใช้จาก Groovy ได้ (โปรเจคหลักเป็น Groovy) &lt;br /&gt;โชคดีที่ Class ที่ Scala compile ออกมามันหน้าตาดีมาก ไม่มีการแปลงชื่อหรือเปลี่ยนรูปมาก&lt;br /&gt;ทำให้เราสามารถเรียกใช้ได้ตรงๆ&lt;br /&gt;&lt;pre class="hl"&gt;groovy:000&amp;gt; f = RateStrategyParser.parse("minimum 1 min , nextcharge 6 sec : 10%")    &lt;br /&gt;===&amp;gt; RateStrategy@34a02677&lt;br /&gt;groovy:000&amp;gt; f&lt;br /&gt;===&amp;gt; RateStrategy@34a02677&lt;br /&gt;groovy:000&amp;gt; f.calc(24.00, 72)&lt;br /&gt;===&amp;gt; 28.80&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-8475181588085921008?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/8475181588085921008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=8475181588085921008' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8475181588085921008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8475181588085921008'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/07/dsl-parser-combinator.html' title='ทำ DSL ด้วย parser combinator'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5409380177714579724</id><published>2009-06-26T10:46:00.005+07:00</published><updated>2009-06-26T11:33:42.206+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='learning'/><title type='text'>Dreyfus model</title><content type='html'>เมื่อวานได้มีโอกาสนั่งดู presentation หัวข้อ "&lt;a href="http://www.infoq.com/presentations/Developing-Expertise-Dave-Thomas"&gt;Developing Expertise: Herding Racehorses, Racing Sheep&lt;/a&gt;" ของ Dave Thomas. ฟังแล้วประทับใจมากทั้งขำหัวเราะจนท้องแข็ง และปิ๊งกับเนื้อหาที่มีการใช้ metaphor เรื่องเด็กสองขวบทำให้ผม(ซึ่งมีลูกเล็กๆ 2 ขวบ) เห็นภาพชัดเจน&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Theme ใน presentation ของ Dave(ที่ไม่ใช่ program dave ในหนังสือ ruby on rails ที่มีคนแปลมั่วๆไว้) กล่าวอ้างถึง &lt;a href="http://en.wikipedia.org/wiki/Dreyfus_model_of_skill_acquisition"&gt;Dreyfus model of skill acquisition&lt;/a&gt; ซึ่ง Stuart และ Hubert Dreyfus เสนอไว้เมื่อปี 1980 ว่า ในการเรียนรู้ทักษะใดๆก็ตาม มันมี 5 level ที่เราต้องผ่าน&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Novice &lt;/li&gt;&lt;li&gt;Advance Beginner&lt;/li&gt;&lt;li&gt;Competent&lt;/li&gt;&lt;li&gt;Proficient&lt;/li&gt;&lt;li&gt;Expert&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;ที่น่าสนใจก็คือ Dave claim ว่าใน domain ใดๆก็ตาม พวกที่มีมากสุดก็คือ level 2 Advance Beginner, คำถามที่น่าสนใจก็คือ ทำไม? ทำไมคนส่วนใหญ่ถึงไปติดค้างอยู่ที่ระดับนั้น. บางคนก็เสนอขึ้นมาว่า เพราะเขาคิดไปเองน่ะสิว่า ระดับที่เขาอยู่นั้นเป็นระดับ 4. อันนี้ตรงกับกฎข้อที่ 1 ของ &lt;a href="http://en.wikipedia.org/wiki/Dunning-Kruger_effect"&gt;Dunning-Kruger effect&lt;/a&gt; ที่ว่า&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;Incompetent individuals tend to overestimate their own level of skill.&lt;/blockquote&gt;&lt;/div&gt;บางคนก็เสนอว่า ในการที่จะเลื่อนจากระดับ 2 ไประดับ 3 ได้นั้น เขาต้องการ mentor แต่จำนวนคนในระดับ 3,4,5 ไม่พอที่จะเป็น mentor ให้กับทุกคนในระดับ 2 (การเป็น mentor นั้นต้องอาศัยส่วนผสมหลายอย่างที่ลงตัว ไม่ใช่อยากจะเป็นก็เป็นได้)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Dave เขาถามผู้ฟังว่าอะไรคือความแตกต่างระหว่าง level 2 กับ level 3.  ความแตกต่างที่เห็นชัดสุดก็คือ "Dependent" คนที่อยู่ระดับที่ 3 สามารถตัดสินใจได้ด้วยตัวเอง ต่างกับระดับที่ 2 ที่ต้องการให้มีคนบอกว่า ต้องทำอะไรบ้าง. สิ่งที่ตามมากับ Dependent ก็คือ "Risk". ฟังถึงตรงนี้แล้วตรงใจมาก คนส่วนใหญ่ไม่พร้อมจะเสี่ยง ทุกคนอยากอยู่ใน safety zone หรือ &lt;a href="http://en.wikipedia.org/wiki/Comfort_zone"&gt;comfort zone&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;quote ที่ผมชอบสุดก็คือ ประโยคนี้&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;don't never ever let the expert choose your next architecture because they will choose the components that they are curious to see if it work.&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;ปล. 1 ในหนังสือ &lt;a href="http://www.pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;Pragmatic Thinking and Learning: Refactor Your Wetware&lt;/a&gt; มีบทที่ว่าด้วย Dreyfus Model อยู่บนหนึ่ง มีให้อ่าน free อยู่ครึ่งบทด้วย ใครสนใจไปตามอ่านได้ครับ &lt;a href="http://media.pragprog.com/titles/ahptl/chap2.pdf"&gt;Link&lt;/a&gt; (ผมอ่านแล้ว แล้วก็ลืมหมดแล้ว จนมาฟัง presentation นี้ก็เลยปิ๊งขึ้นมา)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;ปล 2. ขอแสดงความเสียใจกับ Roti และ Opengis ที่ Opendream อนุญาติให้ผมเลือก components ตามใจชอบ (ตอนนี้พยายามยัด erlang ลงไปใน architecture อยู่)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5409380177714579724?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5409380177714579724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5409380177714579724' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5409380177714579724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5409380177714579724'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/06/dreyfus-model.html' title='Dreyfus model'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7718708419752591612</id><published>2009-06-25T12:07:00.004+07:00</published><updated>2009-06-25T12:50:58.361+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>ไก่กับไข่ใน python OOP</title><content type='html'>วันนี้นั่งทำความเข้าใจกับ OOP ใน python&lt;br /&gt;เนื่องจากไปเห็นว่า BaseModel ของ django มัน extend type&lt;br /&gt;ก็เลยสงสัยว่าอะไรคือ  object อะไรคือ type&lt;br /&gt;&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;# In Python, the __class__ attribute points to the type of an object&lt;br /&gt;&lt;br /&gt;In [1]: object.__class__&lt;br /&gt;Out[1]: &amp;lt;type &lt;span class="string"&gt;'type'&lt;/span&gt;&amp;gt;&lt;br /&gt;#  object มี type เป็น type&lt;br /&gt;&lt;br /&gt;In [2]: type.__class__&lt;br /&gt;Out[2]: &amp;lt;type &lt;span class="string"&gt;'type'&lt;/span&gt;&amp;gt;&lt;br /&gt;# recursive structure นิ&lt;br /&gt;&lt;br /&gt;#__bases__ attribute points to a tuple containing supertypes of an object&lt;br /&gt;In [3]: object.__bases__&lt;br /&gt;Out[3]: ()&lt;br /&gt;&lt;br /&gt;In [4]: type.__bases__&lt;br /&gt;Out[4]: (&amp;lt;type &lt;span class="string"&gt;'object'&lt;/span&gt;&amp;gt;,)&lt;br /&gt;# เฮ้ยทำไม base ของ type เป็น object หล่ะ&lt;br /&gt;# เจอปัญหาไก่กับไข่แล้ว&lt;br /&gt;&lt;br /&gt;In [5]: isinstance(object, object)&lt;br /&gt;Out[5]: True&lt;br /&gt;&lt;br /&gt;In [6]: isinstance(type, object)&lt;br /&gt;Out[6]: True&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สรุปได้ว่า&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span"   style="  line-height: 22px; font-family:helvetica, verdana, arial, sans-serif;font-size:medium;"&gt;&lt;code class="literal"&gt;&amp;lt;type 'object'&amp;gt;&lt;/code&gt; เป็น instance ของ &lt;code class="literal"&gt;&amp;lt;type 'type'&amp;gt;&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-family:monospace, verdana, arial, sans-serif;"&gt;&lt;span class="Apple-style-span"  style=" line-height: 22px;font-size:medium;"&gt;&lt;span class="Apple-style-span"  style=" ;font-family:helvetica, verdana, arial, sans-serif;"&gt;&lt;code class="literal"&gt;&amp;lt;type 'object'&lt;/code&gt;&lt;span class="Apple-style-span"  style=" ;font-family:monospace, verdana, arial, sans-serif;"&gt;&amp;gt;&lt;/span&gt; เป็น subtype ของ no object.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-family:helvetica, verdana, arial, sans-serif;"&gt;&lt;span class="Apple-style-span"  style=" line-height: 22px;font-size:medium;"&gt;&lt;code class="literal"&gt;&amp;lt;type 'type'&lt;/code&gt;&lt;span class="Apple-style-span"  style=" ;font-family:monospace, verdana, arial, sans-serif;"&gt;&amp;gt;&lt;/span&gt; เป็น instance ของตัวเอง.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-family:helvetica, verdana, arial, sans-serif;"&gt;&lt;span class="Apple-style-span"  style=" line-height: 22px;font-size:medium;"&gt;&lt;code class="literal"&gt;&amp;lt;type 'type'&lt;/code&gt;&lt;span class="Apple-style-span"  style=" ;font-family:monospace, verdana, arial, sans-serif;"&gt;&amp;gt;&lt;/span&gt; เป็น subtype ของ &lt;code class="literal"&gt;&amp;lt;type 'object'&lt;/code&gt;&lt;span class="Apple-style-span"  style=" ;font-family:monospace, verdana, arial, sans-serif;"&gt;&amp;gt;&lt;/span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:helvetica, verdana, arial, sans-serif;"&gt;&lt;span class="Apple-style-span"  style=" line-height: 22px;font-size:medium;"&gt;&lt;span class="Apple-style-span"  style=" ;font-family:monospace, verdana, arial, sans-serif;"&gt;&amp;lt;&lt;/span&gt;type 'type'&lt;span class="Apple-style-span"  style=" ;font-family:monospace, verdana, arial, sans-serif;"&gt;&amp;gt;&lt;/span&gt; ก็คือ &lt;a href="http://en.wikipedia.org/wiki/Metaclass"&gt;Metaclass&lt;/a&gt; ที่ดันเป็น subtype ของ instance ของตัวเอง &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7718708419752591612?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7718708419752591612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7718708419752591612' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7718708419752591612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7718708419752591612'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/06/python-oop.html' title='ไก่กับไข่ใน python OOP'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7137612438181863812</id><published>2009-05-15T14:15:00.002+07:00</published><updated>2009-05-15T14:24:44.972+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>ย้าย commit ใน git</title><content type='html'>ปัญหาก็คือ branch ของผมมีหน้าตาเป็นแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;                  A deploy&lt;br /&gt;                 /     &lt;br /&gt;    D---E---F---G master&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;แต่ผมอยากย้าย commit G ไปอยู่ใน branch deploy&lt;br /&gt;ให้มีหน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;               G--A deploy&lt;br /&gt;             /     &lt;br /&gt;    D---E---F  master&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;หลังจากนั่งทดลองอยู่นาน ก็พบว่า คำตอบนั้นง่ายนิดเดียว&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git checkout master&lt;br /&gt;git reset --hard HEAD^&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7137612438181863812?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7137612438181863812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7137612438181863812' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7137612438181863812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7137612438181863812'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/05/commit-git.html' title='ย้าย commit ใน git'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2345731098024470987</id><published>2009-04-21T14:51:00.003+07:00</published><updated>2009-04-21T15:18:58.157+07:00</updated><title type='text'>pre-amp</title><content type='html'>หลวมตัวซื้อ mac มาใช้ ก็พบว่ามีหลายอย่างที่มันไม่ยอมทำให้เหมือนคนอื่น&lt;br /&gt;เช่น mic input ของมันก็รับ input ที่ระดับ line-level แทนที่จะเป็น microphone-level&lt;br /&gt;ซึ่งความแตกต่างของมันก็คือ ระดับของสัญญาณ&lt;br /&gt;line-level มี voltage peek-to-peek ที่ระดับ millivolt&lt;br /&gt;ส่วน mic-level มี voltage peek-to-peek ที่ระดับ volt&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;ครั้งจะไปหาซื้อ mic ของ apple มาใช้ ก็ดูเป็นการลงทุนที่ไม่คุ้มค่ายิ่งนัก&lt;/div&gt;&lt;div&gt;ต่อวงจรขยายเองน่าจะคุ้มกว่า&lt;/div&gt;&lt;div&gt;หลังจากเปิด google ไปสักพัก ก็พบวงจรนี้&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.epanorama.net/circuits/micamp.html"&gt;http://www.epanorama.net/circuits/micamp.html&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;เมื่อได้วงจรมา ก็ดองไว้อีกอาทิตย์กว่าๆ ก็มีอัศวินม้าขาวมาช่วยต่อวงจรให้&lt;/div&gt;&lt;div&gt;(อัศวินม้าขาวก็คือ "กอบ"-เพื่อนร่วมงานผม ผู้เชี่ยวชาญทั้งด้าน eclipse RCP + SWT และด้าน hardware)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;ผลลัพท์ที่ได้&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/Se1_82UkIzI/AAAAAAAAArI/G78Ov64jXMM/s1600-h/Picture+2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 235px;" src="http://3.bp.blogspot.com/_I3r3qYlfei4/Se1_82UkIzI/AAAAAAAAArI/G78Ov64jXMM/s320/Picture+2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5327054617599812402" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2345731098024470987?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2345731098024470987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2345731098024470987' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2345731098024470987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2345731098024470987'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/04/pre-amp.html' title='pre-amp'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_I3r3qYlfei4/Se1_82UkIzI/AAAAAAAAArI/G78Ov64jXMM/s72-c/Picture+2.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6968330799505812812</id><published>2009-04-06T06:55:00.004+07:00</published><updated>2009-04-06T08:15:17.375+07:00</updated><title type='text'>สรุปหนังสือจากงานหนังสือ 52</title><content type='html'>พอแก่แล้วก็เริ่มสนใจประวัติศาสตร์ ชุดนี้น่าสนใจตรงขอบเขตที่พยายามจะมองภาพรวมของภูมิภาค&lt;br /&gt;&lt;ul&gt;&lt;li&gt;เอเซียตะวันออกเฉียงใต้ในยุคการค้า ค.ศ. 1450-1680 เล่ม 1 ดินแดนใต้ลม (แอนโทนี รีด)&lt;/li&gt;&lt;li&gt;เอเซียตะวันออกเฉียงใต้ในยุคการค้า ค.ศ. 1450-1680 เล่ม 2 การขยายตัวและวิกฤติการณ์&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;เล่มนี้ของอ.เจตนา นาควัชระ  ผมสนใจหัวข้อ "สถาปัตยกรรมร่วมสมัยกับความคาดหวังของคนนอก"&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;เก่ากับใหม่ อะไรดี มนุษยศาสตร์ไทยในกระแสของความเปลี่ยนแปลง (เจตนา นาควัชระ)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;สองเล่มนี้ พูดถึงประเด็นที่ไกล้เคียงกัน เล่มแรกผมสนใจการทำงานของจิตในเรื่องการคิด,  ส่วนเล่มสองเขาพูดประเด็นที่บอกว่า ระหว่าง trigger กับ action มันมี freedom อยู่, เล่มนี้ใช้แนวเขียนแบบ Story &lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;ทลายกับดักความคิด - Conceptual Blockbusting : A Guide to Better Ideas&lt;/li&gt;&lt;li&gt;พลิกคำถามเปลี่ยนชีวิต - Change Your Questions Change Your Life&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;เล่มนี้เป็นวรรณกรรมสำหรับเด็ก เป็นเรื่องของเด็กชาวเล (Note: ดูสิว่าจะอ่านแล้วรู้สึกทะแม่งเหมือนเรื่อง กะทิ หรือเปล่า, เรื่องกะทิกับผมมีปัญหากันตรงที่ ผมรู้สึกว่ามัน Fake) เล่มนี้กะว่าจะอ่านให้ลูกฟังก่อนนอน&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;บีตั๊ก ดาวดวงนั้นระหว่างน้ำกับฟ้า&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;ส่วนการ์ตูนเล่มนี้ คนเขียนเป็นคนมาเลเซีย ผมเคยเห็นลายเส้นของเขามานานแล้ว พอมีคนแปล ก็เลยตัดสินใจหยิบโดยไม่ลังเล&lt;/div&gt;&lt;ul&gt;&lt;li&gt;เด็กน้อยจากหมู่บ้าน The Kampung Boy ของ Lat.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;ส่วนของสำนักพิมพ์มติชน คงไม่ต้องอธิบายมาก&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;เศรษฐศาสตร์แห่งชีวิต The Logic of Life&lt;/li&gt;&lt;li&gt;ประวัติศาสตร์โลกผ่านเกลือ Salt : A World History&lt;/li&gt;&lt;li&gt;ปฎิบัติการล่าไอคิว The Know-It-All&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;ของลูกชายได้มาสิบกว่าเล่ม เล่มที่ลูกชอบมากสุดคือเล่มนี้ เล่าตั้งแต่กำเนิดโลกยันมนุษย์ถ้ำ&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;โลกยุคก่อนประวัติศาสตร์ Prehistoric world &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6968330799505812812?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6968330799505812812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6968330799505812812' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6968330799505812812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6968330799505812812'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/04/52.html' title='สรุปหนังสือจากงานหนังสือ 52'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4319117009617313528</id><published>2009-03-19T12:47:00.001+07:00</published><updated>2009-03-19T12:50:27.722+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>excluding transitive dependency in maven</title><content type='html'>ใครที่เคยใช้ maven คงจะรู้ดีกว่า มันช่วยลดอาการปวดหัวในการจัดการกับ jar file ได้ระดับหนึ่ง เพราะมันช่วยเราจัดการพวก transitive dependency ให้เราได้ (แต่ไปปวดหัวเรื่อง setup มันแทน)&lt;br /&gt;&lt;br /&gt;แต่การที่มัน solve transitive dependency ให้ก็นำมาซึ่งปัญหาอันใหม่ นั่นก็คือ version conflict, สมมติว่า project เรามี dependency ถึง library Foo กับ library Bar. ทั้ง Foo และ Bar บังเอิญมี dependency ซึ่ง Library Z เหมือนๆกัน แต่ดันเป็นคนละ version. เมื่อเรา build project, เราก็จะมี library Z เข้ามาใน class path เราทั้งสอง version, ทำให้เกิด error พวก missing method หรือ missing class ได้&lt;br /&gt;(Note: ที่ผมเจอ ผมเจอปัญหานี้ตอน mvn eclipse:eclipse, แต่จากการทดสอบด้วยคำสั่ง mvn dependency:resolve ปรากฎว่า มันได้ผลลัพท์ไม่เหมือนกัน เจ้า dependency:resolve ดูเหมือนจะจัดการได้ถูกต้อง)&lt;br /&gt;&lt;br /&gt;วิธีแก้ไขก็คือ การระบุ exclusion ลงใน pom.xml ของ maven&lt;br /&gt;จากของเดิม&lt;br /&gt;&lt;pre class="hl"&gt;&amp;lt;&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;my.pphetra&amp;lt;/&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;foo&amp;lt;/&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;1.0&amp;lt;/&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;my.pphetra&amp;lt;/&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;bar&amp;lt;/&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;1.0&amp;lt;/&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;ไปเป็น&lt;br /&gt;&lt;pre class="hl"&gt;&amp;lt;&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;my.pphetra&amp;lt;/&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;foo&amp;lt;/&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;1.0&amp;lt;/&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;exclusions&lt;/span&gt;&amp;gt;&lt;br /&gt;   &amp;lt;&lt;span class="function-name"&gt;exclusion&lt;/span&gt;&amp;gt;&lt;br /&gt;     &amp;lt;&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;test&amp;lt;/&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;&lt;br /&gt;     &amp;lt;&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;Z&amp;lt;/&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;&lt;br /&gt;   &amp;lt;/&lt;span class="function-name"&gt;exclusion&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;/&lt;span class="function-name"&gt;exclusions&lt;/span&gt;&amp;gt;&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;my.pphetra&amp;lt;/&lt;span class="function-name"&gt;groupId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;bar&amp;lt;/&lt;span class="function-name"&gt;artifactId&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;1.0&amp;lt;/&lt;span class="function-name"&gt;version&lt;/span&gt;&amp;gt;&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;dependency&lt;/span&gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทางแก้ดูแล้วง่ายดาย แต่จริงๆแล้ว มันไปยุ่งตอนที่หาว่า library ตัวไหนที่เราควรจะไปใส่ excludes ให้มัน&lt;br /&gt;วิธีการก็คือ ตอนที่สั่ง mvn command (กรณีของผมก็คือ eclipse:eclipse) ให้ใส่ -X ลงไปด้วย&lt;br /&gt;จากนั้นก็นั่งไล่หาว่า เจ้าตัวไหนคือตัวปัญหา (คำเตือน วิธีนี้มีผลข้างเคียงก็คือ ทำให้เกิดอาการง่วงนอน)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4319117009617313528?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4319117009617313528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4319117009617313528' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4319117009617313528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4319117009617313528'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/03/excluding-transitive-dependency-in.html' title='excluding transitive dependency in maven'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1615949435990513046</id><published>2009-02-04T16:03:00.002+07:00</published><updated>2009-02-04T16:09:27.993+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>การใช้งาน git กับ host ที่ไม่ support git</title><content type='html'>วันก่อนเจอคำถามถึงว่า ต้องการทำ version control กับ web application project&lt;br /&gt;โดยทำกับ directory ที่ live อยู่บน host ที่ไม่ support git จะมีวิธีไหนทำได้บ้าง?&lt;br /&gt;&lt;br /&gt;วิธีแรกก็คือใช้ sshfs ในการ mount remote file system แล้วก็สร้าง git repository บน live directory นั้นเลย&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;sshfs pune.ingres.co.th:/srv/www/htdocs/test /mnt -o workaround=rename&lt;br /&gt;cd /mnt&lt;br /&gt;git init&lt;br /&gt;git add .&lt;br /&gt;git commit -m 'init project'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: &lt;code&gt;workaround=rename&lt;/code&gt; เป็นการหลีกเลี่ยง bug ของ git กรณีที่ทำงานกับ sshfs&lt;br /&gt;&lt;br /&gt;วิธีที่สองก็คือ หลีกเลี่ยงการเก็บ ตัว repository (directory .git) ไว้บน server โดยการ สร้าง repository ไว้บน local ของเรา&lt;br /&gt;จากนั้นก็ใช้ environment variable GIT_DIR กับ GIT_WORK_TREE ในการ config directory ที่ถูกต้อง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;sshfs pune.ingres.co.th:/srv/www/htdocs/test /mnt&lt;br /&gt;cd /mnt&lt;br /&gt;export GIT_DIR=/mylocalgit&lt;br /&gt;export GIT_WORK_TREE=/mnt&lt;br /&gt;git init&lt;br /&gt;git add .&lt;br /&gt;git commit -m 'init project'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;วิธีที่สามก็คือ แยก repository และ working tree ที่เราทำงาน ออกจาก live directory &lt;br /&gt;แล้วใช้ technic reset --hard ในการ sync ข้อมูลแทน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;#สร้าง git repository&lt;br /&gt;&lt;br /&gt;mkdir myproject&lt;br /&gt;cd myproject&lt;br /&gt;git init&lt;br /&gt;...&lt;br /&gt;git commit -m 'my project'&lt;br /&gt;&lt;br /&gt;#เมื่อต้องการ sync project กับ remote file บน host&lt;br /&gt;&lt;br /&gt;sshfs pune.ingres.co.th:/srv/www/htdocs/test /mnt&lt;br /&gt;cd /mnt&lt;br /&gt;GIT_DIR=/myproject/.git git --work-tree=. reset --hard&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;วิธีที่ 1-3 workflow ไม่ค่อยเป็นทางการเท่าไร ถ้าต้องการ workflow ดูดีๆหน่อย ก็ต้องประมาณนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;                    auto   &amp;lt;&amp;lt;sshfs mount dir&amp;gt;&amp;gt;&lt;br /&gt;    +-------------+ update +==============+&lt;br /&gt;    ! bare repo.  !-------&gt;! working tree !&lt;br /&gt;    +-------------+        +--------------+&lt;br /&gt;         ! ^&lt;br /&gt;    pull ! ! pull&lt;br /&gt;         v !&lt;br /&gt;   +--------------+&lt;br /&gt;   !  Developer   !&lt;br /&gt;   ! working tree !&lt;br /&gt;   +--------------+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เริ่มด้วยการสร้าง bare repository สำหรับเก็บข้อมูลที่จะ publish ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;GIT_DIR=mygit git init&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;สร้าง hook script ที่ชื่อ post-receive &lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;#&lt;/span&gt;&lt;span class="comment"&gt;!/bin/&lt;/span&gt;&lt;span class="keyword"&gt;sh &lt;/span&gt;&lt;span class="comment"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="variable-name"&gt;WEBROOT_DIR&lt;/span&gt;=/srv/www/htdocs/test &lt;br /&gt;&lt;span class="variable-name"&gt;GIT_WORK_TREE&lt;/span&gt;=${&lt;span class="variable-name"&gt;GIT_WORK_TREE&lt;/span&gt;-$&lt;span class="variable-name"&gt;WEBROOT_DIR&lt;/span&gt;} &lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; [ &lt;span class="string"&gt;"$GIT_DIR"&lt;/span&gt; = &lt;span class="string"&gt;"."&lt;/span&gt; ]; &lt;span class="keyword"&gt;then&lt;/span&gt; &lt;br /&gt;    &lt;span class="variable-name"&gt;GIT_DIR&lt;/span&gt;=&lt;span class="sh-quoted-exec"&gt;`pwd`&lt;/span&gt; &lt;br /&gt;&lt;span class="keyword"&gt;fi&lt;/span&gt; &lt;br /&gt;&lt;span class="keyword"&gt;while &lt;/span&gt;&lt;span class="builtin"&gt;read&lt;/span&gt; oldrev newrev ref ; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;br /&gt;    if [ &lt;span class="string"&gt;"$ref"&lt;/span&gt; = &lt;span class="string"&gt;"refs/heads/master"&lt;/span&gt; ]; &lt;span class="keyword"&gt;then&lt;/span&gt; &lt;br /&gt;        echo &lt;span class="string"&gt;"Updating $GIT_WORK_TREE"&lt;/span&gt; &lt;br /&gt;        echo &lt;span class="string"&gt;"Using $ref, now at $newrev"&lt;/span&gt; &lt;br /&gt;        if [ &lt;span class="negation-char"&gt;!&lt;/span&gt; -d &lt;span class="string"&gt;"$GIT_WORK_TREE"&lt;/span&gt; ]; &lt;span class="keyword"&gt;then&lt;/span&gt; &lt;br /&gt;           mkdir &lt;span class="string"&gt;"$GIT_WORK_TREE"&lt;/span&gt; &lt;br /&gt;        fi &lt;br /&gt;        cd $&lt;span class="variable-name"&gt;GIT_WORK_TREE&lt;/span&gt; &lt;br /&gt;        git --work-tree=$&lt;span class="variable-name"&gt;GIT_WORK_TREE&lt;/span&gt; reset --hard $&lt;span class="variable-name"&gt;ref&lt;/span&gt; &lt;br /&gt;    fi &lt;br /&gt;&lt;span class="keyword"&gt;done&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Note: ตัว script ลอกมาจาก presentation ของ Jon Loeliger&lt;br /&gt;&lt;br /&gt;จากนั้นในฝั่ง working copy ของเรา ก็ระบุ remote branch ไว้ด้วย&lt;br /&gt;เมื่อไรก็ตามที่ต้องการ publish content ก็ให้ push ไปที่ remote branch นั้น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;mkdir myproject&lt;br /&gt;cd myproject&lt;br /&gt;git init&lt;br /&gt;git add .&lt;br /&gt;git commit -m 'bla bla'&lt;br /&gt;&lt;br /&gt;# add bare repo to remote list&lt;br /&gt;git remote add origin /path/to/bare/repo&lt;br /&gt;&lt;br /&gt;# เมื่อเราสั่ง push&lt;br /&gt;# directory ที่กำหนดไว้ใน WEBROOT_DIR ก็จะ update ให้โดยอัตโนมัติ&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1615949435990513046?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1615949435990513046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1615949435990513046' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1615949435990513046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1615949435990513046'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/02/git-host-support-git.html' title='การใช้งาน git กับ host ที่ไม่ support git'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3328351416563033229</id><published>2009-01-22T17:42:00.001+07:00</published><updated>2009-01-22T17:44:13.445+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>cluster by</title><content type='html'>Tom  MoerTel เขียน function มหัศจรรย์ไว้ใน post &lt;a href="http://blog.moertel.com/articles/2007/09/01/clusterby-a-handy-little-function-for-the-toolbox"&gt;ClusterBy: a handy little function for the toolbox&lt;/a&gt;&lt;br /&gt;function มีหน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="type"&gt;Control.Arrow&lt;/span&gt; ((&lt;span class="variable-name"&gt;&amp;amp;&amp;amp;&amp;amp;&lt;/span&gt;))&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="keyword"&gt;qualified&lt;/span&gt; &lt;span class="type"&gt;Data.Map&lt;/span&gt; &lt;span class="keyword"&gt;as&lt;/span&gt; &lt;span class="type"&gt;M&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="function-name"&gt;clusterBy&lt;/span&gt; &lt;span class="variable-name"&gt;::&lt;/span&gt; &lt;span class="type"&gt;Ord&lt;/span&gt; b &lt;span class="variable-name"&gt;=&amp;gt;&lt;/span&gt; (a &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; b) &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; [a] &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; [[a]]&lt;br /&gt;&lt;span class="function-name"&gt;clusterBy&lt;/span&gt; f &lt;span class="variable-name"&gt;=&lt;/span&gt; &lt;span class="type"&gt;M&lt;/span&gt;&lt;span class="variable-name"&gt;.&lt;/span&gt;elems &lt;span class="variable-name"&gt;.&lt;/span&gt; &lt;span class="type"&gt;M&lt;/span&gt;&lt;span class="variable-name"&gt;.&lt;/span&gt;map reverse &lt;span class="variable-name"&gt;.&lt;/span&gt; &lt;span class="type"&gt;M&lt;/span&gt;&lt;span class="variable-name"&gt;.&lt;/span&gt;fromListWith (&lt;span class="variable-name"&gt;++&lt;/span&gt;)&lt;br /&gt;            &lt;span class="variable-name"&gt;.&lt;/span&gt; map (f &lt;span class="variable-name"&gt;&amp;amp;&amp;amp;&amp;amp;&lt;/span&gt; return)&lt;/pre&gt;&lt;br /&gt;แล้วก็ทำแบบนี้ได้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;let antwords = words "the tan ant gets some fat"&lt;/span&gt;&lt;br /&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;clusterBy length antwords&lt;/span&gt;&lt;br /&gt;[["the","tan","ant","fat"],["gets","some"]]&lt;br /&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;clusterBy head antwords&lt;/span&gt;&lt;br /&gt;[["ant"],["fat"],["gets"],["some"],["the","tan"]]&lt;br /&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;clusterBy last antwords&lt;/span&gt;&lt;br /&gt;[["the","some"],["tan"],["gets"],["ant","fat"]]&lt;/pre&gt;&lt;br /&gt;ตัว function กระทัดรัดมาก แต่อ่านแล้วไม่เข้าใจเลย ก็เลยต้องออกแรงนั่งแกะหน่อย&lt;br /&gt;เริ่มแรกสุด มันจะทำแบบนี้ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;map&lt;/span&gt; (f &lt;span class="variable-name"&gt;&amp;amp;&amp;amp;&amp;amp;&lt;/span&gt; return) [&lt;span class="string"&gt;"the"&lt;/span&gt;, &lt;span class="string"&gt;"tan"&lt;/span&gt;, &lt;span class="string"&gt;"ant"&lt;/span&gt;, &lt;span class="string"&gt;"gets"&lt;/span&gt;, &lt;span class="string"&gt;"some"&lt;/span&gt;, &lt;span class="string"&gt;"fat"&lt;/span&gt;]&lt;/pre&gt;&lt;br /&gt;เจ้า &lt;code&gt;&amp;&amp;&amp;&lt;/code&gt; มีชื่อเรียกว่า fanout ลองดูการทำงานมัน (เพราะอธิบายยากมาก) &lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;(head &amp;amp;&amp;amp;&amp;amp; last) "abc"&lt;/span&gt;&lt;br /&gt;('a','c')&lt;br /&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;(head &amp;amp;&amp;amp;&amp;amp; id) "abc"&lt;/span&gt;&lt;br /&gt;('a',"abc")&lt;/pre&gt;&lt;br /&gt;จะเห็นว่ามัน apply function แรกกับ function ที่ 2 เข้ากับ parameter ผลลัพท์ที่ได้ก็จับมาเข้าคู่เป็น tuple ไว้ (หรือจะเรียกว่า pair ก็ได้ เพราะมีแค่ 2 elements)&lt;br /&gt;Note: ถ้าใครยังงงๆกับ fanout ลองดู diagram ที่ Daniel Lyons เขียนไว้ใน &lt;a href="http://storytotell.org/articles/2007/04/08/haskell-arrows"&gt;Haskell Arrows&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ที่นี้มาลองดู return บ้าง การที่เราสั่ง &lt;code&gt;return "abc"&lt;/code&gt; ผลลัพท์ของมันก็คือ Monad "abc"&lt;br /&gt;(ถ้ายังไม่รู้จัก monad ก็ให้สมมติไปก่อนว่า monad ก็คือ container structure แบบหนึ่ง)&lt;br /&gt;ดังนั้นผลลัพท์ของ &lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;map (head &amp;&amp;&amp; return) ["the", "tan", "ant", "gets", "some", "fat"]&lt;br /&gt;ก็จะได้&lt;br /&gt;[("t", Monad "the"), ("t", Monad "tan"), ...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;การทำงานลำดับถัดมาก็คือ &lt;code&gt;M.fromListWith (++)&lt;/code&gt; หรือถ้าเขียนเต็มๆก็คือ &lt;code&gt;Data.Map.fromListWith (++)&lt;/code&gt;&lt;br /&gt;เจ้า fromListWith เป็น function ที่ไว้ใช้สร้าง Dictionary(Map) structure&lt;br /&gt;โดยมันจะรับ array ของ pair (ในทีนี้ก็คือ [("t", M "the"), ("t", M "tan"), ...]) แล้วสร้างเป็น Map structure โดยใช้ element แรกของ pair เป็น key ส่วน element ที่สองก็เป็น value ไป&lt;br /&gt;แต่เจ้า fromListWith มีความพิเศษตรงที่ว่า กรณีที่มันพบว่า first element นั้นซ้ำกับที่เคยมีอยู่แล้ว&lt;br /&gt;มันก็จะ apply function ที่เราส่งเข้าไป (ในที่นี้ก็คือ (++)) ให้กับ value ทั้งสอง&lt;br /&gt;ลองดูตัวอย่าง&lt;br /&gt;&lt;code&gt;fromListWith (++) [("a", "ant"), ("b", "bat"), ("a", "axe")]&lt;/code&gt;&lt;br /&gt;จะได้ผลลัพท์เป็น&lt;br /&gt;Map ที่มี 2 entry&lt;br /&gt;entry แรกมี key เป็น "a" และ value เป็น "axeant"&lt;br /&gt;ส่วน entry ที่สอง มี key เป็น "b" และ value เป็น "bat"&lt;br /&gt;&lt;br /&gt;กรณีของเรา element ที่สองมันมี type เป็น Monad&lt;br /&gt;เจ้า function (++) เมื่อเจอกับ Monad มันจะมีพฤติกรรมเป็นแบบนี้แทน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;[1] ++ [2]&lt;/span&gt;&lt;br /&gt;[1,2]&lt;br /&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;(return 1) ++ (return 2)&lt;/span&gt;&lt;br /&gt;[1,2]&lt;br /&gt;&lt;span class="comint-highlight-prompt"&gt;*Main&amp;gt; &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;(return "ant") ++ (return "axe")&lt;/span&gt;&lt;br /&gt;["ant","axe"]&lt;/pre&gt;&lt;br /&gt;Note: ดูเหมือนว่าใน haskell่, Array กับ Monad มีความสัมพันธ์แน่นแฟ้น&lt;br /&gt;&lt;br /&gt;คำสั่งลำดับถัดไปก็คือ &lt;code&gt;M.map reverse&lt;/code&gt;&lt;br /&gt;อันนี้ไม่มีอะไรแล้ว แค่ reverse ค่า value ทุกตัวใน Map&lt;br /&gt;&lt;br /&gt;สุดท้ายก็คือ &lt;code&gt;elems&lt;/code&gt; ก็คือ กรองเอาแต่ค่า value ใน Map&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3328351416563033229?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3328351416563033229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3328351416563033229' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3328351416563033229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3328351416563033229'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/01/cluster-by.html' title='cluster by'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5830085101865163849</id><published>2009-01-05T15:52:00.002+07:00</published><updated>2009-01-05T15:56:18.927+07:00</updated><title type='text'>เวลาหายไปไหน</title><content type='html'>ยิ่งแก่ก็ยิ่งฟุ้งซ่านอยากทำนู่นอยากทำนี่เต็มไปหมด แต่เวลาก็มีนิดเดียว&lt;br /&gt;ปีใหม่นี้ก็เลยหาเครื่องมือมาช่วยจัดการเวลา ได้ solution ลงตัวที่ &lt;a href="http://klok.mcgraphix.com/"&gt;klok&lt;/a&gt;&lt;br /&gt;กับ &lt;a href="http://chandlerproject.org/"&gt;chandler&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;เจ้า chandler นี่เอามาใช้จัดการ GTD ส่วน klok นี่เอามาช่วยคุม flow(ทำงานให้ต่อเนื่องไม่ switch ไปมา) กับ balance เวลาที่จัดสรรให้กับแต่ละ project&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_I3r3qYlfei4/SWHKg3J8QgI/AAAAAAAAAmk/660Cl-Y3akY/s1600-h/Picture+3.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 270px;" src="http://3.bp.blogspot.com/_I3r3qYlfei4/SWHKg3J8QgI/AAAAAAAAAmk/660Cl-Y3akY/s320/Picture+3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5287730103419945474" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5830085101865163849?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5830085101865163849/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5830085101865163849' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5830085101865163849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5830085101865163849'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/01/blog-post_05.html' title='เวลาหายไปไหน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_I3r3qYlfei4/SWHKg3J8QgI/AAAAAAAAAmk/660Cl-Y3akY/s72-c/Picture+3.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-101644947974873252</id><published>2009-01-04T21:16:00.004+07:00</published><updated>2009-01-05T10:26:01.392+07:00</updated><title type='text'>ปากกาแท่งใหม่</title><content type='html'>ปีใหม่นี้ผมให้ของขวัญตัวเองเป็นปากกาหมึกซึมยี่ห้อ &lt;a href="http://www.lamy.com/eng/b2c/safari/018"&gt;LAMY รุ่น safari&lt;/a&gt; ราคา 700 กว่าบาท, ซึ่งแท่งเก่าที่มีก็ใช้ยี่ห้อนี้อยู่แล้ว ใช้มาเกือบ 10 ปี หัวปากกามันหลวมใช้ไม่ค่อยดีเสียแล้ว &lt;br /&gt;&lt;br /&gt;requirement ในการเลือกปากกาของผมก็คือ ผมใช้วาดรูปหรือ sketch เป็นหลัก เรื่องเขียนตัวหนังสือเป็นเรื่องเป็นราวนี่น้อยมาก ก็เลยเลือกขนาดของหัวเป็น medium (M)&lt;br /&gt;&lt;br /&gt;สมัยที่เข้าทำงานใหม่ๆ เจ้านายและอาจารย์ผม(สองบทบาทในร่างเดียว) ก็พร่ำสอนว่า ต้องมีสมุดจดและต้องรู้จักจด ซึ่งผมก็ทำตาม แต่ไม่ร้อยเปอร์เซนต์เพราะสันดานออกจะเป็นคนขี้เกียจ แต่ก็จดมาได้เกือบสิบปี พอมาถึงยุค notebook ครองโลก ด้วยความที่มันพกสะดวกก็เลยทอดทิ้งสมุดโน้ตไปเสีย (จริงๆไม่เกี่ยวกันหรอก แค่อ้างให้ดูมีเหตุผล) &lt;br /&gt;&lt;br /&gt;เมื่อสัปดาห์ก่อนผมได้อ่านหนังสือ &lt;a href="http://www.pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;Pragmatic Thinking and Learning&lt;/a&gt; ซึ่งเขาก็ชักจูงให้เห็นความสำคัญของการจดบันทึก ก็เลยเกิดอาการกลับตัวกลับใจ รื้อฟื้นสมุดโน้ตขึ้นมาใช้อีกครั้ง ส่งผลให้มีสาเหตุที่ต้องซื้อปากกาแท่งใหม่ ด้วยความเชื่อที่ว่า การมีปากกาที่ดีจะทำให้เราสนุกกับการเขียน (เหตุผลเดียวกับการใช้ mac)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-101644947974873252?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/101644947974873252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=101644947974873252' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/101644947974873252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/101644947974873252'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2009/01/blog-post.html' title='ปากกาแท่งใหม่'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5493195582632437002</id><published>2008-12-23T11:34:00.002+07:00</published><updated>2008-12-23T11:38:59.320+07:00</updated><title type='text'>Eureka</title><content type='html'>เมื่อคืนก่อนตอนเที่ยงคืน ผมสะดุ้งตื่นเพราะได้ยินเสียงสัญญาณกันขโมย ก็เลยลุกงัวเงียเดินไปแง้มหน้าต่างเพื่อดูว่ารถตูหรือเปล่าวะ, อ้าวฉิบหายแล้ว รถเรานี่เอง ตอนนั้นสิ่งที่นึกในหัวก็คือ "เอาอีกแล้วไอ้เจ้าสัญญาณกันขโมยเฮงซวย" ที่คิดอย่างนั้นก็เพราะช่วงหลังๆ สัญญาณกันขโมยผมมันชอบร้องโวยวายโดยไม่มีสาเหตุ ร้องจนผมเลิกล็อครถตอนจอดอยู่ในบ้านแล้ว&lt;br /&gt;&lt;br /&gt;หลังจากรู้ว่ารถตัวเองแน่นอน ก็เลยรีบวิ่งลงไปข้างล่างเพื่อไปหากุญแจมาปิดสัญญาณ(ไม่อยากให้เพื่อนบ้านหนวกหู) แต่ค้นเท่าไรก็ไม่เจอ อารมณ์โมโหเริ่มครอบงำ นึกกล่าวโทษภรรเมียว่าเก็บกุญแจไม่เป็นที่เป็นทาง &lt;br /&gt;&lt;br /&gt;ตอนที่เดินหากุญแจในอยู่ ก็พบว่า อ้าวตู้เนี่ยทำไมเปิดอยู่ จัดการปิดซะเดินไปหาถึงประตูหลัง อ้าวประตูเปิดอยู่ นึกในใจ เด็กทำงานบ้านเราทำไมมันชุ่ยอย่างนี้ ไม่ยอมปิดประตูก่อนนอน ว่าแล้วก็ปิดประตูซะ ผ่านไปพักใหญ่สัญญาณก็หยุดทำงาน ก็เลยหยุดหาแล้วก็เปิดประตูหน้าบ้านออกไปดูว่าเกิดอะไรขึ้น พอหมุนลูกบิดก็รู้สึกว่า อ้าวประตูไม่ได้ล็อก ดูเสร็จไม่พบอะไรก็ล็อกซะ&lt;br /&gt;&lt;br /&gt;ตอนนั้นก็ไม่เอะใจอะไร เดินไปปลุกแฟน เอากุญแจไปซ่อนไว้ที่ไหนหามาให้เดี๋ยวนี้นะ&lt;br /&gt;แฟนก็บอกว่าไม่ได้เอาไป แล้วก็เดินลงมาช่วยหา ซึ่งก็หาไม่เจออีก แฟนก็เลยเดินขึ้นไปปลุกเด็กที่ทำงานบ้าน เด็กก็บอกว่าไม่ได้เอาไป&lt;br /&gt;เท่านั้นเอง ผมก็ตาสว่างร้อง Eureka!! ขโมยขึ้นบ้านกูนี่หว่า ว่าแล้วก็ลงไปสำรวจหลังบ้าน ก็ได้พบกุญแจรถยนต์สมใจ (ขโมยมันโยนทิ้งไว้)&lt;br /&gt;&lt;br /&gt;เรื่องที่อยากเล่าให้ฟังจริงๆก็คือ วิธีการทำงานของสมองเรา สิ่งที่เราคิดเราเชื่อจะเป็นตัวกำหนดกรอบความคิดของเรา เมื่อเกิด event หนึ่งๆขึ้น สมองเราก็จะใช้เส้นทางที่มันถนัดที่สุดในการตีความ event นั้นๆ&lt;br /&gt;ซึ่งการด่วนตัดสิน(ฟันธง, confirm) ก็จะทำให้เราพลาดการมองเห็นสิ่งที่เกิดขึ้นจริงๆ&lt;br /&gt;&lt;br /&gt;ยิ่งเราแก่ตัวลง ถ้าเรายึดมั่นในชุดความคิดใดความคิดหนึ่งมากๆ การคิดบ่อยๆเชื่อบ่อยๆ ก็จะยิ่งย้ำเส้นทางของ Neural network ในสมองเราให้เชื่อมกันแนบแน่นยิ่งขึ้น นี่เป็นสาเหตุที่เรามักพบว่า คนแก่มักจะหัวดื้อและไม่ยอมฟัง&lt;br /&gt;&lt;br /&gt;นึกเปรียบเทียบกับสภาพบ้านเมืองตอนนี้ ขโมยขึ้นบ้าน(ภาวะเศรษฐกิจโลก) แต่เรายังหมกมุ่นกับเรื่องกุญแจไม่เลิก (แทนคำว่ากุญแจกันเอาเองนะ แทนได้หลายคำเลย)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5493195582632437002?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5493195582632437002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5493195582632437002' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5493195582632437002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5493195582632437002'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/12/eureka.html' title='Eureka'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6287811533747115027</id><published>2008-12-08T22:34:00.002+07:00</published><updated>2008-12-08T22:45:03.374+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>random เรียกชื่อคน</title><content type='html'>เมื่อสุดสัปดาห์ที่ผ่านมาผมมีภาระกิจต้องไปสัมมนากับบริษัทฯ โดยในสัมมนาผมต้องนำคุยทำความรู้จักกับนิยามของ Knowledge แต่เนื่องจากเรื่องนี้มันคลุมเครือและออกนามธรรมมาก การที่น้องๆจะสมัครใจแสดงความคิดเห็น คงจะยากมาก ประกอบกับพึ่งอ่าน blog ของอาจารย์ cholwich ซึ่งเขียน&lt;a href="http://cholwich.org/wordpress/2008/12/02/student-randomizer/"&gt;โปรแกรม random ชื่อนักเรียนด้วย java&lt;/a&gt; ก็เลยลอกวิธีการมาใช้บ้าง แต่เนื่องจากช่วงนี้กำลังอินกับ clojure ก็เลย implement ด้วย clisp&lt;br /&gt;&lt;br /&gt;ลองมาดูตัวโปรแกรมกัน &lt;br /&gt;เริ่มจากการกำหนด global variable สำหรับเก็บชื่อคน&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*names*&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;ตามด้วยการกำหนดชื่อคน&lt;br /&gt;&lt;pre class="hl"&gt;(setq *names* '(pok bunny pann pune))&lt;/pre&gt;&lt;br /&gt;Note: global variable ใน clisp มี &lt;a href="http://pphetra.blogspot.com/2006/04/clisp-perl-dynamic-scope.html"&gt;scope แบบ dynamic&lt;/a&gt;, การตั้งชื่อนิยมตั้งด้วย * นำหน้า เพื่อให้แยกความแตกต่างจาก local variable เพราะถ้าตั้งชื่อตรงกันอาจจะเจอปัญหาการ binding ได้&lt;br /&gt;&lt;br /&gt;จากนั้นก็สร้าง function ที่ random ชื่อออกมา&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;next&lt;/span&gt; ()&lt;br /&gt;  (nth (random (length *names*)) *names*))&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;nth&lt;/code&gt; คือ function ที่ใช้ access list ที่ตำแหน่ง index ที่ต้องการ&lt;br /&gt;ส่วน &lt;code&gt;(random N)&lt;/code&gt; จะได้ค่า integer ระหว่าง 0 ถึง (N-1)&lt;br /&gt;&lt;br /&gt;โปรแกรมก็ทำงานได้ดี แต่ใช้ไปนานๆ มัน random คนออกมาซ้ำๆกันมากไปหน่อย &lt;br /&gt;กลับมาจากสัมมนาแล้ว ก็เลยมานั่งปรับปรุงให้มันเอาชื่อคนที่เคยเรียกแล้วออกไปจาก list ให้ด้วย (จะได้ไม่เรียกซ้ำ)&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;next&lt;/span&gt; ()&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((name (nth (random (length *names*)) *names*)))&lt;br /&gt;    (setq *names* (remove name *names*))&lt;br /&gt;    name))&lt;/pre&gt;&lt;br /&gt;สุดท้ายก็ปรับปรุงว่า ถ้าเอาชื่อออกจนหมดแล้ว ก็ให้ reset รายชื่อใหม่&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;next&lt;/span&gt; ()&lt;br /&gt;  (&lt;span class="keyword"&gt;if&lt;/span&gt; (null *names*)&lt;br /&gt;      (setq *names* '(pok bunny pann pune)))&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((name (nth (random (length *names*)) *names*)))&lt;br /&gt;    (setq *names* (remove name *names*))&lt;br /&gt;    name))&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6287811533747115027?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6287811533747115027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6287811533747115027' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6287811533747115027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6287811533747115027'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/12/random.html' title='random เรียกชื่อคน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2420398413802991697</id><published>2008-12-02T11:30:00.001+07:00</published><updated>2008-12-02T11:32:03.107+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>เรียนรู้ Clojure ตอน tail recusive</title><content type='html'>recursive คือหัวใจหลักของภาษาตระกูล functional, ตัว runtime ของภาษาตระกูลนี้ต้องออกแบบมาให้สามารถทำ optimize tail recursive เพื่อหลีกเลี่ยง stack overflow. โชคร้ายที่ jvm ไม่ได้ออกแบบมาเพื่อทำ optimize แบบนั้น (ยกเว้น IBM JVM - &lt;a href="http://pphetra.blogspot.com/2006/09/java-tail-recursive.html"&gt;Java กับ Tail-Recursive&lt;/a&gt;)&lt;br /&gt;เจ้า closure ก็เลยต้องอาศัย syntax พิเศษเพื่อเลียนการทำงานแบบ tail recursive&lt;br /&gt;&lt;br /&gt;ลองดูตัวอย่าง code จริง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="show-paren-match"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;render-level-layer&lt;/span&gt;&lt;br /&gt;  &lt;span class="string"&gt;"my, what an insane level representation i have here"&lt;/span&gt;&lt;br /&gt;  [target items if-empty if-full]&lt;br /&gt;  (&lt;span class="keyword"&gt;&lt;b&gt;loop&lt;/b&gt;&lt;/span&gt; [target target&lt;br /&gt;         remain (seq items)]&lt;br /&gt;    (&lt;span class="keyword"&gt;if&lt;/span&gt; remain&lt;br /&gt;      (&lt;span class="keyword"&gt;let&lt;/span&gt; [x (&lt;span class="builtin"&gt;:x&lt;/span&gt; (first remain))&lt;br /&gt;            y (&lt;span class="builtin"&gt;:y&lt;/span&gt; (first remain))&lt;br /&gt;            i (level-index x y)&lt;br /&gt;            at (target i)]&lt;br /&gt;        (&lt;span class="keyword"&gt;&lt;b&gt;recur&lt;/b&gt;&lt;/span&gt; (assoc target i (&lt;span class="keyword"&gt;if&lt;/span&gt; (= at \space) if-empty if-full))&lt;br /&gt;               (rest remain)))&lt;br /&gt;      target))&lt;span class="show-paren-match"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;keyword จะอยู่ที่ loop กับ recur นี่แหล่ะ&lt;br /&gt;loop เป็น syntax แบบเดียวกับ let แต่มันจะกำหนดจุดที่จะเกิดการ recursion&lt;br /&gt;ส่วน recur เป็นการสั่งให้กระโดดกลับไปทำงานที่ loop อีกครั้ง แต่จะมีการเปลี่ยน binding ของตัวแปรที่กำหนดไว้ตอนประกาศ loop&lt;br /&gt;&lt;br /&gt;ลองดูตัวอย่างข้างล่าง ซึ่งเป็นการหาค่า factorial โดยใช้วิธี recursive แบบ pass ค่า accumulator&lt;br /&gt;จะเห็นว่าที่ตำแหน่ง loop มีการกำหนด binding ตัวแปร cnt กับ acc&lt;br /&gt;ดังนั้นเมื่อถึงตอนสั่ง recur ก็จะต้องส่ง parameter ที่จะเป็นค่าใหม่ของ cnt และ acc กลับมาด้วย&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="show-paren-match"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;factorial&lt;/span&gt;&lt;br /&gt;  (&lt;span class="keyword"&gt;fn&lt;/span&gt; [n]&lt;br /&gt;    (&lt;span class="keyword"&gt;loop&lt;/span&gt; [cnt n &lt;br /&gt;           acc 1]&lt;br /&gt;       (&lt;span class="keyword"&gt;if&lt;/span&gt; (zero? cnt)&lt;br /&gt;            acc&lt;br /&gt;          (&lt;span class="keyword"&gt;recur&lt;/span&gt; (dec cnt) (* acc cnt)))))&lt;span class="show-paren-match"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;เปรียบเทียบกับการเขียนแบบ recursive ใน clisp ก็จะเป็นแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="show-paren-match"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;factorial&lt;/span&gt; (n &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; (acc 1))&lt;br /&gt;   (&lt;span class="keyword"&gt;if&lt;/span&gt; (&amp;lt;= n 1)&lt;br /&gt;       acc&lt;br /&gt;       (factorial (- n 1) (* acc n)))&lt;span class="show-paren-match"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;       &lt;br /&gt;หลังจากที่ Clojure ออกมา ก็มีคนถามประเด็น mutual recursive ว่าจะเขียนอย่างไร&lt;br /&gt;ลองดูตัวอย่าง mutual recursive ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="show-paren-match"&gt;(&lt;/span&gt;declare bar&lt;span class="show-paren-match"&gt;)&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;foo&lt;/span&gt; [n] &lt;br /&gt;  (&lt;span class="keyword"&gt;if&lt;/span&gt; (pos? n) &lt;br /&gt;    (bar (dec n)) &lt;br /&gt;    &lt;span class="builtin"&gt;:done-foo&lt;/span&gt;)) &lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;bar&lt;/span&gt; [n] &lt;br /&gt;  (&lt;span class="keyword"&gt;if&lt;/span&gt; (pos? n) &lt;br /&gt;    (foo (dec n)) &lt;br /&gt;    &lt;span class="builtin"&gt;:done-bar&lt;/span&gt;)) &lt;br /&gt;&lt;br /&gt;(foo 1000000) &lt;br /&gt;-&amp;gt; java.lang.StackOverflowError &lt;/pre&gt;&lt;br /&gt;Note: code แบบข้างบนนี้ ถ้าไปเขียนใน clisp จะ run ได้โดย stack ไม่ overflow&lt;br /&gt;&lt;br /&gt;Rich Hickey ก็เลยแก้ code ของ clojure โดยใช้ technique ที่เรียกว่า trampoline&lt;br /&gt;&lt;blockquote&gt;instead of actually making a call in the tail, a function returns the function to be called, and a controlling loop calls it&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;syntax ของ foo bar ข้างบนก็เลยต้องเขียนแบบนี้แทน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="show-paren-match"&gt;(&lt;/span&gt;declare bar&lt;span class="show-paren-match"&gt;)&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;foo&lt;/span&gt; [n] &lt;br /&gt;  (&lt;span class="keyword"&gt;if&lt;/span&gt; (pos? n) &lt;br /&gt;    #(bar (dec n)) &lt;br /&gt;    &lt;span class="builtin"&gt;:done-foo&lt;/span&gt;)) &lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;bar&lt;/span&gt; [n] &lt;br /&gt;  (&lt;span class="keyword"&gt;if&lt;/span&gt; (pos? n) &lt;br /&gt;    #(foo (dec n)) &lt;br /&gt;    &lt;span class="builtin"&gt;:done-bar&lt;/span&gt;)) &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2420398413802991697?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2420398413802991697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2420398413802991697' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2420398413802991697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2420398413802991697'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/12/clojure-tail-recusive.html' title='เรียนรู้ Clojure ตอน tail recusive'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4765691610208228990</id><published>2008-11-24T22:06:00.002+07:00</published><updated>2008-11-24T22:11:51.743+07:00</updated><title type='text'>google กับ *</title><content type='html'>วันนี้เล่น clojure แล้ว search หาตัวอย่างการใช้ fn* ใน google&lt;br /&gt;ปรากฎว่าการใส่ * ในช่อง text ที่ใช้ search กลายเป็นการ enable feature searchwiki ไป&lt;br /&gt;&lt;br /&gt;ดูตัวอย่าง&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/SSrD3UdDLNI/AAAAAAAAAkE/w9DNscAqxfw/s1600-h/p2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 158px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/SSrD3UdDLNI/AAAAAAAAAkE/w9DNscAqxfw/s320/p2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5272241668941360338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/SSrD3TRz3hI/AAAAAAAAAkM/QVTNn_6pDrU/s1600-h/p3.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 155px;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/SSrD3TRz3hI/AAAAAAAAAkM/QVTNn_6pDrU/s320/p3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5272241668625784338" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4765691610208228990?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4765691610208228990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4765691610208228990' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4765691610208228990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4765691610208228990'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/11/google.html' title='google กับ *'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_I3r3qYlfei4/SSrD3UdDLNI/AAAAAAAAAkE/w9DNscAqxfw/s72-c/p2.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4160544005496406278</id><published>2008-11-24T16:22:00.002+07:00</published><updated>2008-11-24T16:33:11.688+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>เรียนรู้ Clojure ตอนที่ 3</title><content type='html'>Clojure ประกาศตัวชัดเจนว่า มันไม่ได้เป็น OOP (ถึงแม้จะ run อยู่บน platform ที่เป็น OOP จ๋าแบบ Java)&lt;br /&gt;และเลือกข้าง functional อย่างชัดเจน (ไม่เหมือน scala ที่เลือกมันสองข้างเลย)&lt;br /&gt;&lt;br /&gt;มาลองดู code ที่ตัดมาจากตัวอย่าง code ในตอนที่ 1&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;ns&lt;/span&gt; test&lt;br /&gt;        (&lt;span class="builtin"&gt;:import&lt;/span&gt; (java.util.regex Pattern)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defstruct&lt;/span&gt; &lt;span class="function-name"&gt;pos&lt;/span&gt; &lt;span class="builtin"&gt;:x&lt;/span&gt; &lt;span class="builtin"&gt;:y&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;explode&lt;/span&gt;&lt;br /&gt;  &lt;span class="string"&gt;"return a pos/type seq for each char in the line"&lt;/span&gt;&lt;br /&gt;  [y line]&lt;br /&gt;  (map (&lt;span class="keyword"&gt;fn&lt;/span&gt; [x y type] (vector (struct pos x y) type))&lt;br /&gt;       (iterate inc 0) (repeat y) line))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;line-explode&lt;/span&gt;&lt;br /&gt;  &lt;span class="string"&gt;"call explode on each line with the appropriate y"&lt;/span&gt;&lt;br /&gt;  [lines]&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; [linesplit (&lt;span class="keyword"&gt;.&lt;/span&gt; Pattern compile &lt;span class="string"&gt;"\n"&lt;/span&gt; (&lt;span class="keyword"&gt;.&lt;/span&gt; Pattern MULTILINE))] &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;must be a better way?&lt;br /&gt;&lt;/span&gt;    (reduce into&lt;br /&gt;            (map explode&lt;br /&gt;                 (iterate inc 0)&lt;br /&gt;                 (&lt;span class="keyword"&gt;.&lt;/span&gt; linesplit split lines)))))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เริ่มด้วยบรรทัดบนสุด เป็นการระบุ namespace (ถ้าเทียบกับ java ก็คือ package)&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;ns&lt;/span&gt; test&lt;br /&gt;        (&lt;span class="builtin"&gt;:import&lt;/span&gt; (java.util.regex Pattern)))&lt;/pre&gt;&lt;br /&gt;บรรทัดที่สองบอกด้วยว่าให้ import class java.util.regex.Pattern เข้ามาด้วย&lt;br /&gt;(note: ใน clojure มันมี syntax สำหรับ regex ให้ใช้เหมือนกัน แต่คนเขียนเลือกใช้แบบเรียกผ่าน java)&lt;br /&gt;&lt;br /&gt;มาดู function line-explode ก่อน&lt;br /&gt;จะเห็นเขาประกาศ function ด้วย form แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;(defn function-name description params body)&lt;br /&gt;&lt;br /&gt;เข่น&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;plus-9&lt;/span&gt; &lt;span class="string"&gt;"plus with 9"&lt;/span&gt; [x] (+ x 9))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;โปรแกรมตระกูล lisp นี่, ตัว code ก็เป็น data structure ด้วย&lt;br /&gt;ดังนั้นจะเห็นว่า params ก็คือ vector type, ส่วน body เป็น list type&lt;br /&gt;&lt;br /&gt;จาก code จะเห็นว่า function explode รับ parameter ตัวเดียว ก็คือ lines&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;line-explode&lt;/span&gt;&lt;br /&gt;  &lt;span class="string"&gt;"call explode on each line with the appropriate y"&lt;/span&gt;&lt;br /&gt;  [lines]&lt;/pre&gt;&lt;br /&gt;ตัว body ของ function line-explode จะอยู่ในรูป let syntax&lt;br /&gt;&lt;pre class="hl"&gt;(let bindings body)&lt;br /&gt;เช่น&lt;br /&gt;(let [x 9, y 10] (+ x y))&lt;br /&gt;&lt;br /&gt;run แล้วได้ผลลัพท์เป็น 19&lt;br /&gt;&lt;br /&gt;ใน bindings ของ let นั้น, variable ที่ถูก bind ก่อน สามารถถูกอ้างถึงโดย variable ที่ bind ทีหลังได้&lt;br /&gt;&lt;br /&gt;(let [x 9, y (+ x 7)] (+ x y))&lt;br /&gt;&lt;br /&gt;run แล้วได้ผลลัพท์เป็น 25&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ลองดู bindings ของโปรแกรมตัวอย่าง&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;let&lt;/span&gt; [linesplit (&lt;span class="keyword"&gt;.&lt;/span&gt; Pattern compile &lt;span class="string"&gt;"\n"&lt;/span&gt; (&lt;span class="keyword"&gt;.&lt;/span&gt; Pattern MULTILINE))] &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;must be a better way?&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;ถ้าเทียบกับ java แล้วบรรทัดข้างบนก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="type"&gt;Pattern&lt;/span&gt; &lt;span class="variable-name"&gt;linesplit&lt;/span&gt; = Pattern.compie(&lt;span class="string"&gt;"\n"&lt;/span&gt;, &lt;span class="constant"&gt;Pattern&lt;/span&gt;.MULTILINE);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ที่นี้ก็มาถึง body ของ function line-explode&lt;br /&gt;&lt;pre class="hl"&gt;    (reduce into&lt;br /&gt;            (map explode&lt;br /&gt;                 (iterate inc 0)&lt;br /&gt;                 (. linesplit split lines)))&lt;/pre&gt;&lt;br /&gt;เวลาอ่านก็ให้ไล่จากในออกมาข้างนอก&lt;br /&gt;เริ่มจากในสุด&lt;br /&gt;&lt;code&gt;(. linesplit split lines)&lt;/code&gt;&lt;br /&gt;ก็คือการเรียกใช้ method split บน Pattern instance (ในที่นี้ก็คือการตัดที่ตำแหน่ง new-line)&lt;br /&gt;&lt;br /&gt;&lt;code&gt;(iterate inc 0)&lt;/code&gt;&lt;br /&gt;ตรงนี้เขา show ความสามารถของ infinite-sequence และ lazy behavior ของ Clojure&lt;br /&gt;โดยผลลัพท์ที่ได้จะ เป็นไปตามกฎนี้&lt;br /&gt;สมาชิกตัวแรกของผลลัพท์เกิดจากการเรียกใช้ (inc 0) ซึ่งจะได้ผลลัพท์เป็น 1&lt;br /&gt;ผลลัพท์ตัวที่สองเกิดจากการเรียกใช้ (inc ผลลัพท์ตัวแรก) ซึ่งก็คือ (inc 1)&lt;br /&gt;ผลลัพท์ตัวถัดๆไปก็คือ (inc ผลลัพท์ตัวก่อนหน้ามัน)&lt;br /&gt;ดังนั้น (iterate inc 0) จะได้ผลลัพท์เป็น (1 2 3 4 5 6 ... infinity)&lt;br /&gt;&lt;br /&gt;ส่วน function map นี้ เดี๋ยวนี้นิยมใช้กันมากแล้ว คงไม่ต้องอธิบาย&lt;br /&gt;แต่ว่า map ของพวก lisp นี้เขารับ collection ได้หลาย collection พร้อมๆกัน&lt;br /&gt;ลองดูตัวอย่างนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;(map + '(1 2 3) '(3 4 5))&lt;br /&gt;=&gt; (4 6 8)&lt;br /&gt;(map + '(1 2 3) '(3 4 5 6 7 8))&lt;br /&gt;=&gt; (4 6 8)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เนื่องจากเจ้า map function มันไปเรียกใช้ function &lt;code&gt;explode&lt;/code&gt; ดังนั้นเราต้องตามไปดูว่ามันทำอะไร&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defn&lt;/span&gt; &lt;span class="function-name"&gt;explode&lt;/span&gt;&lt;br /&gt;  &lt;span class="string"&gt;"return a pos/type seq for each char in the line"&lt;/span&gt;&lt;br /&gt;  [y line]&lt;br /&gt;  (map (&lt;span class="keyword"&gt;fn&lt;/span&gt; [x y type] (vector (struct pos x y) type))&lt;br /&gt;       (iterate inc 0) (repeat y) line))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ใน code ข้างบนจะเห็นว่ามี &lt;code&gt;fn&lt;/code&gt; ในบรรทัดที่ 4 โดยมันคือการ define function อีกแบบหนึ่ง ซึ่งมีคุณสมบัติแบบ closure &lt;br /&gt;นั่นคือมันจะจับ ตัวแปร ณ scope ที่มันถูกประกาศ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;test&gt; (def foo &lt;br /&gt;        (let [y 10] &lt;br /&gt;             (fn [x] (+ x y))))&lt;br /&gt;#=(var test/foo)&lt;br /&gt;test&gt; (foo 7)&lt;br /&gt;17&lt;br /&gt;&lt;br /&gt;ลองเปลี่ยน y ให้เป็น 12 ณ ขณะที่ run&lt;br /&gt;test&gt; (let [y 12] (foo 7))&lt;br /&gt;17&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ตัว &lt;code&gt;repeat&lt;/code&gt; นั่นเป็น infinite list อีกตัวหนึ่ง&lt;br /&gt;&lt;pre class="hl"&gt;(repeat 7)&lt;br /&gt;=&gt; (7 7 7 7 7 7 7 7 7 .....)&lt;/pre&gt;&lt;br /&gt;ดูจาก code แล้วจะเห็นว่า function &lt;code&gt;explode&lt;/code&gt; มันก็คือการสร้าง list ของ &lt;code&gt;(pos x y) char&lt;/code&gt; นั่นเอง&lt;br /&gt;ทดลอง run ดู&lt;br /&gt;&lt;pre class="hl"&gt;test&gt; (explode 0 "hello")&lt;br /&gt;([{:x 0, :y 0} \h] [{:x 1, :y 0} \e] [{:x 2, :y 0} \l] [{:x 3, :y 0} \l] [{:x 4, :y 0} \o])&lt;/pre&gt;&lt;br /&gt;กลับมาที่ line-explode, function ตัวสุดท้ายก็คือ &lt;code&gt;(reduce into (map ..))&lt;/code&gt;&lt;br /&gt;reduce รับ parameter เป็น function และ list&lt;br /&gt;โดยมันจะเรียก function โดยมี parameter เป็นสมาชิกตัวที่ 1 และ 2 ของ list &lt;br /&gt;จากนั้นก็จะเรียก function โดยมี parameter ตัวที่เป็นผลลัพท์ที่ได้จากครั้งก่อน กับ สมาชิกตัวที่ 3&lt;br /&gt;ทำเช่นนี้ไปเรื่อยๆจนกว่าจะหมด&lt;br /&gt;&lt;pre class="hl"&gt;(reduce + [1 2 3 4 5])&lt;br /&gt;=&gt; 15&lt;br /&gt;เขียนอีกอย่างก็คือ&lt;br /&gt;(+ (+ (+ (+ 1 2) 3) 4) 5)&lt;/pre&gt;&lt;br /&gt;ส่วน &lt;code&gt;into&lt;/code&gt; นั้นทำงานแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;(into [1] [2])&lt;br /&gt;=&gt; [1 2]&lt;br /&gt;(into [1 2 3] [2])&lt;br /&gt;=&gt; [1 2 3 2]&lt;br /&gt;(into [1] [1 2 3])&lt;br /&gt;=&gt;[1 1 2 3]&lt;/pre&gt;&lt;br /&gt;มันคือ operation ที่จับ array บวกกันนั่นเอง&lt;br /&gt;&lt;br /&gt;สรุปแล้วจะเห็นว่าการ explode ก็คือการเปลี่ยน multi-line string ให้อยู่ในรูปของ [(pos x y) char] นั่นเอง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;test&gt; (line-explode "hello\nworld\n")&lt;br /&gt;([{:x 4, :y 1} \d] [{:x 3, :y 1} \l] [{:x 2, :y 1} \r] [{:x 1, :y 1} \o] [{:x 0, :y 1} \w] [{:x 0, :y 0} \h] [{:x 1, :y 0} \e] [{:x 2, :y 0} \l] [{:x 3, :y 0} \l] [{:x 4, :y 0} \o])&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4160544005496406278?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4160544005496406278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4160544005496406278' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4160544005496406278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4160544005496406278'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/11/clojure-3.html' title='เรียนรู้ Clojure ตอนที่ 3'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-131552630103540612</id><published>2008-11-21T11:28:00.000+07:00</published><updated>2008-11-21T11:29:13.415+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>เรียนรู้ clojure - data structure ตอน 2</title><content type='html'>ต่อจากเมื่อวาน&lt;br /&gt;data structure ตัวถัดไปก็คือ map&lt;br /&gt;ลองดูคำสั่งที่เขาใช้ (เขาใช้ map keyboard code เข้ากับ ทิศทาง)&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;AsciiToDx&lt;/span&gt; {72 -1,&lt;br /&gt;                76  1,&lt;br /&gt;                75  0,&lt;br /&gt;                74  0})&lt;/pre&gt;&lt;br /&gt;เจ้าเครื่องหมาย { เป็น syntax อย่างย่อ&lt;br /&gt;ถ้าจะสั่งแบบยาวหน่อย ก็ให้สั่งแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;AsciiToDx&lt;/span&gt; (hash-map 72 -1 76 1 75 0 74 0))&lt;/pre&gt;&lt;br /&gt;เครื่องหมาย comma นั้นจะใส่หรือไม่ใส่ก็ได้&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;AsciiToDx&lt;/span&gt; (hash-map 72 -1, 76 1, 75 0, 74 0))&lt;/pre&gt;&lt;br /&gt;การ access ค่า สามารถทำได้โดยคำสั่ง &lt;code&gt;get&lt;/code&gt;&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(get AsciiToDx 72)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;-1&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ถัดจาก map ก็คือ vector และ list&lt;br /&gt;ทั้ง vector และ list จะใช้คำสั่งเหมือนกันอย่างกับแกะ ต่างกันตรง data structure ภายในที่เก็บ&lt;br /&gt;เจ้า vector เวลาเรา add item เข้าไป, item นั้นจะไปต่อท้าย&lt;br /&gt;ส่วน list นั้น ถ้าเรา add item เข้าไป, item จะไปอยู่ที่หัว&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(conj '(1 2 3) 4)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;(4 1 2 3)&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(conj [1 2 3] 4)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;[1 2 3 4]&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Note: จะเห็นเครื่องหมาย &lt;code&gt;'&lt;/code&gt; ที่หน้า list &lt;code&gt;(1 2 3)&lt;/code&gt;&lt;br /&gt;เครื่องหมายนั้นจะบอก clojure ว่า (1 2 3) เป็น data นะ ไม่ต้องไป evaluate มัน&lt;br /&gt;&lt;br /&gt;เวลาดูโปรแกรม clisp จะเห็นว่ามีเครื่องหมาย &lt;code&gt;()&lt;/code&gt; อยู่เต็มไปหมด (ชื่อภาษามันบอกอยู่แล้วว่ามันคือ list)&lt;br /&gt;แต่เจ้า clojure ที่ลอก syntax มา มีการดัดแปลง syntax ไปเล็กน้อย&lt;br /&gt;โดยจะเห็นว่าเขาเอา vector มาใช้ define พวก parameter list, binding list&lt;br /&gt;ซึ่งก็น่าจะทำให้ programmer recognise code ได้ไวขึ้น&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-131552630103540612?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/131552630103540612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=131552630103540612' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/131552630103540612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/131552630103540612'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/11/clojure-data-structure-2.html' title='เรียนรู้ clojure - data structure ตอน 2'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-9094117717967413586</id><published>2008-11-20T11:34:00.001+07:00</published><updated>2008-11-20T11:37:46.345+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>เรียนรู้ clojure - data structure</title><content type='html'>เมื่อวาน @somkiat post link &lt;a href="http://blog.learnr.org/post/59883018/first-clojure-program"&gt;http://blog.learnr.org/post/59883018/first-clojure-program&lt;/a&gt; ใน twiiter&lt;br /&gt;ผมตามไปอ่านแล้ว รู้สึกถูกใจในขนาดและความยากที่ดูจะเหมาะแก่การเริ่มต้นเรียนรู้ Clojure&lt;br /&gt;&lt;br /&gt;เริ่มกันที่ data structure ก่อน &lt;br /&gt;ในโปรแกรมนี้เขาใช้ StructMaps ในการเก็บค่าต่างๆเช่น position ของ กำแพง,ผู้เล่น&lt;br /&gt;โดย StructMaps นั้นสามารถสร้างได้โดยใช้คำสั่งู่แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defstruct&lt;/span&gt; &lt;span class="type"&gt;pos&lt;/span&gt; &lt;span class="builtin"&gt;:x&lt;/span&gt; &lt;span class="builtin"&gt;:y&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;Note: เจ้าคำสั่ง defstruct จริงๆมันเป็นแค่ macro&lt;br /&gt;สิ่งที่เกิดขึ้นจริงๆ ก็คือมันจะแปลงคำสั่ง &lt;code&gt;(defstruct pos :x :y)&lt;/code&gt; ให้เป็น &lt;code&gt;(def pos (create-struct :x :y))&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;เวลาที่เราต้องการสร้าง data ก็ให้สั่งแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;(struct pos 1 2)&lt;/pre&gt;&lt;br /&gt;ถ้าต้องการ get ค่า x ก็ให้สั่ง&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="builtin"&gt;:x&lt;/span&gt; (struct pos 1 2))&lt;/pre&gt;&lt;br /&gt;เนื่องจาก Clojure พยายามเน้นที่ Parallel programming&lt;br /&gt;ดังนั้น struct เมื่อสร้างขึ้นมาแล้ว ก็ไม่สามารถเปลี่ยนค่าภายในได้ (immutable)&lt;br /&gt;ถ้าเราต้องการเปลี่ยนค่าภายใน ก็หมายถึงว่าเราต้องสร้าง data ใหม่ขึ้นมา&lt;br /&gt;ดูตัวอย่างนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(def mypos (struct pos 1 2))&lt;/span&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;สร้างตัวแปร mypos ที่เก็บตำแหน่ง (1,2)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;#=(var user/mypos)&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(assoc mypos :x 10)&lt;/span&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;เปลี่ยนค่า x ให้เป็น 10&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;{:x 10, :y 2}&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(:x mypos)&lt;/span&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;แสดงค่า x&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;1&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(def mypos (assoc mypos :x 10))&lt;/span&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;ถ้าต้องการเปลี่ยนก็หมายถึงว่าต้อง set mypos ให้ชี้ไปที่ StructMap ตัวใหม่ที่ return จาก assoc&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;#=(var user/mypos)&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(:x mypos)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;10&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เจ้า StructMaps จริงๆแล้วก็คือ Map ที่ define keys ที่เป็นไปได้ไว้ (รวมทั้งลำดับด้วย)&lt;br /&gt;เราสามารถใช้ คำสั่งต่างๆที่เกี่ยวกับ Map มาทำ operation บน StructMaps ได้หมด&lt;br /&gt;เช่น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(vals mypos)&lt;/span&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;return values ของ mypos&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;(10 2)&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(keys mypos)&lt;/span&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;return keys ของ mypos&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;(:x :y)&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คำสั่งที่น่าสนใจ ก็คือ accessor&lt;br /&gt;ที่ return function ที่ใช้ access value ของ StructMaps นั้นๆ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(def get-x (accessor pos :x))&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;#=(var user/get-x)&lt;br /&gt;&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;user&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(get-x mypos)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;10&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ในโปรแกรมจะเห็นตัวอย่างหนึ่งที่น่าสนใจก็คือ&lt;br /&gt;เขาประกาศ struct ของ level ไว้ดังนี้&lt;br /&gt;&lt;pre class="hl"&gt;(&lt;span class="keyword"&gt;defstruct&lt;/span&gt; &lt;span class="type"&gt;level&lt;/span&gt;&lt;br /&gt;           &lt;span class="builtin"&gt;:player&lt;/span&gt; &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;pos&lt;br /&gt;&lt;/span&gt;           &lt;span class="builtin"&gt;:boxes&lt;/span&gt;  &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;set-of-pos&lt;br /&gt;&lt;/span&gt;           &lt;span class="builtin"&gt;:goals&lt;/span&gt;  &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;set-of-pos&lt;br /&gt;&lt;/span&gt;           &lt;span class="builtin"&gt;:walls&lt;/span&gt;  &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;set-of-pos&lt;br /&gt;&lt;/span&gt;           )&lt;/pre&gt;&lt;br /&gt;ที่น่าสนใจก็คือ ปกติถ้าเราต้องการสร้าง data ของ level เราก็ใช้คำสั่ง&lt;br /&gt;&lt;code&gt;(struct level a b c d)&lt;/code&gt; ได้&lt;br /&gt;แต่การใช้คำสั่งแบบนี้ จะทำให้สับสนภายหลังได้ง่าย ว่า ตัวแปร a,b,c,d หมายถึงอะไร&lt;br /&gt;ดังนั้นวิธีการที่เหมาะก็คือ ใช้คำสั่ง struct-map ในการสร้าง data แทน&lt;br /&gt;&lt;pre class="hl"&gt;(struct-map level&lt;br /&gt;            &lt;span class="builtin"&gt;:player&lt;/span&gt; (first (filter-level expl-level \@ \+))&lt;br /&gt;            &lt;span class="builtin"&gt;:boxes&lt;/span&gt; (filter-level expl-level \o \*)&lt;br /&gt;            &lt;span class="builtin"&gt;:goals&lt;/span&gt; (filter-level expl-level \. \* \+)&lt;br /&gt;            &lt;span class="builtin"&gt;:walls&lt;/span&gt; (filter-level expl-level \# \#))))&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-9094117717967413586?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/9094117717967413586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=9094117717967413586' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/9094117717967413586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/9094117717967413586'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/11/clojure-data-structure.html' title='เรียนรู้ clojure - data structure'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7319574457020874942</id><published>2008-11-14T12:25:00.000+07:00</published><updated>2008-11-14T12:26:00.571+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>fast-forward ใน git-merge</title><content type='html'>เห็นคำว่า Fast forward เวลาสั่ง git pull มานานแล้ว, มาอ่าน git-doc ก็เลยเข้าใจขึ้น&lt;br /&gt;&lt;br /&gt;เวลาเราสั่ง git merge, การทำงานของมันสามารถแบ่งได้เป็น 3 ประเภท&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;    &lt;li&gt;ถ้า merged commit ที่เราดึงมา อยู่ใน Head(current tree ของเรา) ของเราแล้ว, ก็จะแสดงผลลัพท์ "Already up-to-date." แล้วก็จบการทำงาน&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;ถ้า Head ของเราอยู่ใน commits ที่ดึงมา, case นี้มักเกิดจากคำสั่ง "git pull" เพื่อดึง code จากต้นน้ำมา update code(ที่ไม่มีการเปลี่ยนแปลง) ของเรา, สิ่งที่เกิดขึ้นก็คือ git จะ update HEAD ของเราให้ตรงตาม HEAD ของ merged commit (โดยไม่มีการสร้าง commit object ใหม่ขึ้นมา) มีศัพท์เฉพาะสำหรับกรณีนี้ว่า "Fast-forward"&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;สุดท้ายเป็นกรณีที่เกิดการ merge จริงๆ นั่นคือ ตัว HEAD ของเรา independent กับ merged commit, ดังนั้นกรณนี้จะเกิดการ merge จริง และมีการสร้าง commmit object ใหม่ขึ้นมา&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7319574457020874942?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7319574457020874942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7319574457020874942' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7319574457020874942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7319574457020874942'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/11/fast-forward-git-merge.html' title='fast-forward ใน git-merge'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1019030649766281857</id><published>2008-11-13T12:13:00.003+07:00</published><updated>2008-11-13T12:25:13.325+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>config syntax ใน grails</title><content type='html'>ผมสงสัยเรื่อง configuration ใน grails มาได้พักใหญ่แล้ว&lt;br /&gt;เพราะเห็น  syntax มันแปลกประหลาดเหลือเกิน  ลองดูตัวอย่างข้างล่างนี้&lt;br /&gt;&lt;pre class="hl"&gt;log4j.appender.stdout = "org.apache.log4j.ConsoleAppender"&lt;br /&gt;log4j.appender."stdout.layout"="org.apache.log4j.PatternLayout"&lt;/pre&gt;&lt;br /&gt;ทำไมบรรทัดที่สองต้องมี quote ครอบด้วยหล่ะ?&lt;br /&gt;&lt;br /&gt;class ที่ทำหน้าที่อ่าน  file Config.groovy ของ grails ก็คือ &lt;a href='http://groovy.codehaus.org/gapi/groovy/util/ConfigSlurper.html'&gt;ConfigSlurper&lt;/a&gt;&lt;br /&gt;โดยวิธีการใช้งานเจ้า ConfigSlurper ก็คือแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;config&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;ConfigSlurper&lt;/span&gt;().parse(&lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;File&lt;/span&gt;(&lt;span class="string"&gt;'/tmp/x.groovy'&lt;/span&gt;).toURL())&lt;/pre&gt;&lt;br /&gt;สิ่งที่เจ้า ConfigSlurper return มาก็คือ Map ตัวหนึ่ง (มันคือ class ConfigObject ที่ extends จาก  LinkedHashMap)&lt;br /&gt;&lt;br /&gt;ที่นี้มาลองดู nature ของมันบ้าง&lt;br /&gt;เริ่มจาก config ง่ายๆ บรรทัดเดียว&lt;br /&gt;&lt;pre class="hl"&gt;topic1.subtopic1=1&lt;/pre&gt;&lt;br /&gt;ผลลัพท์ที่ได้จากการ parse จะเป็น map หน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;[&lt;span class="string"&gt;"topic1"&lt;/span&gt;:[&lt;span class="string"&gt;"subtopic1"&lt;/span&gt;&lt;span class="constant"&gt;:1&lt;/span&gt;]]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ที่นี้ถ้าลองเพิ่ม sub node เข้าไปใต้ subtopic แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;topic1.subtopic1=1&lt;br /&gt;topic1.subtopic1.subofsubtopic1="foo"&lt;/pre&gt;&lt;br /&gt;เมื่อลอง  parse ดู แทนที่จะได้ผลลัพท์ เรากลับได้  exception แบบนี้แทน&lt;br /&gt;&lt;pre class="hl"&gt;groovy.lang.MissingPropertyException: No such property: subofsubtopic1 for class: java.lang.Integer&lt;br /&gt; at script1226551599145.run(script1226551599145.groovy:2)&lt;br /&gt; at Script9.run(Script9:1)&lt;/pre&gt;&lt;br /&gt;ดูเหมือนว่าการทำงานภายในของมันก็คือ เมื่อมันต้องการ assign ค่า &lt;code&gt;topic1.subtopic1.subofsubtopic1&lt;/code&gt; มันจะไป get  value โดยใช้  key &lt;code&gt;topic1.subtopic1&lt;/code&gt; จากนั้นก็พยายามจะ set property &lt;code&gt;subofsubtopic1&lt;/code&gt;ลงใน value ที่ได้มาก ซึ่งไม่สำเร็จแน่ๆเพราะ  value ที่ได้มันไม่ใช่ map อย่างที่ควรจะเป็น&lt;br /&gt;&lt;br /&gt;ที่นี้ลอง config แบบนี้บ้าง&lt;br /&gt;&lt;pre class="hl"&gt;topic1.subtopic1.subofsubtopic1="foo"&lt;br /&gt;topic1.subtopic1=1&lt;/pre&gt;&lt;br /&gt;ผลลัพท์ที่ได้คือ&lt;br /&gt;&lt;pre class="hl"&gt;[&lt;span class="string"&gt;"topic1"&lt;/span&gt;:[&lt;span class="string"&gt;"subtopic1"&lt;/span&gt;&lt;span class="constant"&gt;:1&lt;/span&gt;]]&lt;/pre&gt;&lt;br /&gt;อ้าว แล้ว  subofsubtopic1 ของเราหายไปไหนหล่ะ &lt;br /&gt;กลายเป็นว่า subofsubtopic1 ของเราถูก เจ้า topic1.subtopic1 override ค่าไปเรียบร้อยแล้ว&lt;br /&gt;&lt;br /&gt;ลองให้เหลือ บรรทัดเดียวแบบนี้ ดูบ้าง&lt;br /&gt;&lt;pre class="hl"&gt;topic1.subtopic1.subofsubtopic1="foo"&lt;/pre&gt;&lt;br /&gt;ผลลัพท์ที่ได้ จะเป็นแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;["topic1":["subtopic1":["subofsubtopic1":"foo"]]]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สรุปได้ว่า เนื่องจาก ConfigSlurperใช้ nested  map เป็น internal structure ในการเก็บผลลัพท์จากการ parse&lt;br /&gt;ทำให้เกิดข้อจำกัดในการเก็บขึ้นมา ดังนั้นถ้าเราต้องการเก็บค่าแบบบ้างบนให้ได้ เราก็เลยต้อง hack มัน โดยการห่อค่าให้เป็น string แบบนี้แทน&lt;br /&gt;&lt;pre class="hl"&gt;topic1.subtopic1=1&lt;br /&gt;topic1."subtopic1.subofsubtopic1"="foo"&lt;/pre&gt;&lt;br /&gt;ผลลัพท์ที่ได้จากการ map ก็จะเป็นแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;[&lt;span class="string"&gt;"topic1"&lt;/span&gt;:[&lt;span class="string"&gt;"subtopic1"&lt;/span&gt;&lt;span class="constant"&gt;:1&lt;/span&gt;, &lt;span class="string"&gt;"subtopic1.subofsubtopic1"&lt;/span&gt;:&lt;span class="string"&gt;"foo"&lt;/span&gt;]]&lt;/pre&gt;&lt;br /&gt;อืมม์ไม่สวยเป็นอย่างยิ่ง น่าจะมีคน refactor นะ&lt;br /&gt;&lt;br /&gt;สรุปได้ว่า  ถ้าเรานำไปใช้งาน configuration  ของเรา (ถ้าใช้ grails ก็ต้องเจอแน่ๆ)&lt;br /&gt;เราก็ควรจะออกแบบ tree ของเราดีๆ อย่าให้เกิดกรณีที่ parent node มีค่า value attach อยู่&lt;br /&gt;(ให้มี valueได้เฉพาะ leaf node)&lt;br /&gt;ไม่งั้นอาจจะปวดหัวว่า ทำไมค่า config ถึงหายไป หรือไม่ก็ ทำไมถึง get config ที่ต้องการไม่เจอ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1019030649766281857?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1019030649766281857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1019030649766281857' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1019030649766281857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1019030649766281857'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/11/config-syntax-grails.html' title='config syntax ใน grails'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5525513128722658765</id><published>2008-10-28T15:24:00.002+07:00</published><updated>2008-10-28T15:30:10.332+07:00</updated><title type='text'>Change  blindness</title><content type='html'>ในปี 1998 Simons, D., &amp; Levin, D.  ได้เขียน paper ที่น่าสนใจ ชื่อว่า&lt;br /&gt;Failure to Detect Changes to People During a Real-World Interaction&lt;br /&gt;&lt;br /&gt;เขาทดลองให้ หน้าม้าปลอมตัวเป็นนักท่องเที่ยว  จากนั้นก็ให้หน้าม้าทำทีกางแผนที่เข้าไปสอบถามเส้นทาง กับ เป้าหมาย&lt;br /&gt;จังหวะที่กำลังสอบถามนั้น ก็ให้หน้าม้าอีกสองคน ถือประตู(กำลังขนประตูไปที่ไหนสักแห่ง) เดินแทรกกลางเข้ามาระหว่าง หน้าม้านักท่องเที่ยว กับเป้าหมาย&lt;br /&gt;จังหวะที่ประตูกำลังบังเป้าหมายอยู่นั้น ก็ถือโอกาสสลับเปลี่ยนตัวหน้าม้าที่ทำตัวเป็นนักท่องเที่ยว&lt;br /&gt;&lt;br /&gt;คำถามของเขาก็คือ  เป้าหมายจะรู้หรือไม่ว่า มีการสลับตัวเกิดขึ้น&lt;br /&gt;&lt;br /&gt;ครั้งที่ 1 เขาทดลอง 15 ครั้ง 7 ใน 15 บอกว่าสังเกตเห็นการเปลี่ยนแปลง ที่น่าสนใจก็คือ  7 คน นั้นอยู่ในช่วงอายุ 20-30 ปี ซึ่งเป็นช่วงอายุเดียวกับหน้าม้า&lt;br /&gt;ส่วนเป้าหมายที่ไม่เห็นการเปลี่ยนแปลง จะอยู่ในช่วงอายุ  30-60 ปี&lt;br /&gt;&lt;br /&gt;เขาตั้งข้อสงสัยว่า social group มีส่วนเกี่ยวข้องด้วยหรือไม่? (สมมติฐานมีอยู่ว่า เรา treat คนไม่เท่าเทียมกัน โดยให้ความสำคัญกับกลุ่มที่เป็นพวกเดียวกับเรามากกว่า) เขาก็เลยทำการทดลองใหม่อีกครั้ง คราวนี้เปลี่ยนรูปลักษณ์หน้าม้า โดยแปลงร่างเป็นช่างก่อสร้าง &lt;br /&gt;(ที่พิเศษว่าครั้งแรก ก็คือ หน้าม้าใส่เสื้อคนละสีเลย  เมื่อเทียบกับครั้งแรกที่ยังแต่งตัวเหมือนๆกันอยู่)&lt;br /&gt;&lt;br /&gt;จากการทดลอง 12 ครั้ง กับเป้าหมายที่อายุ 20-30 ปี พบว่า มีแค่ 4 คนเท่านั้นที่สังเกตุเห็นการเปลี่ยนแปลง&lt;br /&gt;&lt;br /&gt;? blindness นั้นเกิดจากอะไร, เป็นข้อจำกัดทางกายภาพของเรา&lt;br /&gt;หรือเกิดจากการที่เราหมกมุ่นกับความคิดที่อยู่ใน "หัว" ของเรามากเกินไป&lt;br /&gt;&lt;br /&gt;ตามไปดูรูปและรายละเอียดได้ที่นี่&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.psych.sjsu.edu/~mvselst/courses/psyc235/presentation/detectchange.ppt"&gt;http://www.psych.sjsu.edu/~mvselst/courses/psyc235/presentation/detectchange.ppt&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://personal.stevens.edu/~ysakamot/175D/assignment/changedetection.pdf"&gt;http://personal.stevens.edu/~ysakamot/175D/assignment/changedetection.pdf&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5525513128722658765?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5525513128722658765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5525513128722658765' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5525513128722658765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5525513128722658765'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/10/change-blindness.html' title='Change  blindness'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7957866623163335343</id><published>2008-10-17T16:59:00.002+07:00</published><updated>2008-10-17T17:05:11.226+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>Local Loop Expression ภาค 2</title><content type='html'>วันนี้เห็น  code  &lt;a href="http://porg.es/blog/simple-socket-programming-with-haskell"&gt;การบ้าน  haskell  ของ Porges&lt;/a&gt;&lt;br /&gt;ตรงส่วนของการวน loop รับ entry ของ user  &lt;br /&gt;โดย user ต้องเลือก choice ว่าอยากให้โปรแกรมเป็น c(client) หรือ s(server)&lt;br /&gt;โดย code มีหน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;&lt;span class="function-name"&gt;main&lt;/span&gt; &lt;span class="variable-name"&gt;=&lt;/span&gt; withSocketsDo &lt;span class="variable-name"&gt;$&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;enable sockets under windows&lt;br /&gt;&lt;/span&gt;        putStrLn &lt;span class="string"&gt;"Welcome to One-Way Chat version 1.0"&lt;/span&gt;&lt;br /&gt;        &lt;span class="variable-name"&gt;...&lt;/span&gt;&lt;br /&gt;        input &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; untilM   &lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;get input of 'c' or 's'&lt;br /&gt;&lt;/span&gt;                (&lt;span class="variable-name"&gt;\&lt;/span&gt;x &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; (not &lt;span class="variable-name"&gt;$&lt;/span&gt; null x) &lt;span class="variable-name"&gt;&amp;amp;&amp;amp;&lt;/span&gt; toLower (head x) &lt;span class="variable-name"&gt;`elem`&lt;/span&gt; &lt;span class="string"&gt;"cs"&lt;/span&gt;)&lt;br /&gt;                (putStr &lt;span class="string"&gt;"Client or server? "&lt;/span&gt; &lt;span class="variable-name"&gt;&amp;gt;&amp;gt;&lt;/span&gt; getLine)&lt;br /&gt;        &lt;span class="variable-name"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ประเด็นที่ผมสนใจ ก็คือ การวนลูป รับค่าจาก user และตรวจสอบว่าเป็นค่าที่ถูกต้องไหม&lt;br /&gt;ตรงนี้เขา define function ที่ชื่อ untilM เข้ามาช่วย&lt;br /&gt;หน้าตาของ untilM &lt;br /&gt;    &lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;monadic `until`&lt;br /&gt;&lt;/span&gt;&lt;span class="function-name"&gt;untilM&lt;/span&gt; p x &lt;span class="variable-name"&gt;=&lt;/span&gt; x &lt;span class="variable-name"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; (&lt;span class="variable-name"&gt;\&lt;/span&gt;y &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; p y &lt;span class="keyword"&gt;then&lt;/span&gt; return y &lt;span class="keyword"&gt;else&lt;/span&gt; untilM p x) &lt;/pre&gt;&lt;br /&gt;จะเห็นว่า untilM รับ arguments 2 ตัวคือ p(predicate) กับ x&lt;br /&gt;โดยมันจะทำ x ก่อน&lt;br /&gt;ผลลัพท์ที่ได้จะถูก test ด้วย p&lt;br /&gt;ถ้าผ่าน ก็จะ return กลับมา ถ้าไม่ผ่านก็ recursive รับค่าต่อไป&lt;br /&gt;&lt;br /&gt;เห็นแล้วก็นึกถึง &lt;a href="pphetra.blogspot.com/2008/09/local-loop-expression.html"&gt;Local loop expression&lt;/a&gt; ที่เคยเขียนถึง&lt;br /&gt;ก็เลยลองจับ untilM มาเขียนใหม่ด้วย fix ได้หน้าตาแบบนี้ออกมา&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="type"&gt;Control.Monad.Fix&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="function-name"&gt;main&lt;/span&gt; &lt;span class="variable-name"&gt;=&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="variable-name"&gt;...&lt;/span&gt;&lt;br /&gt;          &lt;span class="variable-name"&gt;...&lt;/span&gt;&lt;br /&gt;          input &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; fix (&lt;span class="variable-name"&gt;\&lt;/span&gt;loop &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; &lt;br /&gt;                         &lt;span class="keyword"&gt;do&lt;/span&gt; putStr &lt;span class="string"&gt;"Client or server? "&lt;/span&gt;&lt;br /&gt;                            l &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; getLine &lt;br /&gt;                            &lt;span class="keyword"&gt;if&lt;/span&gt; head(l) &lt;span class="variable-name"&gt;`elem`&lt;/span&gt; &lt;span class="string"&gt;"cs"&lt;/span&gt; &lt;span class="keyword"&gt;then&lt;/span&gt; return l &lt;span class="keyword"&gt;else&lt;/span&gt; loop)&lt;br /&gt;          &lt;span class="variable-name"&gt;...&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;ดูแล้วก็ยังไม่สวยถูกใจเท่าไรนัก&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7957866623163335343?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7957866623163335343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7957866623163335343' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7957866623163335343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7957866623163335343'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/10/local-loop-expression-2.html' title='Local Loop Expression ภาค 2'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3336131738986704175</id><published>2008-10-04T08:43:00.002+07:00</published><updated>2008-10-04T10:24:38.055+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>thunks</title><content type='html'>พึ่งได้เห็นคำว่า &lt;code&gt;thunks&lt;/code&gt; จาก &lt;a href="http://www.windley.com/archives/2008/10/javascript_thunks.shtml"&gt;blog ของ Phil Windley&lt;/a&gt;&lt;br /&gt;ซึ่งเขาบอกว่าการเขียน JQuery นั้น, มันบังคับให้ใช้ form แบบ thunks เยอะมาก&lt;br /&gt;และเขาก็ยกตัวอย่าง code มาให้ดูด้วย &lt;br /&gt;&lt;pre class="hl"&gt;$(document).ready(&lt;b&gt;&lt;span class="keyword"&gt;function&lt;/span&gt;()&lt;/b&gt; {&lt;br /&gt;  $(&lt;span class="string"&gt;"#orderedlist li:last"&lt;/span&gt;).hover(&lt;b&gt;&lt;span class="keyword"&gt;function&lt;/span&gt;()&lt;/b&gt; {&lt;br /&gt;     $(&lt;span class="builtin"&gt;this&lt;/span&gt;).addClass(&lt;span class="string"&gt;"green"&lt;/span&gt;);&lt;br /&gt;  },&lt;b&gt;&lt;span class="keyword"&gt;function&lt;/span&gt;()&lt;/b&gt;{&lt;br /&gt;     $(&lt;span class="builtin"&gt;this&lt;/span&gt;).removeClass(&lt;span class="string"&gt;"green"&lt;/span&gt;);&lt;br /&gt;  });&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;ใน code ข้างบนบั้น, thunks ก็คือ function ที่ไม่มี parameter&lt;br /&gt;และวัตถุประสงค์การใช้มัน ก็คือ delay การทำงานของ code ในส่วนที่เราต้องการส่งไปเป็น argument &lt;br /&gt;&lt;br /&gt;ต้องท้าวความนิดหนึ่ง สำหรับคนที่ตามไม่ทัน&lt;br /&gt;ใน programming language ส่วนใหญ่&lt;br /&gt;argument ที่เราส่งไปให้ function หรือ procedure&lt;br /&gt;จะถูก evulate ก่อนที่จะถูกส่งเข้าไปใน function&lt;br /&gt;&lt;br /&gt;ยกตัวอย่าง syntax แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;expr ? Texpr: Fexpr&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ในหลายๆภาษาเราไม่สามารถสร้าง function ให้เรียนแบบ syntax ข้างบนได้&lt;br /&gt;เพราะถ้าเราลองเขียนแบบข้างล่างนี้ดู เราจะพบว่า t กับ f จะถูก evaluate ก่อนที่จะ check if เสมอ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;int ifexpr( int x, int t, int f )&lt;br /&gt;{&lt;br /&gt;    if( x ) return t;&lt;br /&gt;    return f;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สำหรับผมหลังจากนั่งงงดู code สักพักแล้วก็ร้องอ๋อ ว่า มันคาบเกี่ยวกับเรื่องของ higher order function &lt;br /&gt;แล้วก็ closure ด้วย (คิดว่า thunk จริงๆใน lisp ซึ่งเป็นต้นกำเนิดของคำนี้ น่าจะมีความหมายมากกว่านี้)&lt;br /&gt;&lt;br /&gt;อ่านรายละเอียดเพิ่มเติมใน &lt;a href="http://en.wikipedia.org/wiki/Thunk"&gt;wikipedia-thunk&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3336131738986704175?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3336131738986704175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3336131738986704175' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3336131738986704175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3336131738986704175'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/10/thunks.html' title='thunks'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2434424646292533574</id><published>2008-09-22T12:54:00.000+07:00</published><updated>2008-09-22T12:55:43.070+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>Local Loop Expression</title><content type='html'>เคย&lt;a href='http://pphetra.blogspot.com/2008/01/y-combinator-in-bangkokbarcamp.html'&gt;เขียนถึง Y combinator&lt;/a&gt; ไปที &lt;br /&gt;ตอนนั้นก็ไม่เคยคิดว่า มันจะมีตัวอย่างการใช้งานในชีวิตจริง&lt;br /&gt;วันนี้ได้เห็นตัวอย่างการนำไปใช้ ที่น่าสนใจ&lt;br /&gt;โดยเขาเอามันมาทำ local loop expression&lt;br /&gt;&lt;pre class="hl"&gt;reader &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; forkIO &lt;span class="variable-name"&gt;$&lt;/span&gt; fix &lt;span class="variable-name"&gt;$&lt;/span&gt; &lt;span class="variable-name"&gt;\&lt;/span&gt;loop &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;        (nr', line) &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; readChan chan'&lt;br /&gt;        when (nr &lt;span class="variable-name"&gt;/=&lt;/span&gt; nr') &lt;span class="variable-name"&gt;$&lt;/span&gt; hPutStrLn hdl line&lt;br /&gt;        loop&lt;/pre&gt;&lt;br /&gt;ในตัวอย่างข้างบน forkIO คือการแตก thread ออกไปทำงาน,&lt;br /&gt;fix คือ function ที่ทำให้เกิด magic &lt;br /&gt;โดยมันทำให้เราทำ recursive แบบไม่ต้องประกาศชื่อ function ได้&lt;br /&gt;(ในตัวอย่าง loop ก็คือ function ที่ pass เข้ามา, ถ้าเราอยากให้มัน recursive ทำ ก็ให้เรียกมันซ้ำ)&lt;br /&gt;&lt;br /&gt;ใน xmonad ที่เป็น window manager ที่เขียนด้วย haskell ก็มีใช้แบบนี้&lt;br /&gt;โดยการทำงานก็คือการวน loop ตรวจสอบ windows event&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;f&lt;/span&gt; &lt;span class="variable-name"&gt;=&lt;/span&gt; fix &lt;span class="variable-name"&gt;$&lt;/span&gt; &lt;span class="variable-name"&gt;\&lt;/span&gt;again &lt;span class="variable-name"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt;            more &lt;span class="variable-name"&gt;&amp;lt;-&lt;/span&gt; checkMaskEvent d enterWindowMask ev&lt;br /&gt;            when more again &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เจ้า function fix มันอยู่ใน module Control.Monad.Fix&lt;br /&gt;ซึ่งเกิดจาก &lt;a href='http://leventerkok.googlepages.com/mdo.pdf'&gt;paper นี้&lt;/a&gt;&lt;br /&gt;ใครธาตุไฟแข็งแรงก็เข้าไปอ่านได้ครับ (ผมเปิดดูแล้วก็ปิดทันที)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2434424646292533574?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2434424646292533574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2434424646292533574' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2434424646292533574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2434424646292533574'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/09/local-loop-expression.html' title='Local Loop Expression'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-4629763165641920679</id><published>2008-09-12T11:20:00.001+07:00</published><updated>2008-09-12T11:23:00.419+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>เขียน Rome 's plugin</title><content type='html'>ถ้าจะต้องเขียนโปรแกรม parse feed, เราสามารถเลือกวิธีการได้หลายวิธี&lt;br /&gt;แบบที่ง่ายที่สุดก็คือ ใช้พวก xml parser library ซึ่งมีอยู่มากมาย&lt;br /&gt;เรื่องกวนใจที่ตามมาก็คือ feed มันมี format หลักๆอยู่ 2 แบบ คือ rss และ atom&lt;br /&gt;โปรแกรม parser เราก็เลยฉลาดขึ้นมาอีกนิดหนึ่ง (จะเขียนโดยใช้ if หรือแยก function หรือแนว OOP ก็ว่าไป)&lt;br /&gt;&lt;br /&gt;ในส่วนตัวผม ผมเลือกใช้ Rome (มันเป็น Java !!!)&lt;br /&gt;ซึ่งเป็น api ในการ parse feed &lt;br /&gt;โดยมันจะซ่อนความแตกต่างของโครงสร้างระหว่าง RSS และ ATOM ไว้จากเรา&lt;br /&gt;&lt;br /&gt;ปัญหาที่เจอก็คือ กรณีที่เราไปดึง feed จาก FeedBurner&lt;br /&gt;มันจะมี extension element จำนวนหนึ่งที่ Rome ไม่รู้จัก (ทำให้ไม่มี method ให้ get ค่าเหล่านั้น)&lt;br /&gt;ยกตัวอย่าง Feed ของ Feedburner มันจะมีหน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&amp;lt;&lt;span class="function-name"&gt;feed&lt;/span&gt; &lt;span class="sgml-namespace"&gt;xmlns&lt;/span&gt;:&lt;span class="variable-name"&gt;feedburner&lt;/span&gt;=&lt;span class="string"&gt;"http://rssnamespace.org/feedburner/ext/1.0"&lt;/span&gt;&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;&lt;span class="function-name"&gt;entry&lt;/span&gt;&amp;gt;&lt;br /&gt;  &amp;lt;&lt;span class="function-name"&gt;title&lt;/span&gt;&amp;gt;xxx&amp;lt;/&lt;span class="function-name"&gt;title&lt;/span&gt;&amp;gt;&lt;br /&gt;  &amp;lt;&lt;span class="function-name"&gt;content&lt;/span&gt;&amp;gt;yyyy&amp;lt;&lt;span class="function-name"&gt;content&lt;/span&gt;&amp;gt;&lt;br /&gt;  &amp;lt;&lt;span class="function-name"&gt;author&lt;/span&gt;&amp;gt;bact'&amp;lt;/&lt;span class="function-name"&gt;author&lt;/span&gt;&amp;gt;&lt;br /&gt;  &amp;lt;&lt;span class="function-name"&gt;link&lt;/span&gt; &lt;span class="variable-name"&gt;href&lt;/span&gt;=&lt;span class="string"&gt;"http://feeds.feedburner.com/~r/bact/~3/326895937/hauntedness-management.html"&lt;/span&gt;/&amp;gt;&lt;br /&gt;  &amp;lt;&lt;span class="sgml-namespace"&gt;feedburner&lt;/span&gt;:&lt;span class="function-name"&gt;origLink&lt;/span&gt; &lt;span class="variable-name"&gt;href&lt;/span&gt;=&lt;span class="string"&gt;"http://bact.blogspot.com/2008/07/hauntedness-management.html"&lt;/span&gt;/&amp;gt;&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;entry&lt;/span&gt;&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;feed&lt;/span&gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;สิ่งที่เราต้องการก็คือ  &lt;code&gt;origLink&lt;/code&gt; ซึ่ง ROME ไม่มี api ให้ใช้&lt;br /&gt;แต่โชคดีที่คนออกแบบ Rome เขามองการณ์ไกล เขาก็เลยเตรียมช่อง plugin ให้เราไว้แล้ว&lt;br /&gt;&lt;br /&gt;วิธีการก็คือ เราต้องสร้าง (High Level) Parser ของเราขึ้นมาตัวหนึ่ง&lt;br /&gt;และทำการ register มันเข้ากับ Rome&lt;br /&gt;เจ้า Parser ตัวนี้ จะถูกเรียกใช้หลังจากที่ Rome สร้าง DOM structure ขึ้นมาแล้ว&lt;br /&gt;หน้าที่ของ parser ก็คือ return Object ที่ wrap ค่าที่เราต้องการไว้&lt;br /&gt;ตัวอย่าง code&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Parser&lt;/span&gt; &lt;span class="keyword"&gt;implements&lt;/span&gt; &lt;span class="type"&gt;ModuleParser&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;String&lt;/span&gt; &lt;span class="function-name"&gt;getNamespaceUri&lt;/span&gt;() {&lt;br /&gt;        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;FeedburnerModule&lt;/span&gt;.FEEDBURNER_URI;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;Module&lt;/span&gt; &lt;span class="function-name"&gt;parse&lt;/span&gt;(&lt;span class="type"&gt;Element&lt;/span&gt; &lt;span class="variable-name"&gt;element&lt;/span&gt;) {&lt;br /&gt;        &lt;span class="type"&gt;Element&lt;/span&gt; &lt;span class="variable-name"&gt;origLink&lt;/span&gt; = element.getChild(&lt;span class="string"&gt;"origLink"&lt;/span&gt;, &lt;span class="constant"&gt;FeedburnerModule&lt;/span&gt;.FEEDBURNER_NS);&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (origLink != &lt;span class="constant"&gt;null&lt;/span&gt;) {&lt;br /&gt;            &lt;span class="type"&gt;FeedburnerModuleImpl&lt;/span&gt; &lt;span class="variable-name"&gt;impl&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;FeedburnerModuleImpl&lt;/span&gt;(FeedburnerModule.&lt;span class="keyword"&gt;class&lt;/span&gt;, &lt;span class="constant"&gt;FeedburnerModule&lt;/span&gt;.FEEDBURNER_URI);&lt;br /&gt;            impl.setOrigLink(origLink.getTextTrim());&lt;br /&gt;            &lt;span class="keyword"&gt;return&lt;/span&gt; impl;&lt;br /&gt;        }&lt;br /&gt;        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;null&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;ในฝั่งคนใช้งาน ถ้าเขียนเป็น groovy ก็เขียนง่ายๆแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;entry0&lt;/span&gt; = feed.entries[0]&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;module&lt;/span&gt; = entry0.getModule(&lt;span class="string"&gt;'http://rssnamespace.org/feedburner/ext/1.0'&lt;/span&gt;)&lt;br /&gt;assertEquals &lt;span class="string"&gt;'http://bact.blogspot.com/2008/07/hauntedness-management.html'&lt;/span&gt;, module.origLink&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-4629763165641920679?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/4629763165641920679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=4629763165641920679' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4629763165641920679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/4629763165641920679'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/09/rome-s-plugin.html' title='เขียน Rome &apos;s plugin'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7228744060330707614</id><published>2008-08-31T20:54:00.004+07:00</published><updated>2008-09-01T22:15:06.610+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='barcampbangkok'/><title type='text'>barcampbangkok2</title><content type='html'>ในงานผมได้พูดเรื่อง "25 วัน เชียงใหม่-กรุงเทพฯ โดยเรือแคนนู"&lt;br /&gt;แต่ตอน present ผมเปิด google map ไม่ได้&lt;br /&gt;เลยขอลง map ให้ดูในนี้แล้วกัน&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=117097415395555489361.0004559b31a0aac962545&amp;amp;t=h&amp;amp;ll=16.301713,99.564628&amp;amp;spn=4.972607,1.940803&amp;amp;output=embed&amp;amp;s=AARTsJpsvmxEc9gUm2Z1XWQMtIkau3kWeg"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://maps.google.com/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=117097415395555489361.0004559b31a0aac962545&amp;amp;t=h&amp;amp;ll=16.301713,99.564628&amp;amp;spn=4.972607,1.940803&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;ส่วนรูปดูได้ที่นี่&lt;br /&gt;&lt;table style="width:194px;"&gt;&lt;tr&gt;&lt;td align="center" style="height:194px;background:url(http://picasaweb.google.com/f/img/transparent_album_background.gif) no-repeat left"&gt;&lt;a href="http://picasaweb.google.com/pphetra/25days"&gt;&lt;img src="http://lh3.ggpht.com/pphetra/SLgbiRkuV5E/AAAAAAAAAWA/uAOKBZXgoD8/s160-c/25days.jpg" width="160" height="160" style="margin:1px 0 0 4px;"&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="text-align:center;font-family:arial,sans-serif;font-size:11px"&gt;&lt;a href="http://picasaweb.google.com/pphetra/25days" style="color:#4D4D4D;font-weight:bold;text-decoration:none;"&gt;25days&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;เรื่องประทับใจในงาน barcampbangkok2 ของผม&lt;br /&gt;&lt;ul&gt;&lt;li&gt;เนื้อหาที่ประทับใจมากที่สุด เรื่อง Riding on Fans’ Energy: Touhou, Fan Culture, and Grassroot Entertainment ของอาจารย์โป้ง ที่พูดเกี่ยวกับปรากฎการณ์ของเกมส์ &lt;a href="http://en.wikipedia.org/wiki/Touhou_Project"&gt;Touhou&lt;/a&gt;&lt;br /&gt;เกมส์ที่สร้างโดยคนคนเดียว แต่สามารถจัด event ที่บรรดาแฟนๆ 47,000 คนมาร่วมงานได้&lt;br /&gt;ใครสนใจตามไปอ่านบทความของอาจารย์โป้งได้ที่นี่ &lt;a href="http://cardcaptor.moekaku.com/2008/09/01/riding-on-fans-energy-touhou-fan-culture-and-grassroot-entertainment/"&gt;Link&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ไม่ใช่แต่เนื้อหาเท่านั้นที่น่าสนใจ ประเด็นที่น่าสนใจอีกอย่างก็คือ&lt;br /&gt;ตอนที่อาจารย์โป้งพูดนั้น เวลาดันหมดเสียก่อน&lt;br /&gt;แต่ด้วย spirit ของ barcamp, คนฟังก็เลยรวมตัวกันเปิดห้องขึ้นใหม่อีกห้องหนึ่ง เพื่อฟังต่อให้จบ&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;ข้อสังเกต&lt;br /&gt;&lt;ul&gt;&lt;li&gt;จำนวนห้องที่มากถึง 8 ห้อง ทำให้เกิด &lt;a href="http://www.ted.com/index.php/talks/barry_schwartz_on_the_paradox_of_choice.html"&gt;paradox of choice&lt;/a&gt; เล็กๆขึ้นมา&lt;br /&gt;&lt;/li&gt;&lt;li&gt;2 วันนี่เหนื่อยไปหน่อย, ดูเหมือน climax จะไปอยู่ที่ beercamp ทำให้อีกวันดูเงียบๆไปหน่อย&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ขาดบริเวณพักผ่อนกลาง ที่เปิดโอกาสให้นั่งเล่น และ present งานสำหรับคนที่ไม่ได้ถูกเลือกหัวข้อ&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7228744060330707614?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7228744060330707614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7228744060330707614' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7228744060330707614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7228744060330707614'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/08/barcampbangkok2.html' title='barcampbangkok2'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/pphetra/SLgbiRkuV5E/AAAAAAAAAWA/uAOKBZXgoD8/s72-c/25days.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-325386934737274546</id><published>2008-08-26T22:06:00.003+07:00</published><updated>2008-08-27T14:33:00.457+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mq'/><category scheme='http://www.blogger.com/atom/ns#' term='ejaberd'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>rabbiter</title><content type='html'>&lt;a href="http://twitter.com/sugree"&gt;@sugree&lt;/a&gt; tweet ถามถึง &lt;a href='http://github.com/tonyg/rabbiter/tree/master'&gt;Rabbiter&lt;/a&gt;, ด้วยความที่ไม่รู้ว่ามันคืออะไร ก็เลยต้องไปหาข้อมูลมาอ่านก่อน&lt;br /&gt;&lt;br /&gt;เป้าหมายของเจ้า rabbiter ก็คือ&lt;br /&gt;&lt;blockquote&gt;using XMPP to build a decentralized microblogging platform (think Twitter busted apart to run as a distributed network of microblogging providers)&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;h3&gt;หลักการทำงาน&lt;/h3&gt;&lt;br /&gt;เจ้า rabbitter จริงๆแล้วก็คือ &lt;a href='http://www.process-one.net/en/wiki/ejabberd_module_development/'&gt;module extension&lt;/a&gt; ของ &lt;a href='http://www.ejabberd.im/'&gt;ejabberd&lt;/a&gt; โดยมันจะประพฤติตนเป็น bot คอยโต้ตอบกับเรา&lt;br /&gt;หน้าที่ของมันก็คือการ relay message ของคนอื่นๆที่เราสนใจ (follow) มาให้เรา หรือ ส่งต่อข้อความของเรา ไปให้กับคนอื่นที่สนใจติดตามเรา (follower) &lt;br /&gt;โดยการ relay นี้สามารถทำข้ามวง ejabberd server ได้&lt;br /&gt;ในแง่ architecture ประเด็นที่น่าสนใจก็คือ มันใช้ messageing queue (&lt;a href='http://www.rabbitmq.com/'&gt;rabbitmq&lt;/a&gt;) เข้ามา handle publisher-subscriber model.&lt;br /&gt;อ่านเอกสารอธิบายอย่างละเอียดได้ที่นี่&lt;br /&gt;&lt;a href='https://dev.rabbitmq.com/wiki/RabbiterFederation'&gt;https://dev.rabbitmq.com/wiki/RabbiterFederation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;วิธีการ install&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ติดตั้ง erlang, เนื่องจาก erlang compile ได้ง่ายๆ ดังนั้นใช้วิธี download source code มา compile เองดีกว่า&lt;br /&gt;version ที่ผมใช้ก็คือ r12-b3&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ติดตั้ง rabbitemq-server&lt;br /&gt;ตัวนี้ให้ download source version 1.4.0 มา เวลา compile ก็แค่สั่ง make เป็นอันเสร็จพิธี&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ทำให้ erlang มองเห็น rabbitmq-server module&lt;br /&gt;ถ้าเราติดตั้ง erlang ด้วยการ compile code เอง, modules path ของ erlang จะอยู่ที่ /usr/local/lib/erlang/lib&lt;br /&gt;โดยมีหน้าตาประมาณนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;pphetra@[/usr/local/lib/erlang/lib]&lt;br /&gt;$ ls&lt;br /&gt;appmon-2.1.9          dialyzer-1.8.1        mnesia-4.4.3          ssl-3.9&lt;br /&gt;asn1-1.5.2            docbuilder-0.9.8.4    observer-0.9.7.4      stdlib-1.15.3&lt;br /&gt;common_test-1.3.2     edoc-0.7.6            orber-3.6.9           syntax_tools-1.5.5&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ให้ทำ symbolic link กับ directory rabbitmq-server-1.4.0&lt;br /&gt;Note: ตรงนี้ให้ระวังชื่อด้วย เพราะ code ของ rabbiter มันต้องการให้เป็นชื่อ &lt;b&gt;rabbitmq_server&lt;/b&gt; เป๊ะๆ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ ls -l rab*&lt;br /&gt;lrwxr-xr-x   1 root  pphetra  40 Aug 27 13:05 rabbitmq_server -&gt; /Users/pphetra/dev/rabbitmq-server-1.4.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ขั้นถัดไปก็ clone เจ้า rabbiter จาก github&lt;br /&gt;Note: ใครที่สั่ง clone แล้วเกิด error timeout ให้ลองเปลี่ยน url จาก git://github.com/tonyg/rabbiter.git&lt;br /&gt;เป็น http://github.com/tonyg/rabbiter.git&lt;br /&gt;เมื่อได้มาแล้ว ก็ทิ้งไว้ก่อน เดี๋ยวเราจะไปฝาก compile กับ ejabberd&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ติดตั้ง ejabberd, ตัวนี้ให้ใช้ version 2.0.1&lt;br /&gt;ก่อน compile ก็ให้ไปแอบทำ symbolic link เจ้า file mod_rabbiter.erl เข้ามาใน directory ejabberd-2.0.1/src เสียก่อน&lt;br /&gt;(หรือจะเล่นลูกทุ่ง copy เอาดื้อๆ ก็ไม่ผิด)&lt;br /&gt;&lt;li&gt;config ejabberd ให้รู้จัก rabbiter&lt;br /&gt;โดย edit /etc/ejabberd/ejabberd.cfg&lt;br /&gt;เพิ่ม บรรทัดนี้ลงไป&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;{modules,&lt;br /&gt; [&lt;br /&gt;  {mod_adhoc,    []},&lt;br /&gt;  {mod_announce, [{access, announce}]}, % recommends mod_adhoc&lt;br /&gt;  {mod_caps,     []}, &lt;br /&gt;  ...&lt;br /&gt;&lt;b&gt;  {mod_rabbiter, []},&lt;/b&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;การทดสอบ&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;สั่ง start ejabberd ด้วยคำสั่ง &lt;code&gt;/sbin/ejabberdctl start&lt;/code&gt;&lt;br /&gt;ตรวจสอบว่า start สำเร็จหรือไม่ ด้วยคำสั่ง &lt;code&gt;/sbin/ejabberdctl status&lt;/code&gt;&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ ./ejabberdctl status&lt;br /&gt;Node ejabberd@localhost is started. Status: started&lt;br /&gt;ejabberd is running&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;add user &lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ ./ejabberdctl register pphetra localhost mypassword&lt;br /&gt;$ ./ejabberdclt register sugree localhost anypassword&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ทดลองใช้ IM ต่อเข้ามา โดยระบุตัวตนเป็น pphetra &lt;br /&gt;จากนั้นก็ add contact rabbiter@rabbiter.localhost&lt;br /&gt;ลองสั่ง &lt;code&gt;*help&lt;/code&gt; ก็จะได้ message แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;To give me a command, choose one from the list below. Make sure you prefix it with '*', otherwise I'll interpret it as something you want to send to your group of followers. You can say '*help (command)' to get details on a command you're interested in. If you want to actually say 'help', rather than asking for help, type '*say help' instead.&lt;br /&gt;["*help","*follow","*unfollow","*following","*followers",&lt;br /&gt; "*invite"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ลองสั่ง &lt;code&gt;*follow sugree@localhost&lt;/code&gt; ก็จะได้รับ message แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;You are now following sugree@localhost.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: user sugree ต้องเคย logon ก่อนด้วย IM client ไม่งั้น จะได้ error เป็นแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;I don't know that person. Perhaps you could invite them to get in touch?&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Note: ตอนทดสอบต้องระวังประเด็นที่ IM client แอบแก้ message ของเรา เช่น pidgin บน linux, ที่เวลาเราสั่ง &lt;code&gt;*follow x@y&lt;/code&gt; มันจะแอบเปลี่ยนเป็น &lt;code&gt;*follow x@y &amp;lt;mailto:x@y&amp;gt;&lt;/code&gt; ซึ่งส่งผลให้ rabbiter ไม่รู้จักคำสั่งนี้&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-325386934737274546?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/325386934737274546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=325386934737274546' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/325386934737274546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/325386934737274546'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/08/rabbiter.html' title='rabbiter'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2852604860956651455</id><published>2008-08-22T13:19:00.003+07:00</published><updated>2008-08-22T13:26:05.602+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Specifying revisions</title><content type='html'>หลายคนคงเคยผ่านตากับการอ้างถึง parent ของ branch แบบนี้ (เครื่องหมาย ^)&lt;br /&gt;&lt;pre class="hl"&gt;git checkout HEAD^^&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ถ้าไปเปิด man page ของ git-rev-parse จะพบ diagram ที่ช่วยให้เราเข้าใจถึงการใช้ &lt;code&gt;^&lt;/code&gt; &lt;br /&gt;&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;Here is an illustration, by Jon Loeliger. Both node B and C are a&lt;br /&gt;       commit parents of commit node A. Parent commits are ordered&lt;br /&gt;       left-to-right.&lt;br /&gt;       &lt;br /&gt;           G   H   I   J&lt;br /&gt;            \ /     \ /&lt;br /&gt;             D   E   F&lt;br /&gt;              \  |  / \&lt;br /&gt;               \ | /   |&lt;br /&gt;                \|/    |&lt;br /&gt;                 B     C&lt;br /&gt;                  \   /&lt;br /&gt;                   \ /&lt;br /&gt;                    A&lt;br /&gt;&lt;br /&gt;           A =      = A^0&lt;br /&gt;           B = A^   = A^1     = A~1&lt;br /&gt;           C = A^2  = A^2&lt;br /&gt;           D = A^^  = A^1^1   = A~2&lt;br /&gt;           E = B^2  = A^^2&lt;br /&gt;           F = B^3  = A^^3&lt;br /&gt;           G = A^^^ = A^1^1^1 = A~3&lt;br /&gt;           H = D^2  = B^^2    = A^^^2  = A~2^2&lt;br /&gt;           I = F^   = B^3^    = A^^3^&lt;br /&gt;           J = F^2  = B^3^2   = A^^3^2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: tree มันกลับหัว, A คือ ยอดบนสุดของ ที่เกิดจากการ merge B กับ C&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2852604860956651455?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2852604860956651455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2852604860956651455' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2852604860956651455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2852604860956651455'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/08/specifying-revisions.html' title='Specifying revisions'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3770702775647333047</id><published>2008-08-15T20:32:00.004+07:00</published><updated>2008-08-15T20:53:38.307+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='barcampbangkok'/><title type='text'>Do anything at barcamp bangkok 2</title><content type='html'>keyword ของ barcamp ก็คือ "เนื้อหา เกิดจาก ผู้เข้าร่วม"&lt;br /&gt;คนพูดไม่จำเป็นต้องเป็น expert ในสิ่งที่พูดหรอก แค่มีประสบการณ์ดีๆที่อยากแบ่งปัน ก็เพียงพอแล้ว&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/kIAe2Z2l2p0&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/kIAe2Z2l2p0&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;มาร่วมกันสร้างอนาคตใหม่ให้กับวงการ IT เมืองไทย ด้วยการเปลี่ยนจาก ผู้รับ(passive) เป็น ผู้ให้(active) กันเถอะ&lt;br /&gt;&lt;br /&gt;credits&lt;br /&gt;เด็กตีลังกา - เด็กชายปัณณ์&lt;br /&gt;เด็กห้อยหัว - เด็กชายปุณณ์&lt;br /&gt;เด็กที่คนหัวโล้นอุ้ม - เด็กหญิงนมนต์ (สะกดถูกหรือเปล่าเนี่ย)&lt;br /&gt;ผู้ชายกางเกงลาย - ผมเอง&lt;br /&gt;ผู้ชายหัวโล้น - roofimon&lt;br /&gt;คนถ่าย - keng&lt;br /&gt;สถานที่ - opendream.org&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3770702775647333047?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3770702775647333047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3770702775647333047' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3770702775647333047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3770702775647333047'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/08/do-anything-at-barcamp-bangkok-2.html' title='Do anything at barcamp bangkok 2'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5194952452984590581</id><published>2008-08-04T11:34:00.001+07:00</published><updated>2008-08-04T11:36:53.807+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>code ของ grails 's plugin</title><content type='html'>คราวก่อนที่เจอปัญหา xml-rpc บน grails, ผมได้นั่งแกะโครงสร้าง plugins ของ grails ไปแวบหนึ่ง&lt;br /&gt;รู้สึกติดใจว่า ถ้ามีเวลาต้องมานั่งแกะให้เป็นเรื่องเป็นราว&lt;br /&gt;&lt;br /&gt;วันนี้ได้ที ใน mailing list ของ th-grails-user มีปัญหาซึ่งพาดพิงถึง mail-plugin &lt;br /&gt;ก็เลยจัดการ install แล้วก็นั่งแกะ code ว่ามันทำอะไรบ้าง&lt;br /&gt;&lt;br /&gt;function หลักๆที่ mail plugin ทำก็คือ&lt;br /&gt;&lt;ul&gt;&lt;li&gt;สร้าง service ที่ชื่อ mailService เพื่อให้ controller หรือ domain เรียกใช้ตามอัธยาศัย&lt;br /&gt;โดย service นี้ จะทำหน้าที่สร้าง builder ที่จะใช้แปลง closure ที่ pass เข้ามา ให้กลายเป็น mail message&lt;br /&gt;ก่อนจะ delegate ต่อให้ mailSender bean เป็นคนจัดการส่งออกไป &lt;br /&gt;(mailSender แอบใช้ของที่ spring มีอยู่แล้ว นั่นคือ org.springframework.mail.javamail.JavaMailSenderImpl)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;สร้าง method ที่ชื่อ sendMail ให้กับ controller ทุกตัวที่มี ผ่านทางกลไกของ metdata &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;code ในส่วนของ function แรกที่น่าสนใจ ก็คือการ define bean 'mailSender'&lt;br /&gt;ลองดู code ที่มันใช้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;doWithSpring&lt;/span&gt; = {&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;config&lt;/span&gt; = application.config.grails.mail&lt;br /&gt;    mailSender(&lt;span class="type"&gt;JavaMailSenderImpl&lt;/span&gt;) {&lt;br /&gt;        host = config.host ?: &lt;span class="string"&gt;"localhost"&lt;/span&gt;&lt;br /&gt;        defaultEncoding = config.encoding ?: &lt;span class="string"&gt;"utf-8"&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt;(config.port)&lt;br /&gt;            port = config.port&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt;(config.username)&lt;br /&gt;            username = config.username&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt;(config.password)&lt;br /&gt;            password = config.password&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt;(config.protocol)&lt;br /&gt;            protocol = config.protocol&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt;(config.props &lt;span class="keyword"&gt;instanceof&lt;/span&gt; &lt;span class="type"&gt;Map&lt;/span&gt; &amp;amp;&amp;amp; config.props)&lt;br /&gt;            javaMailProperties = config.props&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;method นี้จะถูกเรียกใช้ในช่วง startup ของ grails&lt;br /&gt;ซึ่งข้างในจะเห็นว่ามันใช้ DSL ในการ config spring&lt;br /&gt;code ข้างบน ถ้าเขียนในแบบดั้งเดิมของ spring ก็จะออกมาทำนองนี้&lt;br /&gt;&lt;pre class="hl"&gt;&amp;lt;&lt;span class="function-name"&gt;bean&lt;/span&gt; &lt;span class="variable-name"&gt;id&lt;/span&gt;=&lt;span class="string"&gt;"mailSender"&lt;/span&gt; &lt;br /&gt;      &lt;span class="variable-name"&gt;class&lt;/span&gt;=&lt;span class="string"&gt;"org.springframework.mail.javamail.JavaMailSenderImpl"&lt;/span&gt;&amp;gt;&lt;br /&gt;      &amp;lt;&lt;span class="function-name"&gt;host&lt;/span&gt;&amp;gt;localhost&amp;lt;/&lt;span class="function-name"&gt;host&lt;/span&gt;&amp;gt;&lt;br /&gt;      &amp;lt;&lt;span class="function-name"&gt;defaultEncoding&lt;/span&gt;&amp;gt;utf-8&amp;lt;/&lt;span class="function-name"&gt;defaultEncoding&lt;/span&gt;&amp;gt;&lt;br /&gt;      ...&lt;br /&gt;&amp;lt;/&lt;span class="function-name"&gt;bean&lt;/span&gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;ข้อได้เปรียบที่เห็นได้ชัด สำหรับการใช้ DSL ในการ config spring ก็คือ&lt;br /&gt;เราสามารถใส่ logic ลงไปได้ (เพราะมันเป็น code ไม่ใช่ xml)&lt;br /&gt;&lt;br /&gt;ในส่วน function ที่สอง ที่มีการ inject method ให้กับทุก controller &lt;br /&gt;ถ้าดูตาม code แล้วจะเห็นว่ามันทำแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;doWithApplicationContext&lt;/span&gt; = { applicationContext -&amp;gt;&lt;br /&gt;    configureSendMail(application, applicationContext)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;configureSendMail&lt;/span&gt;(application, applicationContext) {&lt;br /&gt;    application.controllerClasses*.metaClass*.sendMail = {&lt;span class="type"&gt;Closure&lt;/span&gt; callable -&amp;gt;&lt;br /&gt;        applicationContext.mailService?.sendMail(callable)&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;code กระทัดรัดมาก มันแค่ส่งต่อ closure ที่รับเข้ามาให้กับ mailService &lt;br /&gt;ถ้าเป็น java code ก็ต้องยืดยาวประมาณนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="type"&gt;MailService&lt;/span&gt; &lt;span class="variable-name"&gt;svr&lt;/span&gt; = (&lt;span class="type"&gt;MailService&lt;/span&gt;) ctx.getBean(&lt;span class="string"&gt;'mailService'&lt;/span&gt;);&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; (svr != &lt;span class="constant"&gt;null&lt;/span&gt;) {&lt;br /&gt;    svr.sendMail(...);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5194952452984590581?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5194952452984590581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5194952452984590581' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5194952452984590581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5194952452984590581'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/08/code-grails-s-plugin.html' title='code ของ grails &apos;s plugin'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6208687264024846140</id><published>2008-07-22T15:24:00.002+07:00</published><updated>2008-07-22T16:22:24.254+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xmlrpc'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><title type='text'>ปัญหา grails กับ xml-rpc</title><content type='html'>ใน open source project ที่กำลังทำอยู่ (ใช้ grails)&lt;br /&gt;มันมี feature หนึ่งต้องสามารถรับ request เป็น xml-rpc ได้ &lt;br /&gt;&lt;br /&gt;จัดการเคาะ google หนึ่งที ก็ได้ความว่ามี plugin xmlrpc ให้ใช้&lt;br /&gt;จัดการสั่ง &lt;code&gt;grails install-plugin xmlrpc&lt;/code&gt;&lt;br /&gt;implement service, controller อีก ~20 บรรทัด ก็ได้ xml-rpc ฝั่ง server มาเชยชม&lt;br /&gt;&lt;br /&gt;จากนั้นก็ทดลองเขียน xml-rpc client ด้วย python&lt;br /&gt;ทดลอง run ดูก็พบว่าโลกไม่สวยงามอย่างที่คิด&lt;br /&gt;มี stack trace ยอดยืดเป็นหางว่าว&lt;br /&gt;debug พักใหญ่ๆ ก็ได้ความว่า มันเป็น bug ตัวนี้&lt;br /&gt;&lt;br /&gt;&lt;a href='http://jira.codehaus.org/browse/GRAILSPLUGINS-201'&gt;http://jira.codehaus.org/browse/GRAILSPLUGINS-201&lt;/a&gt;&lt;br /&gt;&lt;a href='http://jira.codehaus.org/browse/GRAILS-2017'&gt;http://jira.codehaus.org/browse/GRAILS-2017&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;อืมม์ fixed โดยการ disable feature หนึ่งทิ้งไป&lt;br /&gt;แสดงว่าเราต้องเลือกว่า อยากได้ feature ไหนมากกว่ากัน&lt;br /&gt;ในเบื้องต้นเนื่องจาก xmlrpc ผมยังเป็น rpc ตัวน้อยๆอยู่&lt;br /&gt;ผมก็เลยเลือกว่าจะไม่ใช้ xmlrpc plugin (ซึ่งหมายความว่าต้อง implement xmlrpc แบบง่ายๆด้วยตัวเอง)&lt;br /&gt;&lt;br /&gt;วิธีการง่ายๆแบบนี้เลย &lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;RpcController&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;pingService&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;ping&lt;/span&gt; = {&lt;br /&gt;        &lt;span class="string"&gt;//&lt;/span&gt; &amp;#3605;&amp;#3619;&amp;#3591;&amp;#3609;&amp;#3637;&amp;#3657; grails &amp;#3617;&amp;#3633;&amp;#3609;&amp;#3592;&amp;#3633;&amp;#3604;&amp;#3585;&amp;#3634;&amp;#3619;&amp;#3649;&amp;#3611;&amp;#3621;&amp;#3591; xml &amp;#3607;&amp;#3637;&amp;#3656;&amp;#3626;&amp;#3656;&amp;#3591;&amp;#3617;&amp;#3634;&amp;#3651;&amp;#3609; request body &lt;br /&gt;        &lt;span class="string"&gt;//&lt;/span&gt; &amp;#3651;&amp;#3627;&amp;#3657;&amp;#3629;&amp;#3618;&amp;#3641;&amp;#3656;&amp;#3651;&amp;#3609; params object&lt;br /&gt;        &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;method&lt;/span&gt; = params.methodCall&lt;br /&gt;        &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;methodName&lt;/span&gt; = method.methodName&lt;br /&gt;        &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;methodParams&lt;/span&gt; = method.params&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;result&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (methodName == &lt;span class="string"&gt;'ping'&lt;/span&gt;) {&lt;br /&gt;            result = pingService.ping(methodParams)&lt;br /&gt;        } &lt;span class="keyword"&gt;else&lt;/span&gt; {&lt;br /&gt;            result = &lt;span class="string"&gt;"error: mismatch methodName ${methodName}, expect 'ping'"&lt;/span&gt;&lt;br /&gt;        }&lt;br /&gt;               &lt;br /&gt;        &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;writer&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;StringWriter&lt;/span&gt;()&lt;br /&gt;        &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;mkb&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; groovy.xml.&lt;span class="type"&gt;MarkupBuilder&lt;/span&gt;(writer)&lt;br /&gt;        &lt;br /&gt;        mkb.methodResponse {&lt;br /&gt;            params {&lt;br /&gt;                param {&lt;br /&gt;                    value {&lt;br /&gt;                        string(result)&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        render(writer.toString())&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update: พบปัญหาว่า กรณีมี params มากกว่า 1 ตัวแล้ว ดูเหมือนมันจะ parse ให้ผิด&lt;br /&gt;ตอนนี้เลย switch ไปใช้ xml-rpc plugin แล้วทำการ disable auto xml parsing ไปก่อน&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6208687264024846140?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6208687264024846140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6208687264024846140' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6208687264024846140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6208687264024846140'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/07/grails-xml-rpc.html' title='ปัญหา grails กับ xml-rpc'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3211725055250199531</id><published>2008-07-16T13:43:00.002+07:00</published><updated>2008-07-16T13:45:16.211+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>เริ่มใช้ grails</title><content type='html'>ช่วงเดือนที่ผ่านมา ผมได้มีโอกาสสัมผัสกับ Grails อย่างจริงๆจังเป็นครั้งแรก&lt;br /&gt;โดยได้มีโอกาส implement open source project ตัวหนึ่งด้วย Grails&lt;br /&gt;&lt;br /&gt;อารมณ์ในช่วงแรก ก็คือ "wow"&lt;br /&gt;สาเหตุก็คือ มันใช้ stack ทุกอย่างที่ผมคุ้นเคย และใช้อยู่แล้ว&lt;br /&gt;ไม่ว่าจะเป็น spring, hibernate&lt;br /&gt;แต่นำมาลด noise ด้วยการทำ DSL บ้าง หรือลด noise ด้วยคุณสมบัติของตัวภาษา groovy เองบ้าง&lt;br /&gt;แถมยังยึดแนว convention over configuration ของ Rails อีก&lt;br /&gt;learning curve ก็เลยถือว่าน้อยมากๆ&lt;br /&gt;&lt;br /&gt;พอผ่านอารมณ์ wow มาได้&lt;br /&gt;ก็ได้เวลาขัดอกขัดใจบ้างแล้ว&lt;br /&gt;เริ่มแรกสุด ก็คือ ผมจำชื่อ package ของ java class ที่ใช้บ่อยๆไม่ได้&lt;br /&gt;เดิมผมผลักภาระไปให้ IDE มัน popup หรือ import ให้เรา&lt;br /&gt;แต่พอมาใช้ groovy, เจ้า context assistent ของ groovy ใน eclipse มันทำงานช้าเหลือเกิน&lt;br /&gt;&lt;br /&gt;ความขัดใจที่สองก็คือมัน compile ช้า&lt;br /&gt;เวลาเขียน unit testing เพื่อทดสอบหาแนวทางการทำงานของ library ต่างๆ (เช่น lucene, nekohtml)&lt;br /&gt;การ run แต่ละครั้งมันหนึืดเหลือเกิน&lt;br /&gt;&lt;br /&gt;แต่สิ่งที่ผมยึดถือก็คือ คนเราปรับตัวได้&lt;br /&gt;อย่าปล่อยให้ความลำบากเล็กๆน้อยๆ (จากความไม่เคยชินของเรา) มาเป็นอุปสรรคในการเรียนรู้&lt;br /&gt;&lt;br /&gt;จากข้อ 1 ที่ไหนๆ groovy plugin มันไม่ได้ช่วยอะไรเรา (แถมยังขัดขาอีก)&lt;br /&gt;ก็เลยเปลี่ยนไปใช้ emacs แทน (ซึ่งไม่มี context assistent แน่ๆ)&lt;br /&gt;แล้วก็เปิด javadoc ไว้ข้างๆ เพื่อใช้ค้นหาชื่อ package&lt;br /&gt;แต่ก็พบว่า ถ้าเราเขียนโปรแกรมแบบไม่ระบุ type มันก็จะช่วยลด import statement ไปได้เยอะเหมือนกัน&lt;br /&gt;&lt;br /&gt;ส่วนข้อสอง ก็แก้โดย ใช้ groovy console ทดลองเขียนให้เรียบร้อยก่อน&lt;br /&gt;จากนั้นค่อย copy ไปใส่ไว้ใน unit test.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3211725055250199531?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3211725055250199531/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3211725055250199531' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3211725055250199531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3211725055250199531'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/07/grails.html' title='เริ่มใช้ grails'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7633565242951002502</id><published>2008-07-15T13:54:00.001+07:00</published><updated>2008-07-15T13:55:59.923+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>kernel stat</title><content type='html'>เมื่อวานนั่งฟัง Bart Trojanowski &lt;a href='http://video.google.com/videoplay?docid=-8849863414000120231&amp;q=linux+kernel&amp;ei=GEl8SO22GIXCwgPxotXNBA&amp;hl=en'&gt;พูดเรื่อง "Kernel walkthrough"&lt;/a&gt;&lt;br /&gt;ฟังได้ไม่ถึงสิบนาที ภรรยาก็บอกว่า "ปิดเถอะ ได้ยินแล้วนอนไม่หลับ ไปซื้อหูฟังกันไหม?"&lt;br /&gt;หน้าที่ของสามีที่ดีก็คือ งอนแต่พองาม &lt;br /&gt;สบัดหน้าเม้มปากกัดกรามแล้วก็ปิดแต่โดยดี&lt;br /&gt;&lt;br /&gt;ถึงได้ฟังนิดหน่อย แต่ก็ได้เห็น stat บางอย่างของ Linux kernel ที่น่าสนใจ&lt;br /&gt;(ตัวเลขจริงๆจำไม่ได้แล้ว แต่วันนี้เจอ blog ที่พูดถึงเรื่องนี้เหมือนกัน อยู่ที่&lt;br /&gt; &lt;a href='http://cycle-gap.blogspot.com/2008/07/linux-kernel-development-stats-from.html'&gt;http://cycle-gap.blogspot.com/2008/07/linux-kernel-development-stats-from.html&lt;/a&gt;&lt;br /&gt; ก็เลยลอกมาให้นั่งดู ประหยัดแรง ไม่ต้องไปนั่งถอดเทป&lt;br /&gt; )&lt;br /&gt;&lt;br /&gt;จำนวน changes ต่อวัน&lt;br /&gt;&lt;ul&gt;&lt;li&gt;เพิ่มใหม่ 4300 บรรทัด&lt;br /&gt;&lt;/li&gt;&lt;li&gt;แก้ไข 1800 บรรทัด&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ลบทิ้ง 1500 บรรทัด&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;ใน code จำนวน 9 ล้านบรรทัดของ kernel เป็น core แค่ 5%&lt;br /&gt;ตัวที่มีจำนวนบรรทัดมากสุดคือ driver ล่อไปซะ 55 %&lt;br /&gt;&lt;br /&gt;ฟังได้แค่นี้แหล่ะ ไว้ได้หูฟังมาแล้ว ค่อยฟังต่อ&lt;br /&gt;แต่ตัวเลขน่าสนใจมาก (ใครไม่อยากเสียเวลาฟัง ก็ไปแอบดูใน blog ข้างบนก่อนได้เลย)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7633565242951002502?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7633565242951002502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7633565242951002502' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7633565242951002502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7633565242951002502'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/07/kernel-stat.html' title='kernel stat'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1318827113393546891</id><published>2008-07-08T22:27:00.003+07:00</published><updated>2008-12-11T15:17:47.804+07:00</updated><title type='text'>เปิดท้าย</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/SHOIHKhf3OI/AAAAAAAAAQk/6suGlH8pJ4w/s1600-h/playgroup.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/SHOIHKhf3OI/AAAAAAAAAQk/6suGlH8pJ4w/s320/playgroup.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5220666049717853410" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ผมเป็นคนที่ชอบเล่นกับเด็กเล็ก เมื่อก่อนสมัยเรียนหนังสือในมหาวิทยาลัย พอได้เวลาช่วงปิดเทอมเมื่อไร  ผมก็จะไปออกค่ายสอนหนังสือเด็กๆชาวเขาที่ภาคเหนือ &lt;br /&gt;หลังจากจบมาก็ไม่ได้มีกิจกรรมข้องแวะกับเด็กๆอีกเลย จนมามีลูกเป็นของตัวเอง, เมื่อลูกเริ่มถึงวัยมีเพื่อน ความสนุกเก่าๆก็หวนกลับมาอีกครั้ง, ทุกวันนี้พอตกตอนเย็น ผมก็จะเปิดประตูและกางเสื่อบนท้ายรถรอเด็กๆในหมู่บ้านแวะเวียนมาเล่นร่วมกัน&lt;br /&gt;(รูปนี้แม่เด็กแอบขึ้นไปถ่ายตอนไหนก็ไม่รู้) &lt;br /&gt;&lt;br /&gt;ปัญหาที่พบก็คือ เด็กๆมีคละวัยเหลือเกิน เจ้าเตาะแตะก็อยากเล่นแบบหนึ่ง พวกปรู๊ดปร๊าดก็อยากเล่นอีกแบบหนึ่ง&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1318827113393546891?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1318827113393546891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1318827113393546891' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1318827113393546891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1318827113393546891'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/07/blog-post.html' title='เปิดท้าย'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_I3r3qYlfei4/SHOIHKhf3OI/AAAAAAAAAQk/6suGlH8pJ4w/s72-c/playgroup.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2392801073657544275</id><published>2008-06-24T11:28:00.001+07:00</published><updated>2008-06-24T11:31:19.112+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>git-rerere</title><content type='html'>เวลาเราแตก branch ที่เรารู้ว่าเป็นหนังชีวิต (branch ที่มีอายุยาวๆ)&lt;br /&gt;เรามักจะต้องคอย merge ตัว master เข้ามาที่ branch เรา เพื่อที่จะตรวจสอบ conflict ต่างๆที่เกิดขึ้น&lt;br /&gt;ซึ่งมีศัพท์เรียกกันว่า "test merge"&lt;br /&gt;&lt;br /&gt;ลองดูรูปประกอบข้างล่าง&lt;br /&gt;ในที่นี่ topic ก็คือ branch (ที่อายุยืน) ของเรา&lt;br /&gt;เครื่องหมาย '*' ก็คือ จุดที่ทั้ง topic และ master แตะเนื้อหาที่เดียวกัน (เป็นจุดที่เวลา merge แล้วจะเกิด conflict )&lt;br /&gt;เครื่องหมาย '+' ก็คือ test merge ที่เกิดขึ้น (และได้ทำการ solve conflict ไว้แล้ว)&lt;br /&gt;เมื่อเรา merge topic กลับไปยัง master, &lt;br /&gt;commit ของ test merge ก็จะถูกเก็บไว้ใน history ของ master ไปด้วย&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;        $ git checkout topic&lt;br /&gt;        $ git merge master&lt;br /&gt;        $ ... work on both topic and master branches&lt;br /&gt;        $ git checkout master&lt;br /&gt;        $ git merge topic&lt;br /&gt;&lt;br /&gt;              o---*---o---+---o---o topic&lt;br /&gt;             /           /         \&lt;br /&gt;    o---o---o---*---o---o---o---o---+ master&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: รูปประกอบตัดมาจาก document ของ git &lt;br /&gt;&lt;br /&gt;สำหรับบางคนประเด็นนี้ถือเป็นประเด็นที่สำคัญ&lt;br /&gt;เช่น linus เขาเรียกเจ้า "test merge" นี้ว่า "useless merge"&lt;br /&gt;และ&lt;a href='http://all-thing.net/2008/06/many-styles-of-git.html'&gt;เห็นว่ากันว่า&lt;/a&gt; เขามักจะ reject เจ้า merge branch ที่มีเจ้า "test merge" ติดเข้ามาด้วย&lt;br /&gt;&lt;br /&gt;แล้วทำอย่างไร ถึงจะไม่ให้มี test-merge ติดเข้ามาหล่ะ&lt;br /&gt;วิธีที่เขาทำกันก็คือ &lt;br /&gt;ยังทำ test-merge เหมือนเดิม แต่เมื่อ merge เสร็จแล้ว ก็จัดการลบ commit นั้นทิ้งเสีย ด้วยคำสั่ง &lt;code&gt;git reset --hard HEAD^&lt;/code&gt;&lt;br /&gt;ผลที่ได้ก็คือ เวลา merge topic กลับเข้า master&lt;br /&gt;เราก็จะได้ tree หน้าตาเกลี้ยงเกลาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;              o---*---o-------o---o topic&lt;br /&gt;             /                     \&lt;br /&gt;    o---o---o---*---o---o---o---o---+ master&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;แต่ชีวิตไม่ได้โรยด้วยกลีบกุหลาบ&lt;br /&gt;ปัญหาที่ตามมาก็คือ ไอ้เจ้าพวก conflict ที่เราเคย solve ตอน test merge&lt;br /&gt;มันจะตามกลับมาให้เราแก้ไขอีกครั้งตอน merge topic กลับไปยัง master&lt;br /&gt;ซึ่งการแก้ไขสองรอบแบบนี้ไม่สนุกแน่ๆ&lt;br /&gt;&lt;br /&gt;โชคดีที่ git มีทางออกให้เรา &lt;br /&gt;คำสั่งที่เป็นพระเอกก็คือคำสั่ง &lt;a href='http://www.kernel.org/pub/software/scm/git/docs/git-rerere.html'&gt;git-rerere&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;หลังจาก merge แล้วเกิด conflict ขึ้น&lt;br /&gt;ให้สั่ง git-rerere ก่อนที่จะแก้ไข conflict&lt;br /&gt;และหลังจากแก้ไข conflict เสร็จแล้ว ก็ให้สั่ง git-rerere อีกที&lt;br /&gt;ผลก็คือ เจ้า git จะแอบจำวิธี solve conflict ของเราไว้&lt;br /&gt;ทำให้ครั้งต่อไปที่เกิด conflict แบบนี้ขึ้นมาอีก (test merge ครั้งถัดไป หรือตอน merge topic กลับ master)&lt;br /&gt;มันจะจัดการ resolve ให้โดยอัติโนมัติ&lt;br /&gt;&lt;br /&gt;โดย default แล้ว git-rerere ไม่ได้ถูก enable ไว้&lt;br /&gt;เราสามารถสั่ง enable feature นี้โดยการเข้าไปสร้าง directory rr-cache ไว้ใต้ .git directory&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2392801073657544275?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2392801073657544275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2392801073657544275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2392801073657544275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2392801073657544275'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/06/git-rerere.html' title='git-rerere'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6543058486357510102</id><published>2008-06-20T11:25:00.002+07:00</published><updated>2008-06-20T11:29:44.391+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Java, Parallel, Monad</title><content type='html'>เห็น code java นี้แล้ว ธาตุไฟแทรก&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;static&lt;/span&gt; &amp;lt;A, B&amp;gt; F&amp;lt;Callable&amp;lt;A&amp;gt;, Callable&amp;lt;B&amp;gt;&amp;gt; fmap(&lt;span class="keyword"&gt;final&lt;/span&gt; &lt;span class="type"&gt;F&lt;/span&gt;&amp;lt;A, B&amp;gt; f) {&lt;br /&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;F&lt;/span&gt;&amp;lt;Callable&amp;lt;A&amp;gt;, Callable&amp;lt;B&amp;gt;&amp;gt;() {&lt;br /&gt;      &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;Callable&lt;/span&gt;&amp;lt;B&amp;gt; f(&lt;span class="keyword"&gt;final&lt;/span&gt; &lt;span class="type"&gt;Callable&lt;/span&gt;&amp;lt;A&amp;gt; a) {&lt;br /&gt;        &lt;span class="keyword"&gt;return&lt;/span&gt; bind(a, &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;F&lt;/span&gt;&amp;lt;A, Callable&amp;lt;B&amp;gt;&amp;gt;() {&lt;br /&gt;          &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;Callable&lt;/span&gt;&amp;lt;B&amp;gt; f(&lt;span class="keyword"&gt;final&lt;/span&gt; &lt;span class="type"&gt;A&lt;/span&gt; &lt;span class="variable-name"&gt;ab&lt;/span&gt;) {&lt;br /&gt;            &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Callable&lt;/span&gt;&amp;lt;B&amp;gt;() {&lt;br /&gt;              &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;B&lt;/span&gt; &lt;span class="variable-name"&gt;call&lt;/span&gt;() {&lt;br /&gt;                &lt;span class="keyword"&gt;return&lt;/span&gt; f.f(ab);&lt;br /&gt;              }&lt;br /&gt;            };&lt;br /&gt;          }&lt;br /&gt;        });&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ใครธาตุแข็งแรงเชิญไปอ่านได้ที่นี่&lt;br /&gt;&lt;br /&gt;&lt;a href="http://apocalisp.wordpress.com/2008/06/18/parallel-strategies-and-the-callable-monad/"&gt;Higher-Order Java Parallelism, Part 1: Parallel Strategies and the Callable Monad&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6543058486357510102?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6543058486357510102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6543058486357510102' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6543058486357510102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6543058486357510102'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/06/java-parallel-monad.html' title='Java, Parallel, Monad'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1768389773052852235</id><published>2008-06-11T15:07:00.004+07:00</published><updated>2008-06-11T15:19:23.900+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='orangegears'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='ofbiz'/><title type='text'>merge อย่างไรดี</title><content type='html'>ปัญหาหนึ่งที่ Orangegears เจอก็คือ เนื่องจากเรา fork ออกมาจาก Ofbiz&lt;br /&gt;แต่ก็ยังต้องการ sync กับ Ofbiz อย่างไกล้ชิด (ไม่เหมือน Opentap ที่แยกออกไปอย่างชัดเจน)&lt;br /&gt;ทำให้เราต้องคอย merge code จาก Ofbiz เข้ามายัง Orangegears เป็นระยะๆ&lt;br /&gt;&lt;br /&gt;วิธีการเดิมที่น้อง sand ใช้ ก็คือใช้ kdiff3 ทำการ merge&lt;br /&gt;ซึ่งก็สะดวกดี เพราะมี UI สวยงาม&lt;br /&gt;แต่ผมก็เจอปัญหาว่า changed ของผมมักจะหายไปบ่อยๆ&lt;br /&gt;ผมก็เลยมองหาวิธีใหม่ๆมาเรื่อยๆ&lt;br /&gt;&lt;br /&gt;วันก่อนหลังจากลองเล่น multiple branches ใน git ดู&lt;br /&gt;ก็พบว่า git ยืดหยุ่นพอที่เราจะทำ multiple remote branch จาก svn repository มากกว่า 1 ที่ได้&lt;br /&gt;ผมก็เลยทดลอง merge ด้วยวิธีนี้ดู&lt;br /&gt;&lt;br /&gt;วิธีการก็คือ&lt;br /&gt;เริ่มด้วยการสร้าง git repository ที่มี link ชี้ไปยัง project ofbiz ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git svn init http://svn.apache.org/repos/asf/ofbiz/trunk&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;หลังจากสั่งคำสั่งนี้ git จะสร้าง working directory เปล่าๆให้ (ยังไม่ fetch code มาให้)&lt;br /&gt;โดย file .git/config จะมีหน้าตาแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;[core]&lt;br /&gt;    repositoryformatversion = 0&lt;br /&gt;    filemode = true&lt;br /&gt;    bare = false&lt;br /&gt;    logallrefupdates = true&lt;br /&gt;[svn-remote "svn"]&lt;br /&gt;    url = http://svn.apache.org/repos/asf/ofbiz/trunk&lt;br /&gt;    fetch = :refs/remotes/trunk&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เราก็ทำการจัดแจงแก้ไขชื่อให้เหมาะสม จะได้ไม่งงภายหลัง&lt;br /&gt;และทำการเพิ่ม repository ของ Ofbiz เข้าไป&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;[svn-remote "ofbiz"]&lt;br /&gt;        url = http://svn.apache.org/repos/asf/ofbiz/trunk&lt;br /&gt;        fetch = :refs/remotes/ofbiz_trunk&lt;br /&gt;[svn-remote "orangegears"]&lt;br /&gt;        url = https://orangegears.svn.sourceforge.net/svnroot/orangegears&lt;br /&gt;        fetch = trunk:refs/remotes/og_trunk&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จากนั้นก็ทำการ fetch ข้อมูลทีละ repository&lt;br /&gt;เริ่มจาก ofbiz ก่อน เอา revision ล่าสุดเลย ไม่เอา history&lt;br /&gt;Note1: เลข revision ของ ofbiz นี่ล่อไปหลักหกแสนแล้ว&lt;br /&gt;ทั้งนี้เพราะ repo ของ apache เขาใช้ share ร่วมกันทุก project &lt;br /&gt;Note2: ผมลอง fetch orangegears ก่อน ปรากฎว่า fetch ofbiz ไม่ได้ (ไม่มี error ด้วย)&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git svn fetch -r 666189:HEAD ofbiz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ตามด้วย orangegears โดยเริ่มต้นที่ revision 142 เลย ไม่ต้องย้อนอดีตมากนัก&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git svn fetch -r 142:HEAD orangegears&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ลองสั่ง git branch -r จะเห็นว่ามี og_trunk กับ ofbiz_trunk เกิดขึ้น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;pphetra@mypann:~/projects/java/t$ git branch -r&lt;br /&gt;  ofbiz_trunk&lt;br /&gt;  og_trunk&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;โดย default trunk ของ ofbiz จะถูก map เข้ามาเป็น master ใน local branch&lt;br /&gt;ทำการเปลี่ยนชื่อให้เรียบร้อย จะได้ไม่งงว่าใครเป็นใคร&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git branch -m master ofbiz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ส่วน og_trunk นั้นยังไม่มี local branch ต้องทำการสั่ง &lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git checkout -b og og_trunk &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ขั้นตอนการ merge&lt;br /&gt;fetch code จาก ofbiz ให้ up-to-date ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git checkout -f ofbiz&lt;br /&gt;git svn rebase&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;fetch code ของ orangegears ให้ up-to-date เช่นเดียวกัน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git checkout -f og&lt;br /&gt;git svn rebase&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เตรียม branch สำหรับ merge โดยสร้าง branch แยกออกจาก og (ไม่จำเป็น แต่ก็ควรทำเป็นนิสัย)&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git checkout -b merge-ofbiz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;สั่ง merge&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git merge ofbiz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ที่ยากก็คือเวลาเกิด conflict ก็ต้องตามแก้ไขให้เรียบร้อยก่อน&lt;br /&gt;Note1: สำหรับผม ผมเลือกใช้ emacs + &lt;a href="http://tsgates.cafe24.com/git/git-emacs.html"&gt;git-emacs&lt;/a&gt; mode ซึ่งมันจะเรียกใช้ &lt;a href="http://www.delorie.com/gnu/docs/emacs/ediff_18.html"&gt;ediff&lt;/a&gt; ในการ solve conflict&lt;br /&gt;Note2: คิดว่าการ merge ครั้งแรก จะมี conflict เยอะหน่อย&lt;br /&gt;แต่พอเป็นการ merge ครั้งที่ 2.. จะมี conflict น้อยลง เพราะว่า branch ทั้งสองมี history ที่เชื่อมกันแล้ว&lt;br /&gt;(เป็นผลดีต่อ three-way merge) &lt;br /&gt;&lt;br /&gt;switch กลับไปยัง og และทำการ merge&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git checkout -f og&lt;br /&gt;git merge merge-ofbiz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จากนั้นก็สั่ง commit กลับขึ้น svn repository&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git svn dcommit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;สุดท้ายก็ลบ temporary branch ทิ้ง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;git branch -D merge-ofbiz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ไว้จะทดลองทำสักระยะหนึ่ง (โดยยังไม่ commit ผลลัพท์จากการ merge กลับขึ้น orangegears)&lt;br /&gt;เพื่อดูว่ามันมีประสิทธิภาพแค่ไหนก่อน&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1768389773052852235?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1768389773052852235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1768389773052852235' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1768389773052852235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1768389773052852235'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/06/merge.html' title='merge อย่างไรดี'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-9029852019444491231</id><published>2008-06-10T12:09:00.002+07:00</published><updated>2008-06-10T12:12:39.701+07:00</updated><title type='text'>ใช้ git จัดการ svn branches</title><content type='html'>ช่วงนี้ project ผมซึ่งใช้ subversion เป็น repository เริ่มมีการแตก branch เยอะขึ้น&lt;br /&gt;ส่งผลให้คนที่ดูหลาย branch เกิดความลำบากในการ switch ไปมาระหว่าง branch มากขึ้น&lt;br /&gt;ปกติวิธีการ switch branch เราสามารถใช้คำสั่ง &lt;code&gt;svn switch&lt;/code&gt; ได้เลย&lt;br /&gt;แต่ปัญหาก็คือมันมักจะมี code ที่กำลังแก้ค้างอยู่ จะ commit ไว้่ก่อน switch ก็ไม่เหมาะ เพราะยังแก้ไขไม่ทันเสร็จดี&lt;br /&gt;&lt;br /&gt;ผมเลือกเอา git มาช่วยจัดการปัญหานี้&lt;br /&gt;&lt;br /&gt;เริ่มแรกสุดก็คือ ในตอนที่เราสั่ง svn clone, เราต้องระบุ flag เพิ่มดังนี้&lt;br /&gt;&lt;pre class="hl"&gt;git svn clone http://your-repo -T trunk -b branches -t tags&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ผลที่ได้ ก็คือ master branch ของเราจะ map เข้ากับ trunk ของ svn&lt;br /&gt;ส่วน branches และ tags ต่างๆที่อยู่ใน subversion repository จะมีสถานะ เป็น remote branch ใน git&lt;br /&gt;&lt;pre class="hl"&gt;$ git branch&lt;br /&gt;* master&lt;br /&gt;pphetra@pann@[~/projects/java/gitwcfweb]&lt;br /&gt;$ git branch -r&lt;br /&gt;  c1001&lt;br /&gt;  j1023&lt;br /&gt;  j1027&lt;br /&gt;  j1039&lt;br /&gt;  j1041&lt;br /&gt;  trunk&lt;/pre&gt;&lt;br /&gt;สมมติว่าเราต้องการจะทำงานกับ branch j1023&lt;br /&gt;เราก็จะต้องทำการสร้าง local branch ที่ map เข้ากับ remote branch ด้วยคำสั่งนี้&lt;br /&gt;&lt;pre class="hl"&gt;git checkout -b j1023-local j1023&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ถ้ามีงานด่วนเข้ามา ระหว่างทำงาน ก็เลือกได้ว่า จะ commit เข้าไปก่อน (กลับมา revert ทีหลังได้ หรือใช้ commit --amend เพื่อรวบยอด commit ภายหลังก็ได้)&lt;br /&gt;หรือถ้าไม่อยาก commit เลย ก็อาจจะใช้ &lt;a href='http://pphetra.blogspot.com/2007/11/git-stash.html'&gt;git-stash&lt;/a&gt; แทนก็ได้&lt;br /&gt;พอ clear state ของ working copy เสร็จ ก็ให้สั่ง switch โดยใช้คำสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;git checkout -f master&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;การ update branch และ trunk ให้เท่ากับ svn repository ทำได้โดยใช้คำสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;git svn fetch&lt;/pre&gt;&lt;br /&gt;ผลของมัน ก็คือ git จะ fetch changed (ของทุกๆ branches, tags) ที่เกิดขึ้นบน svn repo มาที่ git repository&lt;br /&gt;แต่จะยังไม่ apply changed เข้ากับ local branch &lt;br /&gt;ถ้าต้องการ apply changed เข้ากับ local branch เราต้องสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;git svn rebase&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-9029852019444491231?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/9029852019444491231/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=9029852019444491231' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/9029852019444491231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/9029852019444491231'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/06/git-svn-branches.html' title='ใช้ git จัดการ svn branches'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-8821846399802473709</id><published>2008-06-04T13:35:00.002+07:00</published><updated>2008-06-04T13:42:12.823+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Structural Type</title><content type='html'>เมื่อก่อนเวลาเขียนโปรแกรมใน Java, สิ่งแรกที่นึกขึ้นมาในหัวก็คือการกำหนด Interface&lt;br /&gt;ยังขำที่โปรแกรม ruby แรกที่เขียน ก็พยายามจะ define interface ทั้งๆที่ ruby มันไม่มี interface แถมยังทำ &lt;a href='http://en.wikipedia.org/wiki/Duck_typing'&gt;duck typing&lt;/a&gt; ได้&lt;br /&gt;วันก่อนอ่านเจอ feature ของ scala ที่ชื่อ &lt;a href='http://en.wikipedia.org/wiki/Structural_type_system'&gt;Structural types&lt;/a&gt; ใน Scala&lt;br /&gt;รู้สึกว่า งามจริงๆ&lt;br /&gt;&lt;br /&gt;ดูตัวอย่างการใช้ Structural Type&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Person&lt;/span&gt; {&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;say&lt;/span&gt;():&lt;span class="type"&gt;String &lt;/span&gt;= &lt;span class="string"&gt;"hi"&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Duck&lt;/span&gt; {&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;say&lt;/span&gt;():&lt;span class="type"&gt;String &lt;/span&gt;= &lt;span class="string"&gt;"Quack"&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;object&lt;/span&gt; &lt;span class="variable-name"&gt;Test&lt;/span&gt; {&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;play&lt;/span&gt;(&lt;span class="variable-name"&gt;element&lt;/span&gt;: &lt;b&gt;&lt;span class="type"&gt;{&lt;/span&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="variable-name"&gt;say&lt;/span&gt;():&lt;span class="type"&gt;String&lt;/span&gt;}&lt;/b&gt;) = {&lt;br /&gt;    System.out.println(element.say());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;main&lt;/span&gt;(&lt;span class="variable-name"&gt;args&lt;/span&gt; : &lt;span class="type"&gt;Array[String]&lt;/span&gt;) : &lt;span class="type"&gt;Unit &lt;/span&gt;= {&lt;br /&gt;    play(&lt;span class="keyword"&gt;new&lt;/span&gt; Person())&lt;br /&gt;    play(&lt;span class="keyword"&gt;new&lt;/span&gt; Duck())&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note: สำหรับคนที่ไม่คุ้นกับ Scala&lt;br /&gt;def คือการกำหนด method, เครื่องหมาย : คือการกำหนด type&lt;br /&gt;กรณี code ข้างล่างนี้, method say มี return type เป็น String&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;say&lt;/span&gt;():&lt;span class="type"&gt;String &lt;/span&gt;= &lt;span class="string"&gt;"Quack"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;ส่วน &lt;code&gt;Object {...}&lt;/code&gt; ก็คือ Singleton Object&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-8821846399802473709?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/8821846399802473709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=8821846399802473709' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8821846399802473709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8821846399802473709'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/06/structural-type.html' title='Structural Type'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1575390145012541972</id><published>2008-06-02T11:58:00.000+07:00</published><updated>2008-06-02T12:10:37.114+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ofbiz'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Groovy in Ofbiz</title><content type='html'>น้องแซนแจ้งมาว่าขณะที่ merge source code ของ Ofbiz เข้า project Orangegears&lt;br /&gt;พบว่ามี้ groovy code โผล่เข้ามาแล้วแล้ว&lt;br /&gt;ว่าแล้วผมก็จัดแจง update source code ของ orangegears เสียหน่อย&lt;br /&gt;แล้วก็สั่ง grep -ilR groovy ดู&lt;br /&gt;ก็พบว่าเริ่มมีการ replace screen action script จากของเดิมที่เขียนด้วย bsh ไปเป็น groovy บ้างแล้ว&lt;br /&gt;แล้วก็พบว่ามีการเตรียมการใช้ groovy ใน service layer อีกด้วย (แต่ยังไม่ได้มีการ implement)&lt;br /&gt;&lt;br /&gt;ลองไล่เปรียบเทียบ syntax ของ bsh กับ groovy ดูว่า ช่วยลดรูปอะไรได้บ้าง&lt;br /&gt;&lt;br /&gt;ใน ofbiz เวลา pass parameters มักจะใช้ Map ในการ pass arguments&lt;br /&gt;พอเปลี่ยนเป็น groovy แล้วการสร้าง Map ก็เลยกระทัดรัดขึ้น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;bsh&lt;/span&gt;&lt;br /&gt;payment = delegator.findByPrimaryKey(&lt;span class="string"&gt;"Payment"&lt;/span&gt;, &lt;br /&gt;            UtilMisc.toMap(&lt;span class="string"&gt;"paymentId"&lt;/span&gt;, paymentId)));&lt;br /&gt;            &lt;br /&gt;&lt;span class="comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="comment"&gt;groovy&lt;/span&gt;&lt;br /&gt;payment = delegator.findByPrimaryKey(&lt;span class="string"&gt;"Payment"&lt;/span&gt;, [paymentId : paymentId]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;แน่นอนพวกการ iterate collections นี่ได้ประโยชน์ไปเต็มๆ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;bsh&lt;br /&gt;&lt;/span&gt;oibIter = orderItemBillings.iterator();&lt;br /&gt;&lt;span class="keyword"&gt;while&lt;/span&gt; (oibIter.hasNext()) {&lt;br /&gt;    orderIb = oibIter.next();&lt;br /&gt;    orders.add(orderIb.getString(&lt;span class="string"&gt;"orderId"&lt;/span&gt;));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="comment"&gt;groovy&lt;br /&gt;&lt;/span&gt;orderItemBillings.each &lt;span class="show-paren-match"&gt;{&lt;/span&gt; orderIb -&amp;gt;&lt;br /&gt;    orders.add(orderIb.orderId);&lt;br /&gt;&lt;span class="show-paren-match"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;การอ้างถึง value ใน Map&lt;br /&gt;ด้วย syntax sugar ของ Groovy ก็เลย สะอาดสะอ้านขึ้นแบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;bsh&lt;br /&gt;&lt;/span&gt;context.put(&lt;span class="string"&gt;"decimals"&lt;/span&gt;, decimals);&lt;br /&gt;context.put(&lt;span class="string"&gt;"rounding"&lt;/span&gt;, rounding);&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="comment"&gt;groovy&lt;br /&gt;&lt;/span&gt;context.decimals = decimals;&lt;br /&gt;context.rounding = rounding;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;การ check empty หรือ null collections ก็สบายตาขึ้น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;bsh&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; (glAccounts != &lt;span class="constant"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; glAccounts.size() &amp;gt; 0) {&lt;br /&gt;    glAccount = glAccounts.get(0);&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="comment"&gt;groovy&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; (glAccounts) {&lt;br /&gt;    glAccount = glAccounts[0];&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1575390145012541972?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1575390145012541972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1575390145012541972' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1575390145012541972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1575390145012541972'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/06/groovy-in-ofbiz.html' title='Groovy in Ofbiz'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2656891390684871738</id><published>2008-05-30T14:40:00.000+07:00</published><updated>2008-05-30T14:41:41.577+07:00</updated><title type='text'>My Pyhton 's process was Killed</title><content type='html'>ช่วงนี้กำลังทดสอบโปรแกรมที่เขียนด้วย python บนเครื่อง Production ที่ใช้ AIX เป็น OS&lt;br /&gt;หลังจาก run program ไปได้พักหนึ่ง, โปรแกรมก็หยุดทำงานพร้อมกับมีข้อความแบบนี้ต่อท้าย&lt;br /&gt;&lt;pre = "hl"&gt;&lt;br /&gt;killed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;คำถามแรกก็คือ "ใคร kill กูวะ"&lt;br /&gt;process ของน้องชิ้ม (น้องที่ทำงาน) ตกเป็นผู้ต้องสงสัยรายแรก&lt;br /&gt;(น้องชิ้มเขียน script ที่คอย monitor ว่ามี process ไหนใช้ cpu ผิดปกติบ้าง&lt;br /&gt; ถ้าเจอก็จะจัดการ kill เสีย)&lt;br /&gt;หลังจากน้องชิ้มตรวจดู log file สักพัก ก็บอกว่า "script ผมไม่ได้ทำครับพี่" &lt;br /&gt;&lt;br /&gt;ผู้ต้องสงสัยรายถัดไป ก็คือ message ข้างล่างนี้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;sem_trywait: Permission denied&lt;br /&gt;sem_wait: Permission denied&lt;br /&gt;sem_post: Permission denied&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;message นี้เกิดขึ้นในระหว่างทำงาน &lt;br /&gt;&lt;br /&gt;หลังจากค้น google ก็พบว่า kernel น่าจะเป็นคนสั่ง kill process เอง (&lt;a href='http://tinyurl.com/59e7yw'&gt;http://tinyurl.com/59e7yw&lt;/a&gt;)&lt;br /&gt;ส่วนสำเหตุก็คือ bug ตัวนี้ &lt;a href="http://bugs.python.org/issue1234"&gt;http://bugs.python.org/issue1234&lt;/a&gt;&lt;br /&gt;นัยว่าเจ้า python มันคงเรียกใช้ sem_wait มากเกินไป จน kernel มันทนไม่ได้&lt;br /&gt;&lt;br /&gt;ว่าแล้วก็รอแปะ patch และ compile python เย็นนี้&lt;br /&gt;จันทร์หน้ามารอดูผลกันใหม่&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2656891390684871738?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2656891390684871738/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2656891390684871738' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2656891390684871738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2656891390684871738'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/my-pyhton-s-process-was-killed.html' title='My Pyhton &apos;s process was Killed'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1020207413397437177</id><published>2008-05-30T11:16:00.002+07:00</published><updated>2008-05-30T11:44:47.045+07:00</updated><title type='text'>เปิดเน็ต  เปิดใจ</title><content type='html'>ที่บ้านผมไม่มีทีวี แล้วก็ไม่ได้รับหนังสือพิมพ์ ก็เลยไม่ตามข่าวการเมืองอย่างไกล้ชิด&lt;br /&gt;แต่ก็รู้ว่า ประชาธิปัตย์ ปัจจุบันแม้จะผ่านไป 50-60 ปีแล้ว ยังเล่นมุขเดิมไม่เลิก&lt;br /&gt;เรื่องที่ไกล้ตัวหน่อยก็มี กรณีนายเทพไท เสนพงศ์ ออกมาสาดโคลนใส่เว็บ &lt;br /&gt;เห็นมีมหาวิทยาลัยเที่ยงคืนรวมอยู่ด้วยแล้วอดรนทนไม่ได้&lt;br /&gt;ก็เลยต้องขอร่วมลงชื่อแสดงเสียงสักหน่อย&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://gopetition.com/online/19589.html" title="ร่วมรณรงค์ ลงชื่อ เพื่อปกป้องสิทธิเสรีภาพของเรา บนอินเทอร์เน็ตของเรา"&gt;&lt;img src="http://facthai.files.wordpress.com/2008/05/opennet-openmind.png" title="ร่วมรณรงค์ ลงชื่อ เพื่อปกป้องสิทธิเสรีภาพของเรา บนอินเทอร์เน็ตของเรา" border="0" height="90" width="160"&gt;&lt;/a&gt;&lt;br /&gt;&lt;font face="sans-serif"&gt;ปกป้องเสรีภาพของเรา&lt;br /&gt;บนอินเทอร์เน็ตของเรา&lt;br /&gt;&lt;small&gt;&lt;a href="http://gopetition.com/online/19589.html" title="ร่วมรณรงค์ ลงชื่อ เพื่อปกป้องสิทธิเสรีภาพของเรา บนอินเทอร์เน็ตของเรา"&gt;ร่วมลงชื่อในแถลงการณ์&lt;/a&gt;&lt;/small&gt;&lt;/font&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1020207413397437177?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1020207413397437177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1020207413397437177' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1020207413397437177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1020207413397437177'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/blog-post_30.html' title='เปิดเน็ต  เปิดใจ'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-8786478918245712594</id><published>2008-05-27T14:24:00.003+07:00</published><updated>2008-05-27T14:32:17.555+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Pyro</title><content type='html'>ช่วงนี้มีความจำเป็นต้องใช้ remote call บน python, ค้น google ดูก็พบเจ้า &lt;a href='http://pyro.sourceforge.net/'&gt;Pyro&lt;/a&gt;&lt;br /&gt;หลังจากอ่านหลักการแล้วก็อ๋อ เพราะทำงานแนวเดิียวกับ java RMI&lt;br /&gt;&lt;br /&gt;หลักการทำงาน&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Name Server&lt;br /&gt;เพื่อให้ client สามารถ lookup service ได้ง่ายๆ, Pyro ก็เลย provide Name Server มาให้ด้วย&lt;br /&gt;เจ้า name server ที่ pyro ให้มา มันแบ่งออกเป็น 2 ประเภทคือ&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Regular, non-persistent&lt;br /&gt;ตัวนี้พอ name server process ตาย, ค่าต่างๆใน registry ก็หายไปด้วย&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Persistent&lt;br /&gt;ค่าต่างๆใน registry จะเก็บไว้บน disk&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;เพื่อเพิ่ม availability, เจ้าตัว name server นี้ ยังสามารถ start ในแบบ Paired mode ได้อีกด้วย โดยทั้งคู่จะทำ replicate registry ซึ่งกันและกันไว้&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Client&lt;br /&gt;การที่ client จะติดต่อ server ได้นั้น ขั้นแรกก็ต้องหา Name Server ให้เจอเสียก่อน&lt;br /&gt;วิธีการหา Name Server ก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="variable-name"&gt;locator&lt;/span&gt; = Pyro.naming.NameServerLocator()&lt;br /&gt;&lt;span class="variable-name"&gt;ns&lt;/span&gt; = locator.getNS()&lt;/pre&gt;&lt;br /&gt;ขั้นตอนการทำงานภายในก็คือ มันจะทำการ broadcast message ออกไป&lt;br /&gt;เจ้า Name server (ซึ่ง implement broadcast listener) ก็จะตอบกลับมา&lt;br /&gt;&lt;br /&gt;Note: กรณีของผม ผมลองใช้ boardcast บน production server แล้ว, message มันหายต๋อมไป (ด้วยความขี้เกียจไปไล่หาสาเหตุ)&lt;br /&gt;ผมก็เลย config ให้ NameServerLocator  มันต่อตรงไปที่ Name Server โดยตรงเลย แทนที่จะ broadcast&lt;br /&gt;การ config ให้ต่อตรงทำได้โดย&lt;br /&gt;&lt;pre class="hl"&gt;Pyro.config.PYRO_NS_HOSTNAME=&lt;span class="string"&gt;'YOUR_HOSTNAME'&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;หลังจากหา Name Server เจอแล้ว ขั้นถัดไปก็คือ ทำการถามหา URI ของ server ที่ต้องการ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="variable-name"&gt;uri&lt;/span&gt; = ns.resolve(&lt;span class="string"&gt;'my_service'&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;เมื่อได้ uri แล้ว สุดท้ายก็คือทำการสร้าง proxy object ซึ่งเจ้า Pyro แบ่งประเภท Proxy ออกเป็น 2 แบบคือ&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Proxy ธรรมดา&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Proxy ที่สามารถ access attribute ของ remote object ได้ด้วย&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;วิธีการสร้าง proxy ก็คือ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="variable-name"&gt;obj&lt;/span&gt; = uri.getProxy()&lt;br /&gt;&lt;span class="comment"&gt;# or&lt;br /&gt;&lt;/span&gt;&lt;span class="variable-name"&gt;obj&lt;/span&gt; = uri.getAttrProxy()&lt;/pre&gt;&lt;br /&gt;ที่เหลือ ก็เป็นการเรียกใช้ method ต่างๆบน remote object ที่เราได้มา&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Server&lt;br /&gt;การทำงานฝั่ง server เริ่มต้นด้วยการสร้าง daemon object &lt;br /&gt;(มี parameter เป็น name server object ซึ่ง lookup มาด้วยวิธีเดียวกันกับ client)&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="variable-name"&gt;daemon&lt;/span&gt; = Pyro.core.Daemon()&lt;br /&gt;daemon.useNameServer(ns)&lt;/pre&gt;&lt;br /&gt;ข้อควรระวังก็คือ ต้องระวังเรื่อง reference ถึง object daemon ของเรา&lt;br /&gt;มิเช่นนั้น garbage collection อาจกวาด daemon เราทิ้งไปได้&lt;br /&gt;&lt;br /&gt;หลังจากได้ daemon มา ก็ทำการสร้าง Object instance ที่ต้องการให้ client ต่อใช้&lt;br /&gt;เนื่องจาก pyro ต้องหลอก object ของเราว่ามันทำงานเหมือนกับอยู่ในสภาพแวดล้อมแบบ stand-alone&lt;br /&gt;ดังนั้น Remote Object ของเราก็เลยต้องถูกห่อไว้ใน Pyro.core.ObjBase&lt;br /&gt;ซึ่งเราสามารถทำได้ 3 วิธีคือ&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ให้ Remote class ของเรา extend Pyro.core.ObjBase ตรงๆเลย &lt;br /&gt;&lt;/li&gt;&lt;li&gt;delegate pattern &lt;br /&gt;บางครั้งเราอยากเขียน Remote Class โดยไม่อยากให้มี dependency ถึง Pyro เลย &lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="variable-name"&gt;obj&lt;/span&gt; = Pyro.core.ObjBase()&lt;br /&gt;&lt;span class="variable-name"&gt;myservice&lt;/span&gt; = MyService()&lt;br /&gt;obj.delegateTo(myservice)&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;สร้าง class ใหม่ที่ extend ทั้ง Pyro.core.ObjBase และ Service class ของเรา&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;ServiceImpl&lt;/span&gt;(Pyro.core.ObjBase, MyService):&lt;br /&gt;  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;__init__&lt;/span&gt;(&lt;span class="keyword"&gt;self&lt;/span&gt;):&lt;br /&gt;    Pyro.core.ObjBase.__init__(&lt;span class="keyword"&gt;self&lt;/span&gt;)&lt;br /&gt;    MyService.__init__(&lt;span class="keyword"&gt;self&lt;/span&gt;) &lt;/pre&gt;&lt;br /&gt;ขั้นถัดไปก็คือทำการ binding &lt;br /&gt;โดยสั่ง connect daemon กับ​ Object instance &lt;br /&gt;ซึ่งเจ้า daemon จะจัดการไปคุยกับ Name Server ให้เราเอง&lt;br /&gt;&lt;pre class="hl"&gt;daemon.connect(obj, &lt;span class="string"&gt;'my_service'&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สุดท้ายก็คือการสั่งให้ daemon วน loop รับ request จาก client&lt;br /&gt;&lt;pre class="hl"&gt;daemon.requestLoop()&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;สิ่งที่ต้องระวังในการ implement remote object ของเราก็คือ&lt;br /&gt;มันต้องเป็น thread safe เนื่องจาก daemon จะใช้วิธีแตก thread เมื่อมี incoming connection วิ่งเข้ามา&lt;br /&gt;feature ที่น่าสนใจในประเด็นนี้ ก็คือ TLS (Thread Local Storage)&lt;br /&gt;ซึ่งช่วยให้เราเก็บข้อมูลโดยการ bind local data เข้ากับ thread แทน&lt;br /&gt;&lt;br /&gt;feature ที่น่าสนใจอีกอย่างก็คือ Mobile code&lt;br /&gt;case นี้เป็นกรณีที่ client call ไปยัง remote object โดยส่ง argument เป็น object ที่ไม่มี code อยู่บน server มาด้วย&lt;br /&gt;ซึ่งโดยปกติ มันจะเกิด error ประเภท NoModuleError&lt;br /&gt;แต่ถ้าเรากำหนด configuration ให้ Pyro ใช้ feature Mobile code&lt;br /&gt;เวลาที่ server เจอ code ที่ไม่รู้จัก มันก็จะ request code มายัง client&lt;br /&gt;กรณี mobile code นี้ support 2-way&lt;br /&gt;นั่นคือ support กรณี server return object ที่ไม่มี code บน client ด้วย&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-8786478918245712594?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/8786478918245712594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=8786478918245712594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8786478918245712594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8786478918245712594'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/pyro.html' title='Pyro'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-8374540202656805068</id><published>2008-05-25T21:33:00.003+07:00</published><updated>2008-12-11T15:17:48.037+07:00</updated><title type='text'>ป้ายแดง</title><content type='html'>ช่วงนี้โรงเรียนลูกเปิดเทอมแล้ว&lt;br /&gt;ผมก็เลยถอยรถคันใหม่ออกมาส่งลูกโดยเฉพาะ&lt;br /&gt;เลือกรุ่น entry level (ราคาถูกกว่า&lt;a href="http://ipats.exteen.com/20080428/entry"&gt;คันของ ipats&lt;/a&gt;) พร้อมด้วยเบาะนั่งของเด็ก&lt;br /&gt;ขี่ไปส่งลูกแล้วรู้สึกมั่นใจในความปลอดภัยขึ้นเยอะ&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_I3r3qYlfei4/SDl60_UaYmI/AAAAAAAAAPM/inlA0pSSf-Y/s1600-h/bike.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_I3r3qYlfei4/SDl60_UaYmI/AAAAAAAAAPM/inlA0pSSf-Y/s320/bike.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5204325895172153954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ส่วนคันเก่าก็ไม่ได้ทิ้งไปไหน พอส่งลูกเสร็จก็เปลี่ยนเป็นขี่เสือหมอบไปทำงาน&lt;br /&gt;เพราะเจ้าเสือหมอบมันเร่งแซงได้ทันใจกว่ากันเยอะ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-8374540202656805068?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/8374540202656805068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=8374540202656805068' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8374540202656805068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/8374540202656805068'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/blog-post_25.html' title='ป้ายแดง'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_I3r3qYlfei4/SDl60_UaYmI/AAAAAAAAAPM/inlA0pSSf-Y/s72-c/bike.jpg' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-2085152939891833567</id><published>2008-05-23T13:42:00.000+07:00</published><updated>2008-05-23T13:44:44.200+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Case Class in Scala</title><content type='html'>ใครที่เคยเห็น code ของ Scala, คงต้องเคยเห็นคำว่า &lt;code&gt;case class&lt;/code&gt;&lt;br /&gt;ครั้งแรกที่ผมเห็นมัน ผมรู้สึกมึนตึบ แล้วก็สงสัยว่ามันคืออะไร&lt;br /&gt;ทำไม case statement มาเกี่ยวอะไรกับ class ด้วย&lt;br /&gt;&lt;br /&gt;หลังจากนั่งอ่าน &lt;a href='http://www.artima.com/shop/forsale'&gt;Programming in Scala&lt;/a&gt; ของ Martin Odersky, Lex Spoon, Bill Venners&lt;br /&gt;ก็เลยเกิดความกระจ่างขึ้น&lt;br /&gt;เจ้า modifier "case" ข้างหน้า class นั้น มันทำให้ compiler ช่วย generate method บางอย่างเพิ่มขึ้นมา&lt;br /&gt;เพื่อทำให้เราสามารถใช้ feature pattern matching กับ Object ได้ง่ายขึ้น&lt;br /&gt;ในหนังสือเขายกตัวอย่าง กรณี expression&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;abstract&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Expr&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Var&lt;/span&gt;(&lt;span class="variable-name"&gt;name&lt;/span&gt;: &lt;span class="type"&gt;String&lt;/span&gt;) &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Expr&lt;/span&gt; &lt;br /&gt;&lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Number&lt;/span&gt;(&lt;span class="variable-name"&gt;num&lt;/span&gt;: &lt;span class="type"&gt;Double&lt;/span&gt;) &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Expr&lt;/span&gt; &lt;br /&gt;&lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;UnOp&lt;/span&gt;(&lt;span class="variable-name"&gt;operator&lt;/span&gt;: &lt;span class="type"&gt;String&lt;/span&gt;, &lt;span class="variable-name"&gt;arg&lt;/span&gt;: &lt;span class="type"&gt;Expr&lt;/span&gt;) &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Expr&lt;/span&gt; &lt;br /&gt;&lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;BinOp&lt;/span&gt;(&lt;span class="variable-name"&gt;operator&lt;/span&gt;: &lt;span class="type"&gt;String&lt;/span&gt;, &lt;span class="variable-name"&gt;left&lt;/span&gt;: &lt;span class="type"&gt;Expr&lt;/span&gt;, &lt;span class="variable-name"&gt;right&lt;/span&gt;: &lt;span class="type"&gt;Expr&lt;/span&gt;) &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;Expr&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;สมัยแรกๆที่ผมเห็น statement ข้างบน ผมก็จะนึกเดาไปว่า &lt;br /&gt;ตัว case class มันต้องเขียนเรียงติดๆกันแบบเดียวกับพวก case statement&lt;br /&gt;แต่จริงๆแล้วไม่ใช่ "case" เป็นแค่ modifiler (เหมือนพวก public, private, static ทำนองนั้น)&lt;br /&gt;ประโยคข้างบนจริงๆแล้วก็คือ การประกาศ class ขึ้นมา 5 ตัว&lt;br /&gt;ที่ดูแปลกๆ ก็คือใน scala กรณี empty body, เราไม่ต้องใส่ class body &lt;code&gt;{ }&lt;/code&gt; ให้มันก็ได้ (มันจะแอบใส่ให้เราเอง)&lt;br /&gt;&lt;br /&gt;สิ่งที่ case modifier ทำก็คือ&lt;br /&gt;&lt;ol&gt;&lt;li&gt;compiler จะช่วย generate Factory method ที่ชื่อเดียวกับ class ให้เรา&lt;br /&gt;ผลก็คือ เราสามารถใช้คำสั่งแบบนี้ได้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;val&lt;/span&gt; &lt;span class="variable-name"&gt;v&lt;/span&gt; = Number&lt;span class="show-paren-match"&gt;(&lt;/span&gt;10&lt;span class="show-paren-match"&gt;)&lt;/span&gt;&lt;br /&gt;แทนที่จะเป็น&lt;br /&gt;&lt;span class="keyword"&gt;val&lt;/span&gt; &lt;span class="variable-name"&gt;v&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; Number(10)&lt;br /&gt;ซึ่งมีประโยชน์มากเวลาที่เรา nested แบบนี้&lt;br /&gt;BinOp(&lt;span class="string"&gt;"+"&lt;/span&gt;, Var(&lt;span class="string"&gt;"x"&lt;/span&gt;), Number(7))&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;compiler จะช่วยทำให้ argument ทุกตัวกลายเป็น read only property ของ class นั้น&lt;br /&gt;เช่น&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;scala&gt; val b = BinOp("+", Var("x"), Var("x"))&lt;br /&gt;b: BinOp = BinOp(+,Var(x),Var(x))&lt;br /&gt;&lt;br /&gt;scala&gt; b.operator&lt;br /&gt;res23: String = +&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;compiler จะช่วย generate พวก method equals, hashcode ให้เรา&lt;br /&gt;โดยตัว equals method มันจะ implement ในลักษณะที่จะเปรียบเทียบทุกๆ property ของ object ให้เรา&lt;br /&gt;ทำให้เราสามารถเปรียบเทียบแบบนี้ได้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;scala&gt; Var("x") == Var("x")&lt;br /&gt;res21: Boolean = true&lt;br /&gt;&lt;br /&gt;scala&gt; BinOp("+", Number(7), Number(8)) == BinOp("+", Number(7), Number(8))&lt;br /&gt;res22: Boolean = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;ดูเหมือนว่าสิ่งที่มัน gen ก็ไม่มีอะไรมาก&lt;br /&gt;แต่ผลที่ได้ทำให้เราสามารถทำ pattern matching แบบนี้ได้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;simplifyTop&lt;/span&gt;(&lt;span class="variable-name"&gt;expr&lt;/span&gt;: &lt;span class="type"&gt;Expr&lt;/span&gt;): &lt;span class="type"&gt;Expr &lt;/span&gt;= expr &lt;span class="keyword"&gt;match&lt;/span&gt; { &lt;br /&gt;  &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="type"&gt;UnOp&lt;/span&gt;(&lt;span class="string"&gt;"-"&lt;/span&gt;, &lt;span class="type"&gt;UnOp&lt;/span&gt;(&lt;span class="string"&gt;"-"&lt;/span&gt;, &lt;span class="variable-name"&gt;e&lt;/span&gt;)) =&amp;gt; e &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Double negation &lt;br /&gt;&lt;/span&gt;  &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="type"&gt;BinOp&lt;/span&gt;(&lt;span class="string"&gt;"+"&lt;/span&gt;, &lt;span class="variable-name"&gt;e&lt;/span&gt;, &lt;span class="type"&gt;Number&lt;/span&gt;(0)) =&amp;gt; e &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Adding zero &lt;br /&gt;&lt;/span&gt;  &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="type"&gt;BinOp&lt;/span&gt;(&lt;span class="string"&gt;"*"&lt;/span&gt;, &lt;span class="variable-name"&gt;e&lt;/span&gt;, &lt;span class="type"&gt;Number&lt;/span&gt;(1)) =&amp;gt; e &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Multiplying by one &lt;br /&gt;&lt;/span&gt;  &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="variable-name"&gt;_&lt;/span&gt; =&amp;gt; expr &lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-2085152939891833567?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/2085152939891833567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=2085152939891833567' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2085152939891833567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/2085152939891833567'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/case-class-in-scala.html' title='Case Class in Scala'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5058781129590824166</id><published>2008-05-14T15:38:00.003+07:00</published><updated>2008-05-15T10:02:26.993+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>delegate ใน groovy closure</title><content type='html'>งาน NJUG ที่ผ่านมาตอนที่ฟังคุณ &lt;a href="http://chanwit.blogspot.com/"&gt;cblue&lt;/a&gt; พูดเรื่อง groovy &lt;br /&gt;มีตัวแปรใน closure ตัวหนึ่งที่ผมติดใจ ก็คือ &lt;a href='http://groovy.codehaus.org/Closures'&gt;delegate&lt;/a&gt;&lt;br /&gt;กลับมาแล้วก็เลยต้องนั่งเปิด groovy document หาความรู้เพิ่มเติม&lt;br /&gt;ก็เลยเจอ snippet code ที่น่าสนใจตัวนี้เข้า&lt;br /&gt;&lt;br /&gt;เทคนิคที่น่าสนใจก็คือการเปลี่ยน reference ของ delegate ให้ชี้ไปยัง object ที่เราต้องการ&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;class XmlBuilder {&lt;br /&gt;   def out&lt;br /&gt;   XmlBuilder(out) { this.out = out }&lt;br /&gt;   def invokeMethod(String name, args) {&lt;br /&gt;       out &amp;lt;&amp;lt; "&amp;lt;$name&amp;gt;"&lt;br /&gt;       if(args[0] instanceof Closure) {&lt;br /&gt;            &lt;b&gt;args[0].delegate = this&lt;/b&gt;&lt;br /&gt;            args[0].call()&lt;br /&gt;       }&lt;br /&gt;       else {&lt;br /&gt;           out &amp;lt;&amp;lt; args[0].toString()&lt;br /&gt;       }&lt;br /&gt;       out &amp;lt;&amp;lt; "&amp;lt;/$name&amp;gt;"&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ลองดูตัวอย่างการ run code ข้างบน&lt;br /&gt;จะเห็นว่ามีการใช้ closure ซ้อนๆกัน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;def xml = new XmlBuilder()&lt;br /&gt;xml.html {&lt;br /&gt;    head {&lt;br /&gt;        title "Hello World"&lt;br /&gt;    }&lt;br /&gt;    body {&lt;br /&gt;        p "Welcome!"&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ทุกๆครั้งที่ มีการ execute inner closure ก็จะมีการ switch delegate ให้ชี้ไปที่&lt;br /&gt;XmlBuilder Object แทนที่จะเป็น default parent object&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5058781129590824166?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5058781129590824166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5058781129590824166' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5058781129590824166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5058781129590824166'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/delegate-groovy-closure.html' title='delegate ใน groovy closure'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3192046284596171287</id><published>2008-05-12T13:06:00.001+07:00</published><updated>2008-05-12T13:11:53.298+07:00</updated><title type='text'>Script bowl</title><content type='html'>หลังจากไป NJUG กลับมาผมพึ่งพบว่าใน java one มันมี event ที่ชื่อ Script Bowl &lt;br /&gt;event นี้เป็นการจัดประชันระหว่าง script language 4 ตัวก็คือ JRuby, Groovy, Jython, Scala&lt;br /&gt;โดยแบ่งการประชันออกเป็น 3 รอบ&lt;br /&gt;รอบที่ 1 ให้ทำ app ที่เป็น Desktop GUI&lt;br /&gt;รอบที่ 2 ให้ทำ app ที่เป็น Web Application&lt;br /&gt;ส่วนรอบที่ 3 ให้ demo app อะไรก็ได้ที่คิดว่ามัน show feature ได้ถึงใจแฟนๆ&lt;br /&gt;จากนั้นก็ให้ผู้ชมทำการ vote เข้ามา&lt;br /&gt;&lt;br /&gt;ผลลัทพ์ที่ได้ ลองดูกันเองแล้วกัน&lt;br /&gt;A คือ Groovy, B คือ JRuby, C คือ Jython, D คือ scala&lt;br /&gt;&lt;a href='http://weblogs.java.net/blog/rags/archive/scriptbowlresults.jpg/voting.'&gt;Script Bowl Result&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;seapegasus เขาเขียนบรรยายเหตุการณ์โดยคร่าวๆไว้ที่นี่&lt;br /&gt;&lt;a href='http://blogs.sun.com/seapegasus/entry/groovy_jruby_jython_scala_who'&gt;Groovy, JRuby, Jython, Scala: Who Wins the Script Bowl?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3192046284596171287?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3192046284596171287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3192046284596171287' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3192046284596171287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3192046284596171287'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/script-bowl.html' title='Script bowl'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-7147098142064316669</id><published>2008-05-04T09:20:00.002+07:00</published><updated>2008-05-04T10:02:48.569+07:00</updated><title type='text'>ยืน-ไม่ยืน</title><content type='html'>สืบเนื่องจาก เรื่องสิทธิการไม่ยืนในโรงหนัง&lt;br /&gt;ลองดูว่าที่อื่นๆเขามีประเด็นเกี่ยวกับเรื่องนี้อย่างไรบ้าง&lt;br /&gt;&lt;ul&gt;&lt;li&gt;เริ่มจากอเมริกา ดินแดนที่ประเทศไทยนับถือ&lt;br /&gt;มีประเด็นเรื่อง&lt;a href="http://www.politicalgateway.com/news/read.html?id=3068"&gt;ครูบังคับให้นักเรียนยืนเคารพเพลงชาติ&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ต่อด้วยญี่ปุ่น (ซึ่งประเทศไทยนับถืออีกเช่นกัน)&lt;br /&gt;&lt;a href="http://www.japanprobe.com/?p=537"&gt;ศาลตัดสินให้คุณครูไม่ยืนเคารพเพลงชาติได้&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ข้างบนนั้นเป็นกรณีชาติเดียวกัน แต่ถ้าเป็นคนละชาติหล่ะ ก็เป็นเรื่องสิ&lt;br /&gt;&lt;a href="http://www.ynetnews.com/articles/0,7340,L-3231561,00.html"&gt;เด็กนักเรียนอาหรับไม่ยืนเคารพเพลงชาติยิว&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;แต่ที่ผมชอบคืออันนี้ (ซึ่งไม่เกี่ยวกับยืนไม่ยืน)&lt;br /&gt;&lt;a href="http://silkboard.wordpress.com/2006/08/14/why-is-jana-gana-mana-our-national-anthem/"&gt;เพลงชาติอินเดีย สรรเสริญ King George V?&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ส่วนตัวผมก็คิดว่าเราจะหา "สมดุล" ในเรื่องนี้อย่างไร&lt;br /&gt;คือประโยชน์ของมันก็มี ไม่ใช่ไม่มี&lt;br /&gt;แต่จะนำมันออกจากกรอบ "อำนาจนิยม" หรือ ความ"คลั่งไคล้" ได้อย่างไร&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-7147098142064316669?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/7147098142064316669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=7147098142064316669' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7147098142064316669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/7147098142064316669'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/blog-post.html' title='ยืน-ไม่ยืน'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-5656467834660902667</id><published>2008-05-02T16:41:00.002+07:00</published><updated>2008-05-02T16:49:47.645+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>this ใน javascript กับ dojo.hitch</title><content type='html'>ใน javascript, bug ที่พบกันบ่อยๆ ก็คือ scope ของ this ที่มักไม่ได้เป็นไปตามที่ programmer คิด&lt;br /&gt;ยกตัวอย่าง&lt;br /&gt;&lt;pre class="hl"&gt;&lt;span class="function-name"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html-tag"&gt;a&lt;/span&gt; &lt;span class="variable-name"&gt;id=&lt;/span&gt;'linkNode' &lt;span class="variable-name"&gt;href=&lt;/span&gt;&lt;span class="string"&gt;"#"&lt;/span&gt;&lt;span class="function-name"&gt;&amp;gt;&lt;/span&gt;link&lt;span class="function-name"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html-tag"&gt;/a&lt;/span&gt;&lt;span class="function-name"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="function-name"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html-tag"&gt;script&lt;/span&gt;&lt;span class="function-name"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;foo = &lt;span class="function-name"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  greeting: 'hi',&lt;br /&gt;  &lt;br /&gt;  debug: function&lt;span class="function-name"&gt;()&lt;/span&gt; &lt;span class="function-name"&gt;{&lt;/span&gt;&lt;br /&gt;      alert&lt;span class="function-name"&gt;(&lt;/span&gt;this.greeting&lt;span class="function-name"&gt;)&lt;/span&gt;;&lt;br /&gt;  &lt;span class="function-name"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="function-name"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document.getElementById&lt;span class="function-name"&gt;(&lt;/span&gt;'linkNode'&lt;span class="function-name"&gt;)&lt;/span&gt;.onclick = foo.debug;&lt;br /&gt;&lt;span class="function-name"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html-tag"&gt;/script&lt;/span&gt;&lt;span class="function-name"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;ถ้าลอง run code ข้างบนนี้ดู ก็จะพบว่า ถ้าเรา click link นั้นเมื่อไร&lt;br /&gt;message ที่แสดงใน alert มันจะแสดงคำว่า "undefined" แทนที่จะเป็น 'hi'&lt;br /&gt;ที่เป็นเช่นนี้ก็เพราะว่า this object ใน function debug ณ ขณะที่เกิด event&lt;br /&gt;function มันจะเปลี่ยน scope ทำให้ this กลายเป็น DOM element ของ anchor แทนที่จะเป็น object foo&lt;br /&gt;&lt;br /&gt;แล้ว dojo.hitch หล่ะคืออะไร&lt;br /&gt;function นี้ในแง่ของ functional language ถือว่ามันเป็น &lt;a href='http://en.wikipedia.org/wiki/Higher-order_function'&gt;higher order function&lt;/a&gt;&lt;br /&gt;เนื่องจากมันเป็น function ที่ return function&lt;br /&gt;&lt;br /&gt;หน้าที่หลักของ dojo.hitch ก็คือการ bind scope ของ function เข้ากับ object ที่เราต้องการ&lt;br /&gt;อย่างตัวอย่างข้างบน ถ้าเรานำ dojo.hitch มาช่วย ก็จะเขียนเป็นแบบนี้แทน&lt;br /&gt;&lt;pre class="hl"&gt;document.getElementById&lt;span class="function-name"&gt;(&lt;/span&gt;'linkNode'&lt;span class="function-name"&gt;)&lt;/span&gt;.onclick = dojo.hitch&lt;span class="function-name"&gt;(&lt;/span&gt;foo, &lt;span class="string"&gt;"debug"&lt;/span&gt;&lt;span class="function-name"&gt;)&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;ถ้าลอง run ดู ก็จะได้ message 'hi' ขึ้นมา&lt;br /&gt;&lt;br /&gt;จะเห็นว่า hitch ช่วยเราเปลี่ยน scope ของ this จากเดิมที่เป็น DOM element (ที่ trig event นั้น) ให้กลายเป็น foo object แทน โดยใช้ &lt;a href='http://alternateidea.com/blog/articles/2007/7/18/javascript-scope-and-binding'&gt;technique การ binding&lt;/a&gt;&lt;br /&gt;ลองมาดู code ของ function hitch กัน&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;dojo.hitch = &lt;span class="keyword"&gt;function&lt;/span&gt;(scope, method){&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt;(arguments.length &amp;gt; 2){&lt;br /&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; dojo._hitchArgs.apply(dojo, arguments); &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Function&lt;br /&gt;&lt;/span&gt;  }&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt;(!method){&lt;br /&gt;    method = scope;&lt;br /&gt;    scope = &lt;span class="keyword"&gt;null&lt;/span&gt;;&lt;br /&gt;  }&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt;(dojo.isString(method)){&lt;br /&gt;    scope = scope || dojo.global;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt;(!scope[method]){ &lt;span class="keyword"&gt;throw&lt;/span&gt;([&lt;span class="string"&gt;'dojo.hitch: scope["'&lt;/span&gt;, method, &lt;span class="string"&gt;'"] is null (scope="'&lt;/span&gt;, scope, &lt;span class="string"&gt;'")'&lt;/span&gt;].join(&lt;span class="string"&gt;''&lt;/span&gt;)); }&lt;br /&gt;    &lt;b&gt;&lt;span class="keyword"&gt;return&lt;/span&gt; function(){ &lt;span class="keyword"&gt;return&lt;/span&gt; scope[method].apply(scope, arguments || []); };&lt;/b&gt;&lt;br /&gt;  }&lt;br /&gt;  &lt;b&gt;&lt;span class="keyword"&gt;return&lt;/span&gt;&lt;/b&gt; !scope ? method : &lt;b&gt;&lt;span class="keyword"&gt;function&lt;/span&gt;(){ &lt;span class="keyword"&gt;return&lt;/span&gt; method.apply(scope, arguments || []); }; &lt;/b&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เริ่มต้นด้วย การข้าม case กรณี arguments​ &gt; 2 ไปก่อนเลย &lt;br /&gt;อันนั้นเป็นเงื่อนไขพิเศษ กรณีที่ต้องการให้มี fixed(prefix) arguments.&lt;br /&gt;&lt;br /&gt;ส่วนถัดมาก็คือส่วน ตรวจสอบว่า มีการ pass parameter มาตัวเดียวหรือเปล่า&lt;br /&gt;ถ้ามาตัวเดียว ก็จะถือว่า parameter นั้นเป็น method&lt;br /&gt;&lt;br /&gt;สุดท้ายก็ตรวจสอบอีกว่า method เป็น string&lt;br /&gt;หรือเป็น string ก็จะ return closure ที่ห่อคำสั่ง scope[method].apply&lt;br /&gt;ถ้าไม่ใช่ script ก็จะ return closure ที่ใช้คำสั่ง method.apply แทน&lt;br /&gt;&lt;br /&gt;อืมม์พอเริ่มเข้าใจ ก็เริ่มเห็นความงาม&lt;br /&gt;&lt;br /&gt;ถ้าใครสนใจอยากอ่านให้เข้าใจ(หรืองง)เข้าไปอีก อ่านเพิ่มเติมที่นี่&lt;br /&gt;&lt;a href='http://dojocampus.org/content/2008/03/15/jammastergoat-dojohitch/'&gt;Jammastergoat - dojo.hitch&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-5656467834660902667?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/5656467834660902667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=5656467834660902667' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5656467834660902667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/5656467834660902667'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/05/this-javascript-dojohitch.html' title='this ใน javascript กับ dojo.hitch'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-6473982808727422835</id><published>2008-04-29T10:33:00.002+07:00</published><updated>2008-04-29T10:37:46.060+07:00</updated><title type='text'>What is programming.</title><content type='html'>ลอกมาจาก &lt;a href="http://get.a.clue.de/ProgStone/index.html"&gt;Programming 's Stone&lt;/a&gt; บทที่ 2&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Ada is sitting in a room. In the evening the room becomes dark. Ada turns on the light.&lt;br /&gt;&lt;br /&gt;That is the fundamental act of programming. There is a problem domain (the room), which is dynamic (gets dark). There is order to the dynamic problem domain (it will be dark until morning), permitting analysis. There is a system that can operate within the problem domain (the light), and it has semantics (the switch state).&lt;br /&gt;&lt;br /&gt;There is a &lt;b&gt;desire&lt;/b&gt; (that the room shall remain bright), and there is an &lt;b&gt;insight&lt;/b&gt; (that the operation of the switch will fulfill the desire).&lt;br /&gt;Dynamic problem domains, systems and semantics are covered in detail elsewhere.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-6473982808727422835?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/6473982808727422835/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=6473982808727422835' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6473982808727422835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/6473982808727422835'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/04/what-is-programming.html' title='What is programming.'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-1895065231553183437</id><published>2008-04-28T11:36:00.002+07:00</published><updated>2008-04-28T11:40:29.333+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tapestry'/><title type='text'>Override service.</title><content type='html'>เมื่อวันก่อน ผมพึ่งพบปัญหา java process กิน cpu ไป 99% ตลอดเวลา&lt;br /&gt;ในขั้นแรกก็จัดการ "kill -3" เสีย เพื่อที่มันจะได้ให้มัน dump stack trace ออกมาให้เราดู&lt;br /&gt;&lt;pre class="hl"&gt;"ajp-8009-2" daemon prio=1 tid=0x08c76ef0 nid=0x3d52 runnable [0x6aeb6000..0x6aeb8040]&lt;br /&gt;        at java.lang.ClassLoader.loadClass(ClassLoader.java:299)&lt;br /&gt;        - waiting to lock &amp;lt;0x7e734d70&amp;gt; (a sun.misc.Launcher$AppClassLoader)&lt;br /&gt;        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)&lt;br /&gt;        - locked &amp;lt;0x7e734d70&amp;gt; (a sun.misc.Launcher$AppClassLoader)&lt;br /&gt;        at java.lang.ClassLoader.loadClass(ClassLoader.java:251)&lt;br /&gt;        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1267)&lt;br /&gt;        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1198)&lt;br /&gt;        at java.beans.Introspector.instantiate(Introspector.java:1453)&lt;br /&gt;        at java.beans.Introspector.findExplicitBeanInfo(Introspector.java:410)&lt;br /&gt;        - locked &amp;lt;0x6ebe1848&amp;gt; (a java.lang.Class)&lt;br /&gt;        at java.beans.Introspector.&amp;lt;init&amp;gt;(Introspector.java:359)&lt;br /&gt;        at java.beans.Introspector.getBeanInfo(Introspector.java:222)&lt;br /&gt;        at java.beans.Introspector.&amp;lt;init&amp;gt;(Introspector.java:368)&lt;br /&gt;        at java.beans.Introspector.getBeanInfo(Introspector.java:222)&lt;br /&gt;        ...&lt;br /&gt;        at org.apache.tapestry.util.exception.ExceptionAnalyzer.buildDescription(ExceptionAnalyzer.java:129)&lt;br /&gt;        at org.apache.tapestry.util.exception.ExceptionAnalyzer.analyze(ExceptionAnalyzer.java:86)&lt;br /&gt;        at org.apache.tapestry.util.exception.ExceptionAnalyzer.reportException(ExceptionAnalyzer.java:378)&lt;br /&gt;        at org.apache.tapestry.error.RequestExceptionReporterImpl.reportRequestException(RequestExceptionReporterImpl.java:59)&lt;/pre&gt;&lt;br /&gt;จากการไล่ stack trace ก็พบว่า มันมีปัญหาในส่วนของ introspector&lt;br /&gt;ซึ่งเกิดจากหน้าจอรายงาน Error ของ Tapestry, พยายามที่จะ dump ข้อมูล nested Exception ออกมาให้เรา &lt;br /&gt;โดยมันพยายามจะใช้กลไก Introspector ในการอธิบายปัญหาที่เกิดขึ้น, แต่ไม่รู้ด้วยเหตุใด เจ้า introspector มันดันหา class บางตัวไม่พบ&lt;br /&gt;มันก็เลย lock, waiting to lock classloader อยู่ &lt;br /&gt;&lt;br /&gt;สาเหตุของปัญหาจริงๆ ยังจับไม่ได้คาหนังคาเขา แต่คาดว่าน่าจะเกิดจากการ stop webapp บางตัวลงไป กลางอากาศ ณ ขณะที่ user กำลังใช้งานอยู่&lt;br /&gt;เมื่อยังจับต้นตอไม่ได้ แต่ก็ไม่อยากให้เกิดเหตุการณ์ cpu 99% อีก&lt;br /&gt;ผมก็เลยต้องหาวิธีการแก้ปัญหาชั่วคราวก่อน&lt;br /&gt;&lt;br /&gt;สิ่งที่เราต้องการก็คือ เราต้องการ replace function ในส่วนของการพยายามอธิบายรายละเอียด exception ที่เกิดขึ้น&lt;br /&gt;(ให้มันลดการอธิบายรายละเอียดลง)&lt;br /&gt;ในการแก้ปัญหานี้ ถ้าเจ้า framework ไม่ได้ออกแบบมาดีพอ เรามักจะต้องใช้วิธี download source code ของ framework มาแก้ไขแล้วก็็ build custom version เอาเอง&lt;br /&gt;(หรือไม่ก็ใช้ AOP ซึ่งก็ยุ่งไปอีกแบบ)&lt;br /&gt;&lt;br /&gt;โชคดีที่ tapestry ไม่ได้อยู่ในกลุ่มนี้&lt;br /&gt;Howard ออกแบบ tapestry มาอย่างยอดเยี่ยม&lt;br /&gt;มีความเป็น modular สูง สามารถถอด,เพิ่มเปลี่ยน function ได้ง่าย&lt;br /&gt;&lt;br /&gt;ในกรณีนี้ สิ่งที่เราต้องก็คือไล่หาว่า service ตัวไหน รับผิดชอบงานนั้นอยู่&lt;br /&gt;เมื่อเจอแล้ว ก็ทำตามคู่มือ ว่าด้วยการ &lt;a href='http://hivemind.apache.org/hivemind1/override.html'&gt;Overriding a Service&lt;/a&gt;&lt;br /&gt;(production ผมยังใช้ tapestry 4 อยู่)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-1895065231553183437?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/1895065231553183437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=1895065231553183437' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1895065231553183437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/1895065231553183437'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/04/override-service.html' title='Override service.'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-9110164470991921553</id><published>2008-04-22T15:13:00.001+07:00</published><updated>2008-04-22T15:20:04.893+07:00</updated><title type='text'>Distributed Issue Tracker</title><content type='html'>หลังจาก Distributed Version Control เริ่มแพร่หลายกันมากขึ้น&lt;br /&gt;สิ่งที่ติดตามมา ก็คือ Distributed Issue Tracker&lt;br /&gt;&lt;br /&gt;Issue Tracker 2 ตัว ที่ลองเล่นก็คือ&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href='http://github.com/schacon/ticgit/wikis'&gt;ticgit&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href='http://ditz.rubyforge.org/README.txt'&gt;ditz&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;ทั้งสองตัวนี้มีความเหมือนกัน 2 ประการคือ&lt;br /&gt;&lt;ul&gt;&lt;li&gt;implement ด้วย ruby&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ใช้ควบคู่กับ git ได้ (ผ่านทาง &lt;a href='http://jointheconversation.org/rubygit/'&gt;ruby/git&lt;/a&gt;)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;เริ่มที่เจ้า ticgit ก่อน&lt;br /&gt;วิธีการใช้งาน ก็ตรงไปตรงมา&lt;br /&gt;เริ่มจาก command line ของมัน ใช้ชื่อว่า 'ti'&lt;br /&gt;การสั่ง ti ครั้งแรก มันจะแอบไปสร้าง branch ใน git repository ที่ชื่อ ticgit&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ git branch&lt;br /&gt;* master&lt;br /&gt;&lt;br /&gt;$ ti&lt;br /&gt;I, [2008-04-22T12:21:53.049764 #7521]  INFO -- : creating ticgit repo branch&lt;br /&gt;Please specify at least one action to execute.&lt;br /&gt; list state show new checkout comment tag &lt;br /&gt;&lt;br /&gt;$ git branch&lt;br /&gt;* master&lt;br /&gt;  ticgit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;การสร้าง ticket ทำได้โดยการสั่ง&lt;br /&gt;&lt;pre class="hl"&gt;$ ti new -t 'my ticket'&lt;br /&gt;I, [2008-04-22T12:24:38.588505 #7551]  INFO -- : saving 1208841878_my-ticket_57&lt;br /&gt;&lt;br /&gt;Title     : my ticket&lt;br /&gt;TicId     : d00a299473633b5bd617375d1441617e56fea90d&lt;br /&gt;&lt;br /&gt;Assigned  : anon&lt;br /&gt;Opened    : Tue Apr 22 12:24:38 +0700 2008 (0 days)&lt;br /&gt;State     : OPEN&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;สังเกตุเลข Ticket Id ที่ใช้ SHA 40 ตัวอักษร สอดคล้องกับ Id ที่ git ใช้&lt;br /&gt;&lt;br /&gt;ถ้าต้องการแสดง ticket ทั้งหมดที่มี ก็ใช้คำสั่ง &lt;code&gt;list&lt;/code&gt;&lt;br /&gt;&lt;pre class="hl"&gt;$ ti list&lt;br /&gt;&lt;br /&gt;     # TicId  Title                     State Date  Assgn    Tags                &lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;     1 d00a29 my ticket                 open  04/22 anon                         &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;การทำงานกับ ticket ก็ให้ใช้คำสั่ง &lt;code&gt;checkout&lt;/code&gt;&lt;br /&gt;&lt;pre class="hl"&gt;$ ti checkout 1&lt;br /&gt;&lt;br /&gt;$ ti list&lt;br /&gt;&lt;br /&gt;     # TicId  Title                     State Date  Assgn    Tags                &lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;*    1 d00a29 my ticket                 open  04/22 anon                         &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;สังเกตุว่าหลังจาก checkout แล้ว จะมี marker '*' ที่หน้าบรรทัด&lt;br /&gt;&lt;br /&gt;แน่นอน เมื่อเป็น Issue Tracker, ดังนั้นในแต่ละ ticket เราสามารถใส่ comment ได้ &lt;br /&gt;&lt;pre class="hl"&gt;$ ti comment -m 'my comment'&lt;br /&gt;&lt;br /&gt;$ ti show&lt;br /&gt;&lt;br /&gt;Title     : my ticket&lt;br /&gt;TicId     : d00a299473633b5bd617375d1441617e56fea90d&lt;br /&gt;&lt;br /&gt;Assigned  : anon&lt;br /&gt;Opened    : Tue Apr 22 12:24:38 +0700 2008 (0 days)&lt;br /&gt;State     : OPEN&lt;br /&gt;&lt;br /&gt;Comments (1):&lt;br /&gt;  * Added 04/22 12:32 by anon&lt;br /&gt;        my comment&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;feature ที่ขาดไม่ได้ ก็คือการ tag &lt;br /&gt;ซึ่งช่วยให้เราสามารถค้น issue ได้ง่ายขึ้น&lt;br /&gt;&lt;pre class="hl"&gt;$ ti tag 1 ui&lt;br /&gt;&lt;br /&gt;$ ti list -t ui&lt;br /&gt;&lt;br /&gt;     # TicId  Title                     State Date  Assgn    Tags                &lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;*    1 d00a29 my ticket                 open  04/22 anon     ui                  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ลองดูวิธีการเก็บข้อมูลของมันบ้าง&lt;br /&gt;เนื่องจากเรารู้ว่ามันแอบเก็บข้อมูลไว้ใน branch "ticget"  เราก็เลยสามารถใช้คำสั่ง checkout เพื่อดู file ที่มันเก็บได้เลย&lt;br /&gt;&lt;pre class="hl"&gt;$ ls&lt;br /&gt;hello&lt;br /&gt;&lt;br /&gt;$ git checkout ticgit&lt;br /&gt;Switched to branch "ticgit"&lt;br /&gt;&lt;br /&gt;$ ls&lt;br /&gt;1208841878_my-ticket_57&lt;br /&gt;&lt;br /&gt;$ ls 1208841878_my-ticket_57/&lt;br /&gt;ASSIGNED_anon  COMMENT_1208842341_anon  STATE_open  TAG_main  TAG_ui  TICKET_ID&lt;br /&gt;&lt;br /&gt;$ cat 1208841878_my-ticket_57/COMMENT_1208842341_anon &lt;br /&gt;my comment&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ดูจากชื่อ file แล้ว จะเห็นว่ามันเก็บ meaning ไว้ในชื่อ file เลย&lt;br /&gt;&lt;br /&gt;ที่นี้ลองไปดูเจ้า ditz บ้าง&lt;br /&gt;การใช้งาน ditz เริ่มต้นด้วย การ init database ก่อน&lt;br /&gt;&lt;pre class="hl"&gt;$ ditz init&lt;br /&gt;Name (enter for "pok"): projectName&lt;br /&gt;Issues can be tracked across the project as a whole, or the project can be&lt;br /&gt;split into components, and issues tracked separately for each component.&lt;br /&gt;Track issues separately for different components? (y/n): y&lt;br /&gt;&lt;br /&gt;Current components:&lt;br /&gt;None!&lt;br /&gt;&lt;br /&gt;(A)dd component, (r)emove component, or (d)one: a&lt;br /&gt;Component name: ui&lt;br /&gt;&lt;br /&gt;Current components:&lt;br /&gt;  1) ui&lt;br /&gt;&lt;br /&gt;(A)dd component, (r)emove component, or (d)one: a&lt;br /&gt;Component name: domain&lt;br /&gt;&lt;br /&gt;Current components:&lt;br /&gt;  1) ui&lt;br /&gt;  2) domain&lt;br /&gt;&lt;br /&gt;(A)dd component, (r)emove component, or (d)one: d&lt;br /&gt;Ok, bugs directory created successfully.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จะเห็นว่า ditz มี data model มากกว่า ticgit&lt;br /&gt;Issue แต่ละอันที่สร้้างขึ้นมา จะต้องระบุว่ามันเป็นของ component ไหนด้วย&lt;br /&gt;&lt;br /&gt;นอกจากมุมมองในส่วน component, ditz ยังเพิ่มมุมมองในส่วนของ release ด้วย&lt;br /&gt;&lt;pre class="hl"&gt;$ ditz add-release&lt;br /&gt;Name: 0.1&lt;br /&gt;Comments (ctrl-d, ., or /stop to stop, /edit to edit, /reset to reset):&lt;br /&gt;&gt; remark for release 0.1&lt;br /&gt;&gt; Added release 0.1.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลองเพิ่ม issue ลงใน ditz&lt;br /&gt;&lt;pre class="hl"&gt;$ ditz add&lt;br /&gt;Title: feature x&lt;br /&gt;Description (ctrl-d, ., or /stop to stop, /edit to edit, /reset to reset):&lt;br /&gt;&gt; long remark for feature x&lt;br /&gt;&gt; Is this a (b)ugfix or a (f)eature? f&lt;br /&gt;Choose a component:&lt;br /&gt;  1) projectName&lt;br /&gt;  2) ui&lt;br /&gt;  3) domain&lt;br /&gt;Component (1--3): 3&lt;br /&gt;Assign to a release now? (y/n): y&lt;br /&gt;Assigning to release 0.1.&lt;br /&gt;Issue creator (enter for "Polawat Phetra &lt;pphetra@pann&gt;"): &lt;br /&gt;Comments (ctrl-d, ., or /stop to stop, /edit to edit, /reset to reset):&lt;br /&gt;&gt; Added issue domain-1.&lt;br /&gt;You may have to inform your SCM that the following files have been added:&lt;br /&gt;  bugs/issue-911f48717bbca30266d6ed1cdea7819b57158afc.yaml&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Issue แต่ละ Issue สามารถเลือก assign ให้กับ release ได้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ ditz todo&lt;br /&gt;Version 0.1 (unreleased):&lt;br /&gt;_ domain-1: feature x&lt;br /&gt;&lt;br /&gt;Unassigned:&lt;br /&gt;_ ui-1: hi&lt;br /&gt;&lt;br /&gt;$ ditz assign ui-1&lt;br /&gt;Issue ui-1 currently not assigned to any release.&lt;br /&gt;Choose a release:&lt;br /&gt;  1) 0.1 (unreleased)&lt;br /&gt;Release (1--1): 1&lt;br /&gt;Comments (ctrl-d, ., or /stop to stop, /edit to edit, /reset to reset):&lt;br /&gt;&gt; Assigned ui-1 to 0.1.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;การที่มี release ทำให้เราสามารถ track ความคืบหน้าได้ (ทำนองเดียวกับ trac)&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ ditz status&lt;br /&gt;0.1         0/ 0 (100%) bugs,  0/ 2 (  0%) features __&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ส่วนรูปแบบการเก็บ เจ้า ditz เลือกเก็บโดยใช้ format yaml&lt;br /&gt;ซึ่งสะดวกมากสำหรับการแก้ไขด้วยมือ&lt;br /&gt;&lt;pre class="hl"&gt;$ ls&lt;br /&gt;bugs  hello&lt;br /&gt;&lt;br /&gt;$ ls bugs&lt;br /&gt;issue-911f48717bbca30266d6ed1cdea7819b57158afc.yaml  project.yaml&lt;br /&gt;issue-fd2e35242ef0bd1db18fc8c0415ffedcf2dbf0cd.yaml&lt;br /&gt;&lt;br /&gt;$ cat bugs/issue-911f48717bbca30266d6ed1cdea7819b57158afc.yaml &lt;br /&gt;--- !ditz.rubyforge.org,2008-03-06/issue &lt;br /&gt;title: feature x&lt;br /&gt;desc: long remark for feature x&lt;br /&gt;type: :feature&lt;br /&gt;component: domain&lt;br /&gt;release: "0.1"&lt;br /&gt;reporter: Polawat Phetra &lt;pphetra@pann&gt;&lt;br /&gt;status: :unstarted&lt;br /&gt;disposition: &lt;br /&gt;creation_time: 2008-04-22 06:42:19.702124 Z&lt;br /&gt;references: []&lt;br /&gt;&lt;br /&gt;id: 911f48717bbca30266d6ed1cdea7819b57158afc&lt;br /&gt;log_events: &lt;br /&gt;- - 2008-04-22 06:42:21.838025 Z&lt;br /&gt;  - Polawat Phetra &lt;pphetra@pann&gt;&lt;br /&gt;  - created&lt;br /&gt;  - ""&lt;br /&gt;&lt;br /&gt;$ cat bugs/project.yaml &lt;br /&gt;--- !ditz.rubyforge.org,2008-03-06/project &lt;br /&gt;name: projectName&lt;br /&gt;version: "0.2"&lt;br /&gt;components: &lt;br /&gt;- !ditz.rubyforge.org,2008-03-06/component &lt;br /&gt;  name: projectName&lt;br /&gt;- !ditz.rubyforge.org,2008-03-06/component &lt;br /&gt;  name: ui&lt;br /&gt;- !ditz.rubyforge.org,2008-03-06/component &lt;br /&gt;  name: domain&lt;br /&gt;releases: &lt;br /&gt;- !ditz.rubyforge.org,2008-03-06/release &lt;br /&gt;  name: "0.1"&lt;br /&gt;  status: :unreleased&lt;br /&gt;  release_time: &lt;br /&gt;  log_events: &lt;br /&gt;  - - 2008-04-22 06:40:33.376540 Z&lt;br /&gt;    - Polawat Phetra &lt;pphetra@pann&gt;&lt;br /&gt;    - created&lt;br /&gt;    - remark for release 0.1&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-9110164470991921553?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/9110164470991921553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=9110164470991921553' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/9110164470991921553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/9110164470991921553'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/04/distributed-issue-tracker.html' title='Distributed Issue Tracker'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-398976011745556942</id><published>2008-04-14T07:58:00.002+07:00</published><updated>2008-04-14T08:24:39.708+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>new migration</title><content type='html'>ตอนนี้ใน edge rails ได้มีการปรับปรุง migration แล้ว&lt;br /&gt;จากของเดิมที่ migration จะใช้ เลข running version 1,2,3,...&lt;br /&gt;ไปเป็น utc timestamp แทน เพื่อแก้ปัญหากรณี branch 2 branch ต่างคนต่างสร้าง migration ทำให้เลข version ชนกัน&lt;br /&gt;สำหรับผมถือเป็นการปรับปรุงที่สำคัญมาก&lt;br /&gt;เนื่องจากมันเป็นปัญหาที่กวนใจผมมานานมากแล้ว&lt;br /&gt;&lt;br /&gt;ลองดูตัวอย่าง file ที่ &lt;code&gt;script/generate migration&lt;/code&gt; สร้างให้&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;pphetra@mypann:~/projects/ruby/mg$ ls db/migrate/&lt;br /&gt;20080414002052_create_users.rb  20080414002059_create_posts.rb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;นอกจากนี้ยังได้เปลี่ยนแปลง table ที่ทำหน้าที่เก็บข้อมูล version&lt;br /&gt;จาก schema_info ไปเป็น schema_migrations&lt;br /&gt;โดยจะเปลี่ยนวิธีการเก็บ จากเดิมที่เก็บเฉพาะเลข current version&lt;br /&gt;ไปเป็น เก็บทุก version ที่มีการ apply แล้ว&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;sqlite&gt; select * from schema_migrations&lt;br /&gt;   ...&gt; ;&lt;br /&gt;20080414002052&lt;br /&gt;20080414002059&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สุดท้ายก็คือมีการเพิ่ม rake task เข้ามาใหม่คือ&lt;br /&gt;&lt;code&gt;rake db:migrate:up&lt;/code&gt; กับ&lt;br /&gt;&lt;code&gt;rake db:migrate:down&lt;/code&gt;&lt;br /&gt;โดย algorithm ที่ใช้ จะไม่เหมือนเดิม &lt;br /&gt;จากเดิมเป็นการไล่เลข running ขึ้นลงเพียงอย่างเดียว&lt;br /&gt;ไปเป็นการไล่ตรวจสอบด้วยว่า มีการ apply migration version นั้นๆแล้วหรือยัง&lt;br /&gt;(เขาใช้ศัพท์เรียก migration พวกที่ยังไม่ apply ว่า interleaved migration)&lt;br /&gt;&lt;br /&gt;ยกตัวอย่างการสั่ง up&lt;br /&gt;ถ้าไล่แบบเดิม มันจะไล่ apply ตั้งแต่ current version + 1 ไปจน ล่าสุด (หรือถึง VERSION ที่ระบุ)&lt;br /&gt;แต่ถ้าเป็นของใหม่ มันจะตรวจว่า มี migration อันไหนที่ยังไม่ apply บ้างแล้วจัดการ apply ให้เรา&lt;br /&gt;กรณี down ก็เช่นกัน&lt;br /&gt;ถ้ามันตรวจพบว่ามี migration ไหน ยังไม่ apply มันก็จะ skip migration นั้นๆให้&lt;br /&gt;&lt;br /&gt;Links&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://github.com/rails/rails/commit/8a5a9dcbf64843f064b6e8a0b9c6eea8f0b8536e"&gt;Commit 8a5a9dcbf...&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://dev.rubyonrails.org/changeset/9122"&gt;Changeset 9122&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-398976011745556942?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/398976011745556942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=398976011745556942' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/398976011745556942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/398976011745556942'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/04/new-migration.html' title='new migration'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3043089487668926855</id><published>2008-04-12T11:26:00.002+07:00</published><updated>2008-04-12T11:29:50.754+07:00</updated><title type='text'>My History</title><content type='html'>เห็นใน feed มาพักใหญ่แล้ว&lt;br /&gt;มาเล่นกันบ้างดีกว่า&lt;br /&gt;&lt;pre class="hl"&gt;&lt;br /&gt;$ history|awk '{a[$2]++} END{for(i in a){printf "%5d\t%s\n",a[i],i}}'|sort -rn|head&lt;br /&gt;  130   git&lt;br /&gt;  102   cd&lt;br /&gt;   74   ls&lt;br /&gt;   42   gitk&lt;br /&gt;   22   vi&lt;br /&gt;   19   svn&lt;br /&gt;   15   sudo&lt;br /&gt;   11   exit&lt;br /&gt;   11   df&lt;br /&gt;    8   echo&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;อ้าววันๆใครทำอะไรกันบ้าง มา show ให้ดูกันดีกว่า&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3043089487668926855?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3043089487668926855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3043089487668926855' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3043089487668926855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3043089487668926855'/><link rel='alternate' type='text/html' href='http://pphetra.blogspot.com/2008/04/my-history.html' title='My History'/><author><name>polawat phetra</name><uri>http://www.blogger.com/profile/17730887203314080442</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9704902.post-3106343392593402703</id><published>2008-04-11T13:09:00.000+07:00</published><updated>2008-04-11T13:10:57.001+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='birt'/><title type='text'>Passing xml stream into BIRT</title><content type='html'>BIRT สามารถกำหนด datasource ได้หลายแบบ&lt;br /&gt;ในบางสถานะการณ์ผมก็จะเลือกใช้ xml&lt;br /&gt;ในคู่มือส่วนใหญ่ก็จะระบุว่า ถ้าเลือก xml ก็ต้องกำหนด location ของ xml&lt;br /&gt;ซึ่งอาจเป็น url หรือ local file ก็ได้&lt;br /&gt;&lt;br /&gt;ปัญหาก็คือ ผมอยากจะ pass xml stream ของผมเข้าไปที่ Engine ตรงๆ&lt;br /&gt;ไม่อยากจะทำเป็น temporary file (ขี้เกียจจัดการ)&lt;br /&gt;แล้วก็ไม่อยากทำเป็น url ด้วย (มีประเด็นเรื่อง security เพิ่มขึ้น)&lt;br /&gt;&lt;br /&gt;พยายามมองหาวิธีในคู่มือ ก็หาไม่เจอ&lt;br /&gt;สุดท้ายก็เลยต้องเปิด &lt;a href='http://www.krugle.com/'&gt;Krugle&lt;/a&gt; เพื่อค้นหาจาก source แทน&lt;br /&gt;หลังจาก zoom ตาม hirarchy ของ class ไปเรื่อยๆ&lt;br /&gt;(ไล่ข้าม project เลย, ปรากฎว่ามันอยู่ใน project Data Tools Platform)&lt;br /&gt;ก็ไปเจอเจ้า &lt;a href='http://www.krugle.org/kse/codespaces/BUHWnd'&gt;Connection&lt;/a&gt; ซึ่งมี code แบบนี้&lt;br /&gt;&lt;pre class="hl"&gt;    &lt;span class="keyword"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;open&lt;/span&gt;( &lt;span class="type"&gt;Properties&lt;/span&gt; &lt;span class="variable-name"&gt;connProperties&lt;/span&gt; ) &lt;br /&gt;            &lt;span class="keyword"&gt;throws&lt;/span&gt; &lt;span class="constant"&gt;org&lt;/span&gt;.&lt;span class="constant"&gt;eclipse&lt;/span&gt;.&lt;span class="constant"&gt;datatools&lt;/span&gt;.&lt;span class="constant"&gt;connectivity&lt;/span&gt;.&lt;span class="constant"&gt;oda&lt;/span&gt;.&lt;span class="type"&gt;OdaException&lt;/span&gt; &lt;br /&gt;    { &lt;br /&gt;        ...&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; ( appContext != &lt;span class="constant"&gt;null&lt;/span&gt; &lt;br /&gt;                &amp;amp;&amp;amp; appContext.get( &lt;span class="constant"&gt;Constants&lt;/span&gt;.APPCONTEXT_INPUTSTREAM ) != &lt;span class="constant"&gt;null&lt;/span&gt; &lt;br /&gt;                &amp;amp;&amp;amp; appContext.get( &lt;span class="constant"&gt;Constants&lt;/span&gt;.APPCONTEXT_INPUTSTREAM ) &lt;span class="keyword"&gt;instanceof&lt;/span&gt; InputStream ) &lt;br /&gt;            is = XMLDataInputStreamCreator.getCreator( (&lt;span class="type"&gt;InputStream&lt;/span&gt;) appContext.get( &lt;span class="constant"&gt;Constants&lt;/span&gt;.APPCONTEXT_INPUTSTREAM ) ); &lt;br /&gt;        &lt;span class="keyword"&gt;else&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; ( file != &lt;span class="constant"&gt;null&lt;/span&gt; ) &lt;br /&gt;            is = XMLDataInputStreamCreator.getCreator( file ); &lt;br /&gt;        &lt;span class="keyword"&gt;else&lt;/span&gt; &lt;br /&gt;            &lt;span class="keyword"&gt;throw&lt;/span&gt; &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;OdaException&lt;/span&gt;( Messages.getString( &lt;span class="string"&gt;"Connection.PropertiesMissing"&lt;/span&gt; ) ); &lt;br /&gt;&lt;br /&gt;        ...&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ไล่เปิดดู Constants.APPCONTEXT_INPUTSTREAM สักหน่อยว่าค่าเป็นอะไร&lt;br /&gt;แล้ว fix ลงโปรแกรมเราไปเลย &lt;br /&gt;(ไม่ต้องการเพิ่ม dependency ด้วยการอ้างถึง class Constants, &lt;br /&gt; แต่แน่นอนอนาคตถ้า upgrade birt ก็อาจ run ไม่ผ่านเอาดื้อๆได้)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9704902-3106343392593402703?l=pphetra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pphetra.blogspot.com/feeds/3106343392593402703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9704902&amp;postID=3106343392593402703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9704902/posts/default/3106343392593402703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.co
