Friday, January 19, 2007

code vs comment

ปกติเวลาเราเขียนโปรแกรม
source code คือเนื้อหาหลัก ส่วน comment คือเนื้อหารอง
,comment จะแทรกอยู่ใน source code โดยมี prefix พิเศษนำหน้า

haskell มันมี file format อยู่อันหนึ่งที่เรียกว่า "lhs"
ถ้าอยู่ใน format นี้ source code จะหลายเป็นเนื้อหารองแทน
(คือต้องมี prefix นำหน้า)
-------------------------------------------------------------------------------
Haskell code and test cases
for the non-monadic stages
-------------------------------------------------------------------------------

This file contains the Haskell code for the various non-monadic stages
in the derivation of a Gaussian Elimination program using the
methodology. The remaining programs are in other files in the same
directory.

It also contains 3 sets of test data. Typing "main" runs the programs
for each non-monadic stage on this data and prints the results.

It needs modules which provide finite sequences:

> import SeqFinSeq
> import ParFinSeq
> import Cyclic

-------------------------------------------------------------------------------
The following function runs the functions on the test data:

> main :: IO()
> main = do putStr "The test data is:\n"
> putStr "1st set: \n"
> putStr ("a = " ++ (show a_matrix) ++ "\n")
> putStr ("b = " ++ (show b_vector) ++ "\n")
> putStr "2nd set: \n"
> putStr ("a = " ++ (show a2_matrix) ++ "\n")
> putStr ("b = " ++ (show b2_vector) ++ "\n")
> putStr "3rd set: \n"
> putStr ("a = " ++ (show a3_matrix) ++ "\n")
> putStr ("b = " ++ (show b3_vector) ++ "\n")


------------------------------
Test data (need a matrix A and a vector b)

> a_matrix :: Matrix Rational
> a_matrix = [[2,3],[1,-1]]
> b_vector :: Vector Rational
> b_vector = [25,10]

Related link from Roti

Wednesday, January 17, 2007

Debug ruby with xmpfilter

XMPFilter คือเครื่องมือที่ช่วยให้เรา
ทดสอบ ruby code ได้ง่ายขึ้น
โดยมันจะช่วยพิมพ์ผลลัพท์ตามจุดต่างๆ ทำให้เรา debug program ได้ง่ายขึ้น

สมมติว่าเรามี code, เราอยากจะเห็นว่าเกิดอะไรขึ้นกับ n เราก็เลยใส่ # => ลงไปข้างท้ายบรรทัด
[1,2,3].collect do |n|
n + 1 # =>
end


ถ้าเรานำ code นี้ไป run ผ่าน xmp process
เราก็จะได้ output แบบนี้
[1,2,3].collect do |n|
n + 1 # => 2, 3, 4
end


แต่ที่ชอบมากที่สุด ก็คือมัน integrate เข้ากับทั้ง vi และ emacs
อย่างใน emacs เราสามารถสั่ง evaluate โดยใช้ M-x xmp

Related link from Roti

Friday, January 12, 2007

Erlang Application ในไทย

(ข้อความต่อไปนี้ เป็นการเผยแพร่ศาสนา erlang
คนเชื่อคนง่าย กรุณาใช้วิจารณญาณ ในการรับฟัง)

Luke Gorrie เขาเขียนไว้ใน blog ดังนี้

We've developed and marketed a system for mobile phone companies to keep track of all the mobile phones their customers are using and to automatically send configurations for picture messaging, web browsing, and so on. We've installed these systems in over 60 countries and have more than 200 million of you puny humans represented in our Mnesia databases. We're the market leaders and have only a fraction of our competitors' staff size, but of course you guessed this when I said "Erlang". :-)

ดูแผนที่ที่เขาไป มีไทยด้วยนะครับ

Note: mnesia เป็นชื่อ database ใน erlang

Related link from Roti

Thursday, January 11, 2007

ประโยคนี้น่าสนใจนะ


Thirty years of research has shown that
many children who pass reading tests in the early grades
cannot learn content later on when the
emphasis shifts in school from
“learning to read” to “reading to learn”

Related link from Roti

Wednesday, January 10, 2007

Web Server survey

ผลสำรวจ Web Server Survey ของ Netcraft

จำนวน site ที่สำรวจ 104,944,594
ลำดับหนึ่งแน่นอนว่า Apache 63,819,607

พวก opensource appserver (java)
ต่อตรง ไม่มี apache ดักหน้า
tomcat (coyote) 383,475
jetty 110,730
Resin 106,684

commercial app server
IBM_HTTP_SERVER 54,660 (websphere)
WebLogic 26,356

ที่นี้ลองดูพวก ภาษาทางเลือกดูบ้าง
เริ่มที่ zope ก่อน
zope 47,326

ตามด้วย rails ที่ไม่ได้ใช้ apache หรือ lighttpd เป็นด่านหน้า
(ก็เลยไม่รู้ว่า พวกที่ deploy แบบ fcgi, หรืออื่นๆ มีจำนวนเท่าไร)
mongrel 2,407
WEBrick 286

erlang ก็มีคนใช้เหมือนกัน
yaws 110

พวก CLISP
AllegroServe 285

พวก smalltalk บน Squeak
Comanche 97

Related link from Roti

Tuesday, January 09, 2007

Java กับ Functional Language

ในงาน NJUG ครั้งแรก
dean4j ถามมาประเด็นหนึ่งที่เกี่ยวกับ java กับ functional language
มีประมาณว่า inner class ก็ตอบโจทย์ในสิ่งที่ functional ทำได้
ผมลืมคำถามไปแล้ว
แต่บางส่วนมันยังติดอยู่ใจอยู่
ประเด็นที่ติด ก็คือ คำตอบหนึ่งของผม
(จริงๆแล้วคำตอบก็ลืมแล้วเหมือนกัน ที่จำว่าจำได้ นี่ก็ไม่รู้ว่าจริงหรือเปล่า)
ก็คือ functional language สำหรับผม
มันดูเหมือนจะทำให้ผม express ตัวตนได้ดีกว่า
ซึ่งคำตอบนี้คลุมเครือและ subjective มาก
วันนี้นึกออกแล้ว จะเปรียบเทียบกับอะไรดี (แต่ก็ยัง subjective อยู่ดี)

ลองนึกถึงพวกบทกลอนหรือกวีดูสิ
เป้าหมายเดียวกัน แต่ form มันต่างกัน
ความชอบก็ขึ้นอยู่กับ context ของคนที่อ่านหรือแต่ง ณ ขณะนั้น

เช่นถ้าผมอยู่คนเดียวเงียบๆ บนภูเขาสูง ผมย่อมชอบไปทาง ไฮกุ
แต่ถ้าอยู่รวมเป็นหมู่คณะ แน่นอนพวก สักวา กลอนแปด นี่สนุกกว่า ไฮกุ แน่นอน

java สำหรับผม ก็เหมือน กลอนแปด ในส่วนง่ายๆ
ในส่วนยาก เปรียบกับ โคลงสี่สุภาพ แล้วกัน

แต่พวก functional language นี่มันได้อารมณ์เหมือน ไฮกุ

inside the forest
a quiet sound
of the leaves

Takahito Suzuki 10yrs

สระน้ำ อันเก่าแก่
เจ้ากบตัวหนึ่งกระโดดลง
เสียงน้ำพลันดังจ๋อม

บะโช มะทสึโอะ (ฺBashoo Matsuo)

Related link from Roti

Friday, January 05, 2007

We are what we repeatedly do

อันนี้เขาว่าเป็นคำพูดของ Aristotle
ซึ่งถ้าให้เหลือคำเดียว ก็คือคำว่า Habit
หรือของไทย ก็คือ นิสัย,กิจวัตร,ความเคยชิน

พออายุมากขึ้น ผมก็เริ่มเห็นพ้องกับคำพูดข้างต้น
จากการเฝ้ามองตัวเอง
จะเห็นว่ามีหลายอย่างเลย ที่เป็นปฏิกริยาอัติโนมัติ
ทั้งเรื่องเล็กๆน้อยๆ ไปจนถึงเรื่องใหญ่

ลองดูเรื่องเล็กๆแล้วกัน (เรื่องใหญ่ๆ ใครเขาจะเอาตัวเองมาเผาเล่า)
ยกตัวอย่างนิสัยการเคี้ยวอาหาร
หลังจากใช้เวลา 30 กว่าปี ในการเคี้ยวอย่างมูมมาม
ในที่สุดผมก็พบว่า ปากผมมันมักจะไม่ยอมเชื่อฟังคำสั่งผมแล้ว
ในขณะที่สมองสั่งว่า เคี้ยวเยอะๆไม่ต้องรีบกลืน
แต่พออาหารแตะถึงส่วนหลังของโคนลิ้น
กลไกการกลืนจะเกิดขึ้นทันที
เป็นสภาพที่น่าตกใจเหมือนกันนะ
ที่พบว่าเราสูญเสียการควบคุมบางอย่างไป

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

ตัวอย่างของ Habit ที่เห็นได้ชัด
จะเกี่ยวข้องกับ motor learning
สมัย ที่ผมเข้าเรียนมหาวิทยาลัยใหม่ๆ
ตอนนั้นผมเล่น softball เป็น catcher หรือไม่ก็ short stop
สิ่งที่ผมทำมาตลอด ระหว่างการเล่นก็คือ
การเบี่ยงตัวหลบ วิถีการเคลื่อนที่ ของลูกที่ไม่ได้คาดคิด
เช่น เด้นพื้นผิดทิศทาง
ซึ่งถ้าวัดความเร็วของปฏิกริยาแล้ว ก็ขอโม้ว่า
น้องๆทืมชาติ

วันหนึ่ง ผมเดินไปเรียนกับเพื่อนสาวสวยซึ่งเป็นดาวคณะวิศวะในตอนนั้น
คุณเธอเดินหน้า ผมเดินตามหลัง
อยู่ดีๆ เธอก็เกิดลื่นพรื่ดหงายหลังขึ้นมา
ซึ่งถ้าในนิยายหวานแหว๋วทั่วไป พระเอกก็ต้องโอบรับนางเอกอย่างสวยงาม
แต่ในกรณีผม ร่างกายมันฝึกมาดี
ทำให้ผมเบี่ยงตัวหลบได้อย่างสวยงาม
รอดพ้นจากการรับได้อย่างหวุดหวิด
(เศร้าไหมหล่ะ กำลังจีบอยู่ด้วย)

การคิดทั้งหลาย ก็เป็น Habit เหมือนกัน
Edward De bono ยังเขียนขายได้ตั้งหลายเล่ม

แน่นอน นิสัยมันกินความไปถึง วิชาชีพที่เราทำด้วย
นิสัยการ coding ของเรา
"code แบบไม่คิดอะไร ทำงานได้ก็พอ"
ทำบ่อยเข้า ก็จะติดเปิดนิสัย
หรือ นิสัยขององค์กร พัฒนาโปรแกรมที่ไร
ก็ทำตามแบบที่เคยทำกันไปเรื่อยๆ

ปัญหาก็คือ
แล้วเราจะเลิกนิสัยไม่ดี หรือ เริ่มนิสัยดี ได้อย่างไร
อุปสรรคของการเริ่มนิสัยดีๆ ก็คือ postpone หรือภาษาไทยว่า ปัดวันประกันพรุ่ง
อย่างที่ประโยคข้างบนว่า "repeatedly do"
ถ้าไม่เริ่มทำ ก็ไม่มีจุดเริ่มต้น
ส่วนอาวุธที่จะจัดการ นิสัยที่ไม่ดี ก็คือ สติ
(คำถามที่น่าสนใจ องค์กร จะมี สติ ได้ไหม
สติ ของ องค์กร เป็นอย่างไร)

ปิด ด้วยคำคม

“Our habits always occur in the plural, and they belong together,
constituting a style.”
From the point of view of character, our habits make us who we are.


Habits do not wait to be used;
they project themselves into one’s thoughts and behaviors.


เรื่อง Habit มีประเด็นน่าสนใจอีกเยอะ
ควรอ่านเพิ่มเติมอย่างยิ่ง
The Laws of Habit
Tacit knowledge and habit

Related link from Roti

Thursday, January 04, 2007

ห้าเรื่องที่คุณไม่รู้หรอก

สนุกๆ น้อง Wiennat เริ่มต้นแล้ว

1. งานแต่งงานผม ผมแจก card เชิญไป 2 ใบ
เมื่อรวมกับที่ภรรยาแจกแล้ว รวมเป็นจำนวนทั้งสิ้น 42 ใบ
แถมมีข้อความใน card ระบุว่า "งดรับเงินช่วย"

2. ต่อเนื่องจากข้างบน
ผมพบรักกับภรรยาผมทาง net ครับ
บอก site ก็ได้ thaimate.com
(อันนี้อายสุดๆ)

3. ตอนสมัยเรียน ผมเคยโดนครูตบหน้า 2 ครั้ง
ครั้งแรกใน lab วิชาวิทยาศาสตร์ สมัย ม. 2 มั้ง
สาเหตุ ใส่น้ำแข็งลงใน beaker ไม่ตรงตามลำดับ
ส่วนครั้งที่สอง ตอนที่เรียนปีหนึ่งที่เกษตรศาสตร์
ห้อง lab เคมีอีกเหมือนกัน
เหตุการณ์ทำนองเดียวกับคือ ลำดับไม่ถูกต้อง

ตบไม่แรงหรอก แต่ก็ทำให้เกลียดการเรียนการสอน "ถาวร"

สำหรับตอนนี้สิ่งที่น่าสนใจ ก็คือ
อะไรทำให้ครูสองคนนั้น break down
วิธีพูด สีหน้า แววตา

4. สมัยเด็กๆ ลูกพี่ลูกน้องผม เป็น ผู้รักษาประตู ทืมมหาวิทยาลัยเกษตร
ผมก็เลยถูกฝึกให้เป็นผู้รักษาประตู ตั้งแต่ ป. 4
ส่งผลให้ทักษะการมองลูกกลมๆที่ลอยอยู่ในอากาศดีเป็นอย่างยิ่ง
(ถ้ามีการแข่งบอล ภายในของ blognone เมื่อไร
ขอเป็น goal)

5. ตอนเรียนปีสอง มีเพื่อนอักษรเอกละคร คนหนึ่งมาชวนไป
try out เป็น คุณเข้ม (หนังสือเรื่อง ปุลากง)
เสียดายที่มองตัวเองออกว่าความสามารถไม่ถึง
ก็เลยไม่ได้ไป
(ไม่งั้นเสนาหอย คงมีคู่แข่งแล้ว)

กระจายไปให้คนอื่นๆบ้าง
(เอ่อ ใครอ่าน blog ผมบ้างนี่)
RerngRit
Ziddik
OHM
Prach Pongpanich
Pittaya

Related link from Roti

Wednesday, January 03, 2007

Ninety-nine Lisp Problem #P07

วันนี้นั่งทำโจทย์ข้อที่ 7, ในแง่ของ clisp กับ erlang
ข้อนี้ไม่ยากนัก แต่ในแง่ของ haskell แล้ว program ออกมาไม่สวยนัก
ก็เลย mail ไปถามใน mailing list ของ haskell ดู
ซึ่งก็ได้ผล มีคนเก่งๆมาช่วยไขความกระจ่าง
ถือเป็น mailing list ที่น่าประทับใจอันหนึ่ง

ลองดูโจทย์

P07 (**) Flatten a nested list structure.
Transform a list, possibly holding lists as elements into a `flat' list by replacing each list with its elements (recursively).

Example:
* (my-flatten '(a (b (c d) e)))
(A B C D E)

Hint: Use the predefined functions list and append.


CLisp
ใช้ append ในการ ต่อ list 2 อันเข้าด้วยกัน
(defun my-flatten (lst)
(cond ((not (listp lst)) (list lst))
((null lst) nil)
(t (append (my-flatten (car lst))
(my-flatten (cdr lst))))))


Erlang
erlang ก็ใช้วิธีตามแบบ lisp มาติดๆ
แต่ด้วยการใช้ guard + pattern matching เข้ามาช่วย
ทำให้ดูง่ายกว่า clisp
-module(p07).
-export([flat/1]).

flat([]) -> [];
flat(X) when atom(X) -> [X];
flat([H|T]) -> flat(H) ++ flat(T).


Haskell
ข้อนี้ haskell ไม่สามารถทำตรงๆได้
เนื่องจาก type system ของ Haskell
ไม่สามารถกำหนด type nested array แบบนี้ได้

[1,2] type คือ [a]
[1,[2,3]] type คือ [[a]]
[1,[2,[3,4],5]] type คือ [[[a]]]

จากโจทย์ จะเห็นว่า list มันจะซ้อนกันกี่ชั้นก็ได้
ทำให้ไม่สามารถ declare type ได้

ถ้าจะแก้โจทย์นี้ ก็ต้องเปลี่ยน structure ของ list ก่อน
โดย declare เป็น abstract data type ขึ้นมาก
จาก
[1,[2,[3,4],5]]
ให้เป็น
[E 1, S[E 2, S[E 3, E 4], E 5]]

ตัว data type เรา declare แบบนี้
data Store a = E a | S [Store a]
deriving (Show)


จากนั้นก็เขียน function
flat :: [Store a] -> [a]
flat [] = []
flat ((E x):xs) = [x] ++ flat xs
flat ((S x):xs) = flat x ++ flat xs


จะเห็นว่ามันยืดยาวไม่สวยงาม
ผมก็เลยเขียนไปถามใน mailing list
ลองอ่านที่เขาตอบมาดู
flatten a nested list
ผมชอบ solution ที่ Conor McBride ตอบมากสุด
ทำให้รู้ด้วยว่า list มันเป็น monadic structure ด้วย

flat1 :: Store a -> [a]
flat1 (E a) = return a
flat1 (S xs) = xs >>= flat1

magic อยู่ที่ function >>=
ซึ่งถ้าไปดูนิยามมันใน library ดู จะเห็นนิยามมันเป็นแบบนี้
instance Monad [ ] where
(x:xs) >>= f = f x ++ (xs >>= f)
[] >>= f = []
return x = [x]
fail s = []

Related link from Roti

Friday, December 29, 2006

Ninety-nine Lisp Problem #p04,#p05


P04 (*) Find the number of elements of a list.

ทำได้ 2 แบบคือ
1. ใช้ recursive ธรรมดา
2. ใช้ tail recursive

clisp
(defun len (lst)
(if (null lst)
0
(1+ (len (cdr lst)))))

(defun len-2 (lst &optional (cnt 0))
(if (null lst)
cnt
(len-2 (cdr lst) (1+ cnt))))


haskell
len :: [a] -> Int
len ([]) = 0
len (_:xs) = 1 + len xs


len_2 :: [a] -> Int
len_2 (xs) = len_helper xs 0
where
len_helper ([]) n = n
len_helper (_:xs) n = len_helper xs (n + 1)


erlang
-module(p04).
-export([len/1,len_2/1]).

len([]) ->
0;
len([_|XS]) ->
1 + len(XS).


len_2(L) ->
len_iter(L,0).

len_iter([],N) ->
N;
len_iter([_|XS],N) ->
len_iter(XS,N+1).


P05 (*) Reverse a list.


implement ทั้งหมดแบบ tail-recursive

clisp
(defun rev (lst &optional (tmp '()))
(if (null lst)
tmp
(rev (cdr lst) (cons (first lst) tmp))))


haskell
inv :: [a] -> [a]
inv l = invx l []
where
invx [] y = y
invx (x:xs) y = invx xs (x : y)

ดูยาว, แต่ถ้าไปดู implement ของ hugs เขาจะเขียนแค่นี้
reverse   :: [a] -> [a]
reverse = foldl (flip (:)) []


erlang
-module(p05).
-export([rev/1]).

rev(X) ->
rev(X,[]).

rev([], L) ->
L;
rev([H|T], L) ->
rev(T, [H|L]).

Related link from Roti

Thursday, December 28, 2006

clouds

เคยได้ยินเมฆที่ชื่อแบบนี้ไหม
Lenticular cloud, Wave cloud, Nacreous cloud,
Mammatus cloud

ลองไปดูภาพได้ที่นี่ Strange cloud

นึกถือตอนเด็กๆ มีอยู่ช่วงหนึ่งผมสร้างบ้านบนต้นไม้ (แค่เอาแผ่นไม้ 1 แผ่น กับกล่อง 1 กล่อง ไปวาง)
แล้วก็ใช้เป็นสถานีดูเมฆ
ตอนนั้นจำได้ว่าได้แรงบันดาลใจจากบทความว่าด้วยประเภทของเมฆ ในวารสาร "ทักษะ"
หนังสือซึ่งใครที่แก่สักหน่อยคงจะรู้จัก

Related link from Roti

เสริม ICU4J

bact' เขียนเรื่อง Using dictionary with ICU4J BreakIterator
เห็น code ที่ bact' ใช้ในการ initialize
String rules = (RuleBasedBreakIterator.getWordInstance(new Locale ("th"))).toString();

ในส่วนของการได้มาซึ่ง rules มันดูแปลกๆ
ก็เลยไปนั่งไล่ code ดูว่ามันมาจากไหนกันแน่

เริ่มแรกสุด method getWordInstance มันเป็น static method
ใน class BreakIterator
ดังนั้นถ้าจะเขียนให้สื่อหน่อย อาจจะเขียนอย่างนี้แทน
String rules = BreakIterator.getWordInstance(new Locale ("th")).toString()


method getWordInstance ไม่ได้สร้าง BreakIterator เอง
แต่ delegate งานไปให้ BreakIteratorFactory เป็นคนทำ
ซึ่ง BreakIteratorFactory จะใช้กลไก ResourceBundle ในการค้นหา
ว่าจะใช้ rule file ตัวไหนดี

จากการ debug, ตัว rule file จะใช้ file
com/ibm/icu/impl/data/icudt36b/brkitr/word.brk
ภายในเป็น binary file ก็เลยไม่รู้ว่ามีเนื้อหาอะไรบ้าง

เมื่อได้ rule มาแล้ว
BreakIteratorFactory มันจะสร้าง BreakIterator ให้เราโดยใช้คำสั่งนี้
//TODO: Hard code this for now! fix it once CompactTrieDictionary is ported
if(locale.equals("th")){
String fileName = "data/th.brk";
InputStream is = ICUData.getStream(fileName);
iter = new DictionaryBasedBreakIterator(ruleStream, is);
}

Note: สังเกตุ comment ใน Todo ด้วย

ดังนั้นตัวอย่างของ bact'
ถ้าเขียนใหม่ให้ชัดเจนไปเลยว่า rule มาจากไหน
อาจจะเขียนดังนี้
InputStream rules = ICUData.getStream("data/icudt36b/brkitr/word.brk");
InputStream dict = new FileInputStream("tdict.dic");

DictionaryBasedBreakIterator iter =
new DictionaryBasedBreakIterator(rules, dict);

Related link from Roti

Wednesday, December 27, 2006

การข่าวมหาดไทย

เฮ้อ อ่านข้อเขียนเรื่อง "การข่าวท่านปลัด"
ของ พลเอกบัญชร ชวาลศิลป์ ในมติชนสุดสัปดาห์
ที่เขียนเล่ากรณี การจราจลที่เกิดจากการประท้วงเรื่องโรงงานแทนทาลัมที่ภูเก็ต
แล้วเหนื่อยใจ

ก็พอรู้นะว่า กระทรวงนี้ คาดหวังอะไรไม่ได้
แต่ไม่นึกว่า มันจะแย่ขนาดนี้

Note: เรื่องนี้เกิดมานานแล้ว จำปีไม่ได้แล้ว
แต่เชื่อว่า ปัจจุบันก็มหาดไทยก็คงไม่หนีไปไหนไกลหรอก

ลองอ่าน quote นี้ดู

นายพิศาล มูลศาสตรสาทร ท่านสรุปว่า
เหตุการณ์รุนแรงที่เกิดขึ้นที่ภูเก็ตครั้งนี้มาจากกลุ่มผู้ก่อการร้าย 2 กลุ่มด้วยกัน
ผู้ก่อการร้ายพวกแรกคือพรรคคอมมิวนิสต์แห่งประเทศไทย
...
ในคืนก่อนหน้าที่จะเกิดเหตุ ผกค. จำนวนหนึ่งได้เคลื่อนย้าย
เข้ามายังตัวจังหวัดภูเก็ต เพื่อเป็นแกนนำในการปฏิบัติการ
โดยแทรกซึมเข้ามาด้วยรถบรรทุกสิบล้อจำนวนหนึ่ง
เพื่ออำพรางเจ้าหน้าที่ แล้วโดดลงข้างทางบริเวณที่เป็นสวนยาง
ค่อยๆเดินเท้าแทรกซึมไปรวมตัวกันที่วัดฉลอง แล้วเข้าปฏิบัติการจนเป็นผลสำเร็จ
สามารถเผาโรงแรมและโรงงานได้สำเร็จตามแผน
...
ผม[พลเอกบัญชร]ฟังคำบรรยายสรุปของปลัดมหาดไทยด้วยความแปลกใจ
และขนลุก แต่การรายงานข่าวของท่านยังไม่จบลงเพียงแค่นั้น

นอกจาก ผกค. แล้ว ... มีพวก ขจก. ซึ่งปกติจะเคลื่อนไหว
อยู่เฉพาะจังหวัดชายแดนภาคใต้เท่านั้น
...
ก่อนเกิดเหตุการณ์จราจลไม่กี่วัน เด็กหนุ่ม ขจก. เหล่านี้
ได้กระโดดร่มลงยังบริเวณพื้นที่จังหวัดพังงา

แล้วเดินทางแทรกซึมเข้ามาร่วมการก่อจราจล


พลเอกบัญชร เขาเขียนให้ความเห็นว่า

นี่คือความเชื่อเป็นตุเป็นตะ จนกล้านำเสนอในที่ประชุมระดับชาติของปลัด
ซึ่งน่าจะสะท้อนความเป็นจริงของหน่วยงานที่มีความสำคัญทางด้าน
ความมั่นคงของประเทศหน่วยนี้ในขณะนั้น


น่าสนใจมาก
plot เรื่องเน่าสนิท เหมือนละครก่อน/หลังข่าวเลย

Related link from Roti

Tuesday, December 26, 2006

Chart with dojo

ใครอยาก plot graph บน browser
ตอนนี้มีทางเลือกง่ายๆอีกทางแล้ว

ใน dojo 0.4.x,
มีคน contribute dojo.charting package เข้าไปแล้ว

technique การ implement ที่ใช้ ก็คือ
ถ้าเป็นพวก browser ที่ support svg ก็จะใช้ svg ในการ render
แต่ถ้าเป็น IE ก็ใช้ vml ในการ render

การออกแบบ object Structure ก็ซับซ้อนนิดๆ
แต่ก็ทำให้มันยืดหยุ่นดี
ลอง plot graph เส้นง่ายๆแบบนี้ดู



เริ่มแรกสุด data ที่จะ plot ต้องอยู่ในรูป object dojo.collections.Store
var json = [
{ Id:1, year:2001, value:20000 },
{ Id:2, year:2002, value:30000 },
{ Id:3, year:2003, value:40000 },
{ Id:4, year:2004, value:35000 }
];

var store = new dojo.collections.Store();
store.setData(json);

กำหนด series ซึ่งเป็นชุดของข้อมูลที่จะ plot
(graph หนึ่งๆสามารถมีได้หลาย series)
var s1 = new dojo.charting.Series(
{
dataSource: store,
bindings: { x:"year", y:"value" },
color:"#0022bb",
label:"SFCT"
});

กำหนด Axis (แกน x, แกน y)
var xA = new dojo.charting.Axis();
xA.range = {upper:2004, lower:2001};
xA.origin="year";
xA.showTicks = true;
xA.label = "year";
xA.labels = [2001,2002,2003,2004];

var yA = new dojo.charting.Axis();
yA.range = {upper:100000, lower:0};
yA.showLines = true;
yA.showTicks = true;
yA.label = "value";
yA.labels = [ {label:"0", value:0}, {label:"100,000", value:100000} ];

สร้าง Plot object ซึ่งเป็นการจับคู่แกน axis
และกำหนด series ให้กับ Plot object
(1 Plot สามารถมีได้หลาย Series)
var p = new dojo.charting.Plot(xA, yA);
p.addSeries( {data:s1, plotter: dojo.charting.Plotters.Line} );

กำหนด PlotArea
var pa = new dojo.charting.PlotArea();
pa.size = {width:700, height:200};
pa.padding={top:20, right:40, bottom:30, left:70};
pa.plots.push(p);

สุดท้ายก็คือ Chart object
โดย 1 chart สามารถมีได้หลาย PlotArea
var chart = new dojo.charting.Chart(null, "Test", "description");
chart.addPlotArea({ x:0, y:0, plotArea:pa});


เวลาจะแสดง graph ก็แค่ bind object เข้ากับ dom element ที่ต้องการ
chart.node = dojo.byId("chartTest1");
chart.render();

Related link from Roti

Friday, December 22, 2006

โจทย์ Kata #3

P03 (*) Find the K'th element of a list.
The first element in the list is number 1.
Example:
* (element-at '(a b c d e) 3)
C


clisp
(defun element-at (lst pos)
(if (= pos 1)
(car lst)
(element-at-2 (cdr lst) (1- pos))))


haskell
elt :: [a] -> Int -> a
elt (x:_) 1 = x
elt (_:xs) n | n > 1 = elt xs (n-1)

Note: เครื่องหมาย '|' เรียกว่า Guard
Note: '_' คือ mathcing เข้ากับอะไรก็ได้ (pattern matching)

erlang
-module(p03).
-export([elt/2]).

elt([X|_],1) -> X;
elt([_|XS],N) when N > 1 -> elt(XS,N-1).

Note: when N > 1 ก็คือ Guard แบบเดียวกับ haskell

Note: ธรรมชาติคนเรามักจะเคยชินกับนับเลขไปข้างหน้า
คิดครั้งแรกผมก็ใช้วิธี +1 แล้วตรวจว่าได้เท่ากับค่าที่ต้องการหรือยัง
ซึ่ง code ที่ได้ ยาวต่างกันอย่างเห็นได้ชัด
ลองดู haskell แบบ + ไปข้างหน้า
element_at ::  [a] -> Int -> a
element_at xs pos = element_at_helper xs pos 1

element_at_helper :: [a] -> Int -> Int -> a
element_at_helper (x:xs) pos i
| pos == i = x
| otherwise = element_at_helper xs pos (1 + i)

Related link from Roti

Head On

ปกติผมไม่ค่อยชอบงาน installation เท่าไร
แต่งาน Head On ของ Cai Guo-Qiang นี่สะดุดตาดีนะ



Note: ชอบงานนี้อีกชิ้นหนึ่ง
Same Word, Same Seed, Same Root
ใช้ดอกไม้ไฟช่วยระบาย

Related link from Roti

Thursday, December 21, 2006

Hibernate Lucene Search

ผมมี Idea เรื่องที่จะใช้ Lucene ทำ search index
ของ Domain model มานานแล้ว
แต่ก็ไม่ได้โอกาสทำเสียที
มาตอนนี้ Hibernate 3.2 เขาทำ feature นี้ออกมาให้ใช้เรียบร้อยแล้ว
แถม integrate ได้เนียนดีด้วย

ลองดูตัวอย่างการใช้งาน

ขั้นแรกสุด ก็คือ "การระบุว่า domain ไหนที่ต้องการทำ index"
ตรงนี้เขาใช้ annotation Indexed เข้ามาช่วย
@Indexed(index="indexes/people")
@Entity
public class Person {
...
}

lucene มองสิ่งที่ถูก index ว่าเป็น document
ดังนั้นก็เลยต้องมีการระบุ tag DocumentId
ไว้ที่ primary key ด้วย
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@DocumentId
public Long getId() {
return id;
}

field ไหนที่ต้องการทำ index ก็ต้องระบุด้วย tag Field
@Field(index=Index.UN_TOKENISED)
public String getName() {
return name;
}


เพื่อให้ lucene ทำ index โดยอัตโนมัติ หลังจากที่เรา hibernate insert,update หรือ delete
เราก็ต้อง config add listenter เข้าไป
<event type="post-update">
<listener class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>

<event type="post-insert">
<listener class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>

<event type="post-delete">
<listener class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>


การ config ระบุว่าจะเก็บ index file ที่ไหน ทำผ่าน directory_provider
hibernate.search.default.directory_provider org.hibernate.search.store.FSDirectoryProvider
hibernate.search.default.indexDir=/usr/lucene/indexes


ส่วนการสืบค้นข้อมูล จะเห็นว่าเขาพยายามดึงให้เข้าไปใช้ org.hibernate.Query
ลองดูตัวอย่าง
Session session = HibernateUtil.getSession();
FullTextSession fullTextSession = Search.createFullTextSession(session);
QueryParser parser = new QueryParser("title", new StopAnalyzer());
org.apache.lucene.search.Query query = parser.parse("name:bunn");
org.hibernate.Query query2 = fullTextSession.createFullTextQuery(query);
List results = query2.list();

for (Person tmp : results) {
System.out.println("id =" + tmp.getId() + ",name = " + tmp.getName());
}

Related link from Roti

Wednesday, December 20, 2006

My Kata

PragDave เขาเคยพูดถึงประเด็น Code Kata ไว้
Kata คือ การฝึกรูปแบบการเคลื่อนที่ (patterns of movements practiced)
เป็นศัพท์ที่ใช้ในวงการพวก ศิลปะการป้องกันตัว
(ไม่รู้ว่านำไปเทียบกับ Drill ของพวกว่ายน้ำได้หรือเปล่า)

Dave เขาพูดถึงประเด็นเรื่องการฝึกเขียนโปรแกรม หรือฝึกคิดแก้โจทย์
โดยคุณสมบัติที่เขากำหนดไว้ ก็คือ
  • session ของการฝึกต้องไม่ยาวนัก (~= 30 ถึง 60 นาที)
  • scope ของเรื่องที่ฝึก ก็ต้องน้อยๆไม่เยอะนัก
  • code (ที่ได้จากฝึกนี้) เขียนแล้วโยนทิ้งไปเฉยๆ ไม่ได้เอาไปทำอะไรต่อ
  • ทำซ้ำ และไม่ต้องพะวงกับความผิดพลาด,
    เน้นให้ความสนใจไปที่ feedback เพื่อที่จะนำมาปรับปรุง technic ให้ดีขึ้น


เห็นด้วยกับเขานะ เพราะส่วนใหญ่
พวกเรามักจะฝึกเขียนโปรแกรม ด้วยการทำงานจริง
การทำงานจริง มันจะมีขอบเขต,กับปัจจัยบางอย่างกำหนดอยู่ (เช่นเวลา)
ทำให้เราไม่สามารถทดลอง หรือเล่นได้อย่างอิสระ

อย่างกรณีของผม
ยิ่งผมเรียนรู้ ภาษา, framework หลายๆแบบยิ่งขึ้น
สัดส่วนของความชำนาญก็จะลดลง
ตอนนี้ก็เลยต้องให้ความสำคัญกับ
"การสร้างความชำนาญ" มากขึ้น

เบื้องต้นก็คือ พยายามหาโจทย์ มาฝึกทำ
อย่างโจทย์ของที่นี่ ก็น่าสนใจดี
L-99: Ninety-Nine Lisp Problems
วันนี้ก็เลยเลือกข้อง่ายสุด มาทำ 2 ข้อ


P01 (*) Find the last box of a list.
Example:
* (my-last '(a b c d))
(D)


เริ่มด้วย clisp
(defun find-last (lst)
(if (null lst)
nil
(if (null (cdr lst) 1)
lst
(find-last (cdr lst)))))

haskell
find_last (x : []) = x
find_last (x : xs) = find_last xs

erlang
-module(p1).
-export([find_last/1]).

find_last([X | []]) ->
X;
find_last([X | XS]) ->
find_last(XS).


โจทย์แรกนี่ technic การใช้ pattern matching ชนะแฮะ
haskell ดู clean สุด
ส่วน erlang ถึงแม้จะใช้วิธีเดียวกับ haskell
แต่ syntax ไม่ clean เท่า

โจทย์ข้อต่อไป, โจทย์นี้ไม่ต่างจากโจทย์แรกเท่าไร

P02 (*) Find the last but one box of a list.
Example:
* (my-but-last '(a b c d))
(C D)


clisp
(defun but-last (lst)
(if (null lst)
nil
(if (null (cdr (cdr lst)))
lst
(but-last (cdr lst)))))

haskell
but_last (x : []) = [x]
but_last (x : y : []) = [x, y]
but_last (x : xs) = but_last xs

erlang
-module(p02).
-export([but_last/1]).

but_last([X | []]) ->
[X];
but_last([X | [Y | []]]) ->
[X,Y];
but_last([X | XS]) ->
but_last(XS).

Related link from Roti

Monday, December 18, 2006

Counter Component, Tapestry vs. Seaside

สืบเนื่องจากงาน NJUG (narisa java user group)
ที่ผมไปทำ workshop เรื่อง Tapestry
วันนี้ก็เลยยกตัวอย่างที่ทำ workshop ของ Tapestry
มาเปรียบเทียบกับ Seaside ที่ใช้ smalltalk เขียน

เรื่องด้วย Counter Component ก่อน



เริ่มที่ tapestry
การเขียน Tapestry Component ต้องใช้ 2 file
เป็น java class กับ Html template
public abstract class Counter extends BaseComponent {

@Persist
public abstract int getCount();
public abstract void setCount(int value);

public void increase() {
setCount(getCount() + 1);
}

public void decrease() {
setCount(getCount() - 1);
}
}

<div>
<h3><span jwcid="@Insert" value="ognl:count">0</span></h3>
<a href="#" jwcid="@DirectLink" listener="listener:increase">++</a>
<a href="#" jwcid="@DirectLink" listener="listener:decrease">--</a>
</div>


ฝั่งของ Seaside จะมีแค่ file เดียว
เพราะ Seaside ไม่ได้ใช้ template
(อันนี้เป็นประเด็นทางศาสนา ฝ่ายสนับสนุน
เชื่อว่า pure smalltalk มันงามแท้ๆ)

WAComponent subclass: #WACounter
instanceVariableNames: 'count'
classVariableNames: ''
poolDictionaries: ''
category: 'Seaside-Examples-Test'

initialize
super initialize.
self session registerObjectForBacktracking: self.
count := 0

count
^ count

decrease
count := count - 1

increase
count := count + 1


renderContentOn: html
html heading: count.
html anchor callback: [self increase]; text: '++'.
html space.
html anchor callback: [self decrease]; text: '--'

rendererClass
^ WARenderCanvas

ลองดูความสวยงาม(ที่เขาว่ากัน) ของการ render html ใน method renderContentOn:

ถ้าตัดประเด็นส่วน syntax ที่ไม่เหมือนกัน
กับ Technique การ implement ที่แตกต่างกันออกไป
ก็จะเห็นว่าทั้งสองฝ่ายมีแนวคิดไปในทำนองเดียวกัน นั่นคือ
  • ประเด็นเรื่องการ maintain state บนฝั่ง server,
    ที่ให้ framework เป็นคนจัดการให้เรา
    ทำให้เรามุ่งไปที่ กับ business logic ได้เต็มที่
  • การพยายามทำให้ event ที่เกิดที่ฝั่ง client กลืนเข้าเป็น
    เนื้อเดียวกับ script ที่ฝั่ง server,
    ซึ่งในแง่ของการร้อย event, ทางฝั่ง smalltalk ทำใด้เนียนกว่า


ลองดูการนำ component มาใช้งานบ้าง
ในตัวอย่างนี้ ก็คือการนำ Counter Component มาวางเรียงกันหลายๆอัน



(ซึ่งเวลาทำงานแล้ว แต่ละ component ก็จะ maintain state ของตัวเอง)
เริ่มด้วย Tapestry
เนื่องจากหน้าจอเราไม่ซับซ้อน ดังนั้นทางฝั่ง tapestry จึงใช้แค่ file เดียว
นั่นคือ html template file
<html>
<head>
<title>Multiple Component</title>
</head>
<body>
<span jwcid="@Counter">counter</span>
<hr/>
<span jwcid="@Counter">counter</span>
<hr/>
</body>
</html>

จะเห็นว่าเราใช้ Counter 2 อันมาวางเรียงกัน

ลองกลับไปดูที่ฝั่ง smalltalk บ้าง
ฝั่ง smalltalk จะยุ่งยากกว่าหน่อย
ตรงที่เราต้อง maintain ว่า component MultiCounter ของเรา
มี sub component อะไรบ้าง
โดยการ maintain, seaside กำหนดให้เราต้อง
implement method children
ที่ต้อง return sub component ทั้งหมดที่อยู่ภายใต้ component เรา

WAComponent subclass: #WAMultiCounter
instanceVariableNames: 'counters'
classVariableNames: ''
poolDictionaries: ''
category: 'Seaside-Examples-Test'

initialize
super initialize.
counters _ (1 to: 2) collect: [:i | WACounter new]

children
^ counters

renderContentOn: html
counters
do: [:ea | html render: ea]
separatedBy: [html horizontalRule]


สิ่งที่แตกต่างกันมากที่สุด สำหรับ seaside กับ tapestry
ก็คือเรื่อง flow ระหว่าง page
ในฝั่ง tapestry flow ระหว่าง page
ลองดู code ฝั่ง tapestry เวลาที่เรา flow เปลี่ยน page
@InjectPage("ShowEmployee")
public abstract ShowEmployee getShowEmployee();

public IPage doSubmit() {
ShowEmployee page = getShowEmployee();
page.setEmployee(getEmployee());
return page;
}

จะเห็นว่า tapestry กำหนดไว้ว่า ถ้า method ที่ถูกเรียกใช้
return page ไหนออกมา ก็ให้ render page นั้นแทนที่ page ปัจจุบัน

ดูฝั่ง smalltalk บ้าง ลองดูตัวอย่าง การ flow ของหน้าจอขายของ
self call: (WAStoreFillCart new cart: cart)
cart := self call:
((WAStoreCartConfirmation new cart: cart)
addMessage: 'Please verify your order:')
shipping := self call:
(WAStoreAddressEditor new
validateWith: [:a | a validate];
addMessage: 'Please enter your shipping address:';
yourself)
...

WAStoreFillCart, WAStoreCartConfirmation, WAStoreAddressEditor
คือหน้าจอย่อยๆ แต่ละหน้าจอ
จะเห็นว่าเราสามารถผูกหน้าจอต่างๆเข้าเป็น flow ได้
เวลาจะกระโดดไปหน้าจอไหน ก็ใช้ method call
แถมยังรับ return ค่ากลับจากหน้าจอนั้นได้อีก
ซึ่งถือว่าเป็น feature ที่ดูเด่นกว่า tapetry อย่างเห็นได้ชัด

Related link from Roti

Friday, December 15, 2006

Ebay Architecture

รู้ว่า ebay มี load เยอะแน่ๆ
แต่ไม่รู้ว่าเยอะขนาดนี้
  • 1,000,000,000 page view ต่อวัน
  • 212,000,000 registered user
  • 1,000,000,000 photos
  • 2,000,000 GB data
  • 26,000,000,000 sql execute ต่อวัน
    (อะไรวะ เฉลี่ยหนึ่งหน้า(page view) พ่อล่อไป 26 sql เลยเหรอ)
  • 3,000,000,000 api call ต่อเดือน


architecture เป็นไงบ้างหรือ
  • scale horizontal -> parallel boxes
  • presentation layer -> MSXML (พัฒนาเอง)
  • ใช้แค่ servlet, connection pools
    ภาษาอังกฤษ เขาใช้คำว่า "Throw out most of J2EE"
  • ไม่ใช้ session state
    ถ้าจำเป็นต้องมี ก็จะใช้ cookie หรือ database ชั่วคราว
  • app server ใช้ WebSphere (jdk 1.3)
    ทายสิว่าใช้กี่ตัว
    คำตอบคือ 15,000
  • Database -> oracle
    แค่ 200 database
    โดย split database ตาม functional
  • no stored procedures (บ้านเราคงไม่ชอบแนวนี้)
  • หลีกเลี่ยงการใช้ cpu ใน database layer
    ฉนั้น join กับ sorting กรุณาออกไปทำที่ application layer
  • minimize db transaction


แค่เห็นก็เหนื่อยแล้ว นี่ยังไม่พูดเรื่อง searhing กับ deployment
ใครอยากรู้ต่อ ลองอ่าน ebay presentation ดู

Note: นึกถึง project เมื่อ 2 ปีก่อน
ผมเคยมอง architecture ตรงข้ามกับ
พวกผู้ชำนาญการจากบริษัท IBM กับ คณะที่ปรึกษา (อาจารย์มหาวิทยาลัยมีชื่อแห่งหนึ่ง)
รวมทั้ง sales จาก IBM

ตอนนั้น production จะต้อง deploy บน box 4 cpu กับ box 2 cpu
ผมบอกให้ใช้ 2 cpu เป็น database server
สวน app server ให้ใช้ 4 cpu box

ทางฝั่งนู้น บอก "ไม่ไช่ๆ จากประสบการณ์ของผู้เชี่ยวชาญ
database box ต้องใช้ 4 cpu ต่างหาก"
แล้วก็ดื้อ config เครื่องไปตามนั้น
ถึงวันใช้จริง load ฝั่ง database เฉลี่ยไม่เกิน 10 %
ส่วนฝั่ง app อยู่แถว 60 % ถ้าเจอ spike ก็พุ่งปรึ๊ดเกือบติดเพดาน
ผ่านไป 2 เดือนพวกผู้เชี่ยวชาญก็เลยยอม โอน cpu จาก database
มาให้ application server

เวลาผ่านไปอีก 1 ปี ก็มีการย้ายเครื่องขึ้น linux mainframe
พวกผู้ชำนาญการก็ยังไม่เข็ด
จัดสรร computing resource ให้ database server เยอะกว่า app
แต่สุดท้ายก็ต้องโอน resource กลับมาให้ app อยู่ดี

เรื่องนี้สอนให้รู้ว่า
1. ผู้เชี่ยวชาญ ถ้าใช้ประสบการณ์เดิมๆ มาตัดสิน
บางครั้งก็เดาพลาดได้เหมือนกัน
2. คนเขียนโปรแกรมย่อมรู้ดีว่าตัวเองเขียน app ให้มี nature แบบไหน

Related link from Roti