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

Tuesday, December 12, 2006

Which Logos

Ridiculous fish เขามี quiz สนุกๆให้ทำ
Logos

Related link from Roti

Breaking Barriers

Breaking Barriers
"The Potential of Free and Open Source Software
for Sustainable Human Development"

เป็นเอกสาร case studies project ที่ใช้ FOSS

Related link from Roti

Wednesday, December 06, 2006

ภาษาไทย กับ Squeak

post ครั้งก่อนที่พูดถึงเรื่อง squeak
คุณ Vee post ถามว่า ได้ลองเล่นภาษาไทยบ้างหรือยัง
อืมม์จริงแฮะ เรายังไม่ได้ลองภาษาไทยเลย
ช่วง 2 วันมานี่ก็เลยนั่งค้น net ดู

เบื้องต้น ผมทดลองเขียน web application ด้วย seaside
แล้วทดลอง pass parameter เป็นภาษาไทยไปกับ query string
โดยให้ component มัน extract parameter ออกมาแสดงบน page
ก็พบว่ามันแสดงผลได้ถูกต้องดี

เมื่อไปค้นใน net ดู ก็เห็นว่าใน squeak3.8+
มีการรวม class ที่ support unicode ลงไปแล้ว
มีเอกสาร design คร่าวๆที่นี่
The Design and Implementation of Multilingualized Squeak
Note: เอกสารน่าจะ outdate แล้ว แต่พออ่านเป็นแนวทางได้

เมื่อ structure ภายใน support unicode แล้ว
ก็มาถึงประเด็นว่าเราจะ input ภาษาไทย
กับ display ภาษาไทย ได้อย่างไร

ผมทดลอง input ภาษาไทยเข้าไปที่ workspace
พบว่า ถ้าใช้ virtual machine ที่อยู่บน mac
จะสามารถ key ข้อมูลเข้าไปได้ (cursor ขยับ แต่ไม่แสดงตัวอักษร)
ส่วน virtual machine ที่อยู่บน linux ubuntu
ไม่แสดงอาการว่ามี keyboard event วิ่งเข้าไปเลย
ซึ่งลองค้นๆดูแล้ว เห็นมีคนที่ทำเรื่องภาษา russia
พูดถึง key input error ของ virtual machine
ที่อยู่บน Linux ไว้แบบนี้ (ผมยังไม่ได้ลองดูนะว่าทำได้จริงหรือไม่)
I 've found that stock VM doesn't work for me (I've tried FreeBSD and Kubuntu).
...
When I'm changing x2sqKey (it is x2sqKeyPlain) to x2sqKeyInput it starts
working:

///in sqUnixX11.c
typedef int (*x2sqKey_t)(XKeyEvent *xevt);

static int x2sqKeyPlain(XKeyEvent *xevt);
static int x2sqKeyInput(XKeyEvent *xevt);

static x2sqKey_t x2sqKey= x2sqKeyInput;

(อ้างอิง จาก [HACK] Unicode keyboard input and fonts)

กลับมาเรื่องแสดงผลต่อ
ผมทดลอง import "True Type Font" ที่มีภาษาไทย เข้าไปใน squeak
แล้วลองเปลี่ยน font ในการแสดงผลดู
ดีขึ้นแฮะ จากที่แสดงผลเป้น blank ก็ขึ้นเป็น กรอบสี่เหลี่ยมว่างๆแทน
ลองลง package MorphicTTFReader แล้วลอง browse TTF file ดู
ก็พบว่ามันขึ้นภาษาไทยได้



ลองใล่ดูใน browser บ้าง
ก็พบว่ามันมี class ที่ชื่อ LanguageEnvironment อยู่
ซึ่งมี class ที่ extend ตัวนี้อยู่หลายตัวที่เดียว เช่น
JapaneseEnvironment, GreekEnvironment, ...
ถ้าอ่านดูใน code จะเห็นว่า class นี้รับผิดชอบเรื่องใหญ่ๆอยู่ 3 เรื่องคือ
  • การ interpret Keyboard input
    อันนี้มี class หลักคือ KeyboardInputInterpreter
  • ClipboardInterpreter
  • TextConverter
    ใช้ในการแปลง stream


ดูแล้วการ custom ให้ใช้ภาษาไทย ก็ไม่น่าจะยากเกินไปนัก
(คุณ Vee เสร็จ thesis เมื่อไรก็บอกด้วยนะครับ
ผมจะชวนทำเรื่องนี้ด้วยกัน)

Note: ใน OLPC ก็มี squeak ฉบับ Etoys ลงไว้ด้วยเหมือนกัน
ไม่รู้ทางทืม nectec ได้เตรียมเรื่องภาษาไทยไว้บนนั้นหรือยัง

Related link from Roti

Monday, December 04, 2006

ทดสอบ concurrency บน Seaside

ในการเรียนรู้ Web application framework ตัวใหม่ๆ
ประเด็นแรกที่ผมต้องการรู้ก็คือ
performance ในส่วน concurrency มันเป็นอย่างไร
จะได้ใช้เป็นเกณฑ์ตัดสินได้ถูกว่า solution ไหน ควรจะใช้ framework อะไร

seaside ก็เช่นเดียวกัน
เนื่องจากมันเป็น component oriented ของแท้
ผมก็เลยอยากรู้ว่ามันจะ handler load ได้ดีแค่ไหน

ผมทดลองสร้าง helloworld component ขึ้นมา
(แบบเดียวกับตอนที่ทดลอง yaws)
โดยมี method สำคัญๆแค่ 2 อันคือ
initialRequest: aRequest
name := (aRequest at: 'name')

ใช้ extract parameter ที่ส่งเข้ามา
โดยเก็บค่าที่ได้ไว้ใน instance variable ที่ชื่อ name
renderContentOn: html
html heading: 'Hello ', (self name)

อันนี้เป็น method ที่ใช้ในการ render

ทดลองสั่ง ab -n 300 -c 100
ผลลัพท์ที่ได้ ถ้า run บน squeak
ก็จะเกิด exception บานตะไท
ลดเหลือ -c 10 ถึงจะ run ผ่าน

ด้วยความอยากรู้ว่าถ้าเป็น commercial product หล่ะ
จะเป็นอย่างไร
ก็เลยไป load VisualWorks
ของ Cincom Smalltalk มาลองดู
(มันเป็น dual license, ถ้าเป็น research, learning
ก็จะ free)
ได้ผลลัพท์ดังนี้

Server Software:
Server Hostname: 127.0.0.1
Server Port: 8008

Document Path: /seaside/go/hello?name=world
Document Length: 1644 bytes

Concurrency Level: 100
Time taken for tests: 2.806422 seconds
Complete requests: 300
Failed requests: 0
Write errors: 0
Total transferred: 580056 bytes
HTML transferred: 493200 bytes
Requests per second: 106.90 [#/sec] (mean)
Time per request: 935.474 [ms] (mean)
Time per request: 9.355 [ms] (mean, across all concurrent requests)
Transfer rate: 201.68 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 6 9.8 0 25
Processing: 633 874 98.2 900 992
Waiting: 631 873 98.1 899 991
Total: 658 880 89.2 900 992

Percentage of the requests served within a certain time (ms)
50% 900
66% 933
75% 962
80% 963
90% 968
95% 977
98% 984
99% 985
100% 992 (longest request)

ข้อสังเกต
  • document length ที่ได้ใหญ่มาก
    เพราะว่า seaside มัน generate อะไรต่อมิอะไรให้เยอะแยะเชียว
  • ช่วงเวลาระหว่างกลุ่ม 50% แรก กับที่เหลือ ต่างกันนิดเดียว?

Related link from Roti

New way to config spring bean.

Rod Johnson เสนอ Idea ใหม่ในการ config spring bean
โดยใช้ annotation เข้ามาช่วย
ฟังเผินๆ ก็ดูไม่มีอะไรใหม่
แต่ลองดูวิธีที่เขา config ดูเสียก่อน
@Configuration
public class MyConfig {
@Bean
public Person rod() {
return new Person("Rod Johnson");
}
@Bean(scope = Scope.PROTOTYPE)
public Book book() {
Book book = new Book("Expert One-on-One J2EE Design and Development");
book.setAuthor(rod()); // rod() method is actually a bean reference !
return book;
}
}

จะเห็นว่าเขาไม่ได้ปน annotation เข้าไปกับ bean code ตรงๆ
แต่เป็นการแยก configuration ออกมาต่างหาก
ทำให้ออกไปในแนวของ DSL

ข้อดีที่แน่นอนอย่างหนึ่ง ก็คือ spelling
เป็น java class อย่างนี้แล้ว
ไม่มีกรณีสะกดผิดแน่นอน

Related link from Roti

Friday, December 01, 2006

ทดลอง Squeak

ช่วงนี้มีเวลาว่าง ก็เลยลองเล่น Seaside
ซึ่งเป็น web application framework บน smalltalk
,ตัว smalltalk ที่ใช้ก็คือ Squeak

หลังจากทดลองพัฒนา program บน squeak ดู
ก็พบว่า มันให้ความรู้สึกแบบเดียวกับ emacs
นั่นคือความรู้สึก powerful
ที่เกิดจากความสามารถในการ extend สภาพแวดล้อมได้ทันที
ไม่ต้องเข้า cycle แก้ไข -> ปิดโปรแกรม -> เปิดโปรแกรม

ที่เป็นเช่นนี้เพราะ squeak ไม่แยกความแตกต่างระหว่าง
program code กับ program state
ทุกอย่างผสม และทำงานอยู่บน virtual machine เดียวกัน
การ save snapshot ลง image file
นอกจากจะ save program code แล้ว
พวก program state ต่างๆ เช่น undo history
ก็เก็บลง image file ไปด้วย
ทำให้เราสามารถทำแบบนี้ได้ เช่น
วันนี้ save บน mac, พรุ่งนี้เอาไปเปิดบน linux
ไม่พอใจอะไรบางอย่าง เลบสั่ง redo สิ่งที่ทำไปเมื่อวาน

ในช่วงหลังๆที่ concept virtualize เริ่มเป็นที่นิยม
เราเริ่มจะได้ยินคนพูดถึงประเด็น
ของการเก็บสภาพแวดล้อมการพัฒนาลงใน image file
(อาจจะใช้ vmware หรือ xen)
พอจะเขียนโปรแกรม ก็เปิด image file ขึ้นมา
เขียนๆๆๆ จบวันก็ save image file
วันถัดไป พอเปิดขึ้นมาสภาพแวดล้อมทุกอย่างจะเหมือนเมื่อวานหมด ทำงานต่อได้ทันที
หรือวันดีคืนดี อยากย้ายเครื่อง ก็เอาไปแต่ image file
ไม่ต้องไปนั่ง setup เครื่องให้ยุ่งยาก
หรือถ้ามีพนักงานใหม่เข้ามา ก็ copy image file ไปให้
จะได้เริ่มทำงานได้เลย

กลับมาที่ squeak ต่อ
ปัญหาที่พบใน squeak ก็คือเรื่อง version conflict ของ package ต่างๆ
เช่นผมอยากได้ feature x, y แล้วก็ z
โดย x ทำงานได้กับ y version 2.0
แต่ก็ไปติดตรง z จะทำงานได้ต่อเมื่อ y เป็น version 3.0 เท่านั้น
เกิดอาการรักพี่เสียดายน้องขึ้นมาทันที

เล่นมา 2 วัน
ได้ bad knowhow มาอื้อเลย

Related link from Roti

Wednesday, November 29, 2006

[haskell] currying อีกที

พอรู้จัก currying ก็เลยเริ่มสนุก
ใน tutorial เขาบอกว่า

Haskell only supports one-argument functions.

ดังนั้นประโยค f x y
เวลา evaluate จะทำตามลำดับแบบนี้
((f x) y)

เครื่องหมาย + ก็เป็น function ด้วย
3 + 2 เวลา run ใน haskell
ก็ควรจะเริ่ม evaluate (3 +) ผลลัพท์ที่ได้คือ function
จากนั้นค่อย call function นั้น ด้วย parameter 2 อีกที

ลองดูสิว่าประโยค 3 + 2
เขียนแบบไหนได้อีกบ้าง

(3+) 2
(+3) 2
(+) 3 2

Note: ขออภัยแฟนๆ python ที่เอา "many way to do one thing" มาให้ดู

ส่วน statement แบบนี้

add(3,2)

เห็นแล้วอย่าสับสน
ใน haskell, (3,2) คือ datatype ชนิด tuple
ไม่ได้เป็น parameter syntax แบบที่เรารู้จัก

Related link from Roti

Tuesday, November 28, 2006

Prototype Parameter?

คุณ pete ถามมา

คือเวลาผมใช้ prototype ส่ง parameter เนี่ยมันก็จะอยู่ในรูป 'name=abcd&surname=refd' ใช่ไหมครับ ที่อยากถามคือถ้าหากผมมีตัวแปรชื่อ check เป็น array รับค่ามาจาก checkbox ในเว็บเพจเนี่ย
การส่งตัวแปรแบบ array สามารถทำได้หรือเปล่าครับ


ถ้าใช้ method serialize ใน Form object
มันจะทำให้เราเองครับ
ลองไปทดสอบดูได้ที่ web นี้ครับ
Prototype: Form Serialization and Deserialization
มีหน้าจอให้เราลอง submit ดูด้วย

method ข้างบน มันจะให้ค่าทั้งหมดใน form กลับมา
แต่ถ้าเกิดเราอยากได้แค่ array ของเฉพาะตัวแปรนั้นๆชุดเดียว
ก็ลองดูที่ blog นี้ครับ
Quick Tip: Getting array of checked values using prototype

Related link from Roti

Maven vs. Ant

ช่วงนี้ mk, community builder ของเรา
เกิดต้องหันมาจับ java
ก็เลย post ตั้งคำถาม ไว้จำนวนหนึ่ง
ผมจะค่อยๆหยิบมาทยอยเขียนตอบ
โดยเริ่มที่คำถาม "Ant กับ Maven ต่างกันอย่างไร"

เริ่มที่ Ant ก่อน
Ant คือ build tool ตัวหนึ่ง
เขียนโดย James Duncan Davidson ซึ่งเป็นคนเขียน Tomcat
วัตถุประสงค์ในตอนแรกที่เขาเขียน ก็ชัดเจนมาก
นั่นคือ ใช้ในการ build tomcat

การทำงานของ ant ก็คล้ายๆพวก makefile ของพวก unix
มีหน้าที่คำ build ให้ได้ target file
ซึ่งอาจจะเป็น jar file, war file, javadoc file, ฯลฯ
ซึ่งแน่นอนว่าประเด็นที่สำคัญในการ build ก็คือ ลำดับการทำงาน
ดังนั้นการเขียน ant ก็คือ การร้อยเรียงลำดับการทำงานต่างๆเข้าด้วยกัน

ที่นี้แล้ว maven หล่ะ
maven ก็มีที่กำเนิดคล้ายๆ ant
โดยเกิดในระหว่างการพัฒนาโปรเจค jakarta turbine
(อันนี้เป็น web framework รุ่นแรกๆของ java)
สาเหตุก็คือเมื่อ project ใหญ่ขึ้นมาก
เรามักจะ split project ออกเป็น sub project ย่อยๆ
project structure มีลักษณะเป็น hierarchy structure
แต่ละ sub project ก็มี team ทำงานแยกกัน

+ project X
+ sub project Domain
+ sub project Service
+ sub project Web App
+ sub project Main App
+ sub project Admin App

คนที่เคยใช้ ant กับ project ที่มีลักษณะแบบนี้
จะรู้ว่าการ maintain ant file เป็นเรื่องน่ารำคาญใจเรื่องหนึ่งทีเดียว
การเขียน ant script จะใช้วิธี copy & paste ไปมา
ถ้าเปิดดู build file แต่ละตัว ก็จะเห็นว่ามีส่วนที่ซ้ำกันไปซ้ำกันมาเต็มไปหมด
jar file ของ subproject ก็ซ้ำๆกัน
แถมยังถูก check-in เข้า version control
นอกจากนี้ยังต้องทำ ant script ตัวใหญ่
ไว้คอยคุมลำดับการ build ของโปรเจค, ฯลฯ

maven ก็เลยเกิดมา เพื่อเติมรอยโหว่ในจุดนี้
ลองดูวัตถุประสงค์ของ maven

  • Making the build process easy
    หลักการของ maven ก็คือ ทำอย่างไรให้ช่วงเวลาที่ใช้ในการ setup
    build file น้อยที่สุด
    อันนี้ก็เลยมี feature พวก archetype,
    library repository ที่ช่วยแยก library ออกจาก project

  • Providing a uniform build system
    ถ้าเราเข้าใจกลไกการ build ของ project ที่ใช้ maven อันหนึ่ง
    เราก็จะเข้าใจ project อื่นๆที่ใช้ maven ทันที

  • Providing quality project information
    maven ให้ความสำคัญของ กลไกการ generate document จาก code

  • Providing guidelines for best practices development
    อันนี้ค่อนข้างสำคัญ, สมัยแรกๆที่ใช้ ant
    project แต่ละ project ก็จะมี idea การวางโครงสร้าง directory, การตั้งชื่อ
    กันหลากหลายมาก
    ส่วนของ maven ก็เลยกำหนดมาตรฐาน (default, best practices) ขึ้นมาอันหนึ่ง

  • Allowing transparent migration to new features
    ตรงนี้ประสบการณ์ผมไม่ตรงที่โฆษณาไว้
    จำได้ว่า maven1.x เคย upgrade version แล้ว build มันมั่ว
    ต้องลบทิ้งลงใหม่


feature ที่ผมชอบใน maven ก็มี
  • share repository
    library ที่เราใช้ใน project ต่างๆ จะรวมเก็บไว้ที่เดียว
  • transitive dependency
    ระบุ dependency เฉพาะตัวที่เราต้องการ
    พวกที่เหลือเดี๋ยว maven จะหามาให้เอง
  • การเขียน build file ในลักษณะ extend
    พวก sub project จะ extend คุณสมบัติจาก parent project


ส่วนที่ไม่ค่อยชอบ
  • เริ่มต้นไม่ถูก ไม่รู้จะเรียกคำสั่งไหน เมื่อไร
    จะรู้ได้ไงว่ามีคำสั่งอะไรใช้บ้าง
  • การ integrate project ที่มีโครงสร้างแบบ hierarchy
    เข้ากับ eclipse (IDE) นี่ มันไม่ธรรมดาเหมือนกัน
    ไม่ยาก แต่ก็ไม่สนุก


ที่นี้ก็มาถึงคำถามสำหรับการเลือกใช้
สำหรับผมนะ
project เขียนคนเดียว ไม่ยั่งยืน ผมใช้ build ของ IDE
project เล็กๆที่เผยแพร่ให้คนอื่น ใช้ ant
project กลางๆ ถ้ารีบก็ใช้ ant, ถ้าไม่รีบก็ maven
(เนื่องจากการเรียนรู้ maven ใช้เวลามากกว่า ant)
project ใหญ่เป็นเรื่องเป็นราว ใช้ maven

Related link from Roti

Monday, November 27, 2006

Currying

Christopher Diggins เขาเขียนเรื่อง Typing like a Functional Programme
article นี้ช่วยเฉลยให้ผมเข้าใจเรื่อง currying ขึ้นอีกหน่อย
(เห็นศัพท์นี้บ่อย แต่ไม่ get ว่ามันคืออะไร)

ตัวอย่างที่เขาอธิบายก็คือ
function add_ints
ปกติเวลาเราเขียน imperative language เช่น java
add_ints จะเขียนแบบนี้
public int add_ints(int x, int y) { return x + y; }


ถ้าจะเขียนเป็น functional language (ocaml, haskell, ...)
เราต้อง declare type ก่อน
ซึ่ง type ของ add_ints เขียนได้ดังนี้
add_ints: int -> int -> int

อ่านได้ว่า add_ints รับ parameter ชนิด int 2 ตัว
แล้ว return ค่าเป็น int

จะเห็นว่ามันดูแปลกๆ เพราะในความคิดแบบ imperative ของเราแล้ว
มันควรจะเขียนแบบนี้มากกว่า
add_ints: (int , int) -> int
add_ints: (int -> int) -> int


ที่เป็นเช่นนี้เพราะ ใน Haskell (ขอยกตัวอย่างเฉพาะ haskell) มันพิสดารตรงที่ว่า
ถ้าเรา pass parameter แค่ตัวเดียวให้กับ add_ints version java
ผลก็คือ code จะ compile ไม่ผ่าน
แต่สำหรับกับพวก haskell แล้ว
การ pass parameter ตัวเดียวให้กับ add_ints
มันจะ return กลับมาเป็น function อีก function หนึ่งแทน
โดย function นั้นจะรับ parameter ที่เป็น int ตัวเดียว
และเมื่อเรา call function นั้นอีกทอด เราก็จะได้ผลบวกที่ต้องการ

ลองดูตัวอย่าง
add_ints :: int -> int -> int
add_ints x y = x + y

ลอง run ดู
Main> add_ints 1 7
8
Main> (add_ints 2) 4
6


ใครสนใจอ่าน currying ต่อได้ใน wikipedia

Related link from Roti

Emacs Register

สงสัยเรื่อง feature register ใน emacs มานานแล้ว
วันนี้ได้ฤกษ์หาคำตอบสักที
register ใน emacs ก็คือตัวแปรที่เราสามารถ save ค่าต่างๆลงไปได้
โดยปกติเราจะตั้งชื่อให้กับแต่ละ register ด้วยตัวอักษรหนึ่งตัว

ที่นี้ก็มาถึงคำถามถัดไป
แล้วอะไรหล่ะที่เก็บลง register ได้บ้าง
ตรงนี้แหล่ะที่มันดูดี
เนื่องจากเราสามารถเก็บ content ได้หลากหลายเช่น
  • เก็บ String, Number
    อันนี้ธรรมดามาก
  • เก็บ Marker (position ใน buffer)
  • Window or Frame configuration position
    ใช้เก็บ ตำแหน่งและขนาดของ window หรือ frame
  • file
    ใช้เก็บชื่อ file ทำให้กระโดดไป edit file ที่ต้องการได้ง่ายๆ


short-cut ที่เราใช้สร้าง register จะค่อนข้างหลากหลาย
เนื่องจากมีประเภท content ที่เก็บได้หลายแบบ
เช่น ถ้าจะจำหน้าต่างที่เปิดในปัจจุบัน เราจะใช้ c-x r w
ตามด้วยชื่อ register
หรือถ้าจะจำตำแหน่งใน buffer ก็จะใช้ c-x r <SPC>
แล้วก็ตามด้วยชื่อ register เช่นกัน

ส่วน short-cut ที่ใช้ กระโดดไปยัง register ที่ต้องการ
จะมีตัวเดียว ใช้ได้กับทุกประเภท content
นั่นคือ c-x r j ตามด้วยชื่อ register

ตอนนี้ที่ผมใช้เป็นหลัก ก็คือการจำ window ที่เปิดไว้
จะได้กระโดดกลับมาที่ layout ที่ต้องการได้ง่ายๆ

Related link from Roti

Sunday, November 26, 2006

Emacs-23

ใครที่เคยใช้ emacs จะมีความรู้สึกอย่างหนึ่งก็คือ
font มันไม่สวยเอาเสียเลย
เนื่องจากมันไม่ใช่ anti-alias

ส่วนตัวผม, ปกติผมใช้ emacs บน mac
emacs บนนั้น, font มันจะเนียนสวยมาก
เวลามาใช้บน linux จะรู้สึกขัดอกขัดใจเล็กน้อย

ตอนนี้ที่ branch emacs-unicode-2
(ซึ่งเขาว่ากันว่า จะเป็น branch สำหรับ emacs version 23)
มีการ merge เอา branch ที่มี feature xft เข้ามาแล้ว
ดังนั้นใครที่ต้องการ emacs xft
ก็ควรจะ checkout ออกจาก branch นี้โดยตรง

ขั้นตอนการ build สามารถอ่านได้ที่นี้
XftGnuEmacs

ผมลองบน ubuntu แล้ว สวยงามดีที่เดี่ยว


Related link from Roti

Friday, November 24, 2006

CForms

CForms คือ framework ย่อยๆใน Apache Cocoon
ที่ออกแบบมาเพื่อใช้จัดการเรื่องการ submit ข้อมูลผ่าน html form

CForms มีองค์ประกอบ 2 ส่วนคือ
  • Form Model คือ definition ของ data ที่จะ submit เข้ามา
  • Form Template คือ layout ของ widget
    ที่จะสร้างเป็นหน้าจอให้ผู้ใช้เห็น

แน่นอนว่าด้วยชื่อเสียงของ Cocoon ที่เป็น Framework แห่ง xml
ทั้ง form model และ template ก็ต้อง describe ด้วย xml

สำหรับตัว Form model สิ่งที่ผมชอบก็คือ
เรากำหนด definition ของ form ผ่านทาง xml อย่างเดียว
ไม่จำเป็นต้องไปเขียน java class ใดๆเลย
ลองดูตัวอย่าง form model
<fd:form
xmlns:fd="http://apache.org/cocoon/forms/1.0#definition">

<fd:widgets>
<fd:field id="name" required="true">
<fd:label>Name:</fd:label>
<fd:datatype base="string"/>
<fd:validation>
<fd:length min="2"/>
</fd:validation>
</fd:field>

<fd:field id="email" required="true">
<fd:label>Email address:</fd:label>
<fd:datatype base="string"/>
<fd:validation>
<fd:email/>
</fd:validation>
</fd:field>

</fd:widgets>
</fd:form>

จะเห็นว่าใน form หนึ่งๆ ประกอบด้วย widget หลายๆอัน
ในตัวอย่างของเราชนิดของ widget ที่ใช้ก็คือ Field widget
โดยแต่ละ field ก็จะมีการกำหนด label, datatype, validation
(CForms provide build-in widget มาให้พอสมควร
และเปิดช่องให้เรา define custom widget เข้าไปได้)

ส่วนหน้าตาของ form template ก็จะมีหน้าตาแบบนี้
<html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">

<jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>

<head>
<title>Registration form</title>
</head>
<body>
<h1>Registration</h1>
<ft:form-template action="#{$continuation/id}.continue" method="POST">
<ft:widget-label id="name"/>
<ft:widget id="name"/>
<br/>
<ft:widget-label id="email"/>
<ft:widget id="email"/>

<input type="submit"/>
</ft:form-template>
</body>
</html>

จะเห็นว่ามีการ map widget เข้ากับบริเวณต่างๆใน template
เมื่อมีการ render page นี้, component ย่อยแต่ละตัว ก็จะรับผิดชอบ render ตัวเอง
แล้วนำผลลัพท์ที่ได้มาแทนที่ลงไป

และเมื่อ form ถูก submit กลับเข้ามา,
CForms ก็จะแจกให้ component แต่ละตัว
รับผิดชอบในการดึงข้อมูลออกจาก HttpRequest
และนำข้อมูลท่ี่ได้ไปใส่ไว้ใน form instance

เวลาจะนำ CForm ไปใช้ เรามักจะใช้ผ่าน flowscript
ลองดูตัวอย่าง
cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js");

function registration() {
var form = new Form("registration_definition.xml");

form.showForm("registration-display-pipeline");

var viewData = { "username" : form.getChild("name").getValue() }
cocoon.sendPage("registration-success-pipeline", viewData);
}

โปรแกรมตัวอย่างนี้ จะแสดงหน้าจอให้ user กรอก และเมื่อ user submit เข้ามา
ก็จะทำการแสดงหน้า success
จุดเด่นของ flowscript ก็คือ
1. ใช้ syntax javascript ซึ่ง implement ได้เร็วกว่า java code
2. implement โดยใช้ continuation ทำให้เขียน code อยู่เป็นกลุ่มก้อน ง่ายต่อการ maintain
framework ปัจจุบันที่ใช้ continuation style แบบนี้ ก็คือ Seaside ที่ใช้ภาษา smalltalk,
แล้วก็ web framework ของ DrScheme ที่เขียนด้วยภาษา scheme,
ส่วนในค่าย java นี้ก็มีแค่ cocoon ตัวเดียว
(่jetty continuations เป็นอีกเรื่องหนึ่ง ที่ใช้ศัทพ์เดียวกัน แต่คนละ concept)

ในโปรแกรมข้างบน จุดที่เกิด continuation ก็คือ method form.showForm
ซึ่ง cocoon จะทำการ pause execution ไว้ก่อน
เมื่อ user submit เข้ามา จึงจะเริ่มทำงานต่อจากจุดเดิม

ทั้งหมดข้างบน เป็นแค่ demo การใช้ง่านง่ายๆ
แต่เวลานำไปใช้จริงๆ ข้อมูลนำมาใช้บน form
มักจะอยู่ในรูป xml หรือ beans
ดังนั้นจึงต้องมีขบวนการ binding เกิดขึ้นด้วย
CForms ใช้ XPath เข้ามาช่วยในการ binding
ลองดูตัวอย่าง
<fb:context xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" path="/" >
<fb:value id="name" path="name"/>
<fb:value id="email" path="email"/>
</fb:context>

attribute path ข้างบน ก็คือ xpath ที่ชี้ไปยัง xml element ที่ต้องการ bind

ส่วนประเด็นสมัยนิยม ก็คือเรื่อง Ajax
ตัว CForms ใช้หลักการ partial renderer
ดังนั้นในตอนที่เราเขียน template ก็ต้องใช้พวก tag พวก div, span
เข้ามา grouping บริเวณที่จะเกิดการ replace
ส่วนการ handler request
เนื่องจาก cocoon เป็นเจ้าพ่อแห่ง pipeline
ดังนั้นเราก็แค่แทรก tranformer กับ selector ที่ใช้ handle ajax
เข้าไปใน sitemap

เท่าที่ดูมา CForms ก็ดูเข้าท่าดีเหมือนกัน
ประเด็นที่ต่างกับพวก Tapestry หรือ JSF ก็คือ
  • ใน Tapestry form จะปนๆอยู่กับ controller
    แต่ใน CForms, Forms จะแยกออกจาก controller อย่างชัดเจน
    (seperation of concern จะดีกว่า)
  • ใน JSF เวลาจะเลือกประเภทการ render เราทำได้โดยเปลี่ยน RenderKit
    ใน CForms เนื่องจากเราใช้ concept ของการ transform
    ดังนั้นการเปลี่ยนการ render ก็คือการเปลี่ยน XLST
    ส่วนใน Tapestry ไม่มี concept แบบนี้
  • สามารถใช้ได้โดยไม่ต้องรู้ java เลย


ส่วนข้อเสียที่เห็นได้ชัดก็คือ XML เยอะจริงๆ

Related link from Roti

Thursday, November 23, 2006

Eclipse JDT, ASTParser

ปัจจัยหนึ่งที่ทำให้ Eclipse ได้เปรียบ netbeans (หมายถึง netbeans ตัวเก่าๆนะ ตัวใหม่นี่ยังไม่มีข้อมูล)
ก็คือตัว Java Parser
ซึ่ง bundle อยู่ใน package eclipse JDT (java development tools)

เมื่อใดก็ตามที่เราเปิด source code ขึ้นมาใน editor
eclipse จะใช้ ASTParser ทำการ parse source code
ไปเป็น AST (Abstract Syntax Tree)
ข้อมูล AST ที่ได้จะถูกนำไปใช้หลายๆที่ เช่นใช้ใน Outline view
แต่ที่หนึ่งที่สำคัญก็คือ มันใช้เป็นข้อมูลสำหรับการทำ Refactoring

ลองดู code ตัวอย่างนี้
สมมติเรามี

int x;
x = 2 * 3;

ถ้าเราต้องการ refactor ยุบให้เหลือแค่นี้
int x = 2 * 3;


ผมจะลองเขียน code ให้ดูว่า ถ้าใช้ Eclipse JDT api
เข้ามายุบ source code ข้างบนนั้น
เราจะต้องเขียนอย่างไรบ้าง
เริ่มด้วยการ parse java code ก่อน

ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setKind(ASTParser.K_STATEMENTS);
String src = "int x; x= 2 * 3;";
parser.setSource(src.toCharArray());
ASTNode node = parser.createAST(null);

AST.JLS3 คือ version ของ java code ที่เราจะ parse
ตัว ASTParser มี choice ให้เลือกอยู่แค่ 2 ตัวคือ JLS3 กับ JLS2
ตัว JLS3 จะ support syntax ของ Java 1.5

ขั้นถัดไปก็คือทำการหาว่า จุดไหนที่ทำ refactor ได้บ้าง
โดยเราจะค้นหา statement ที่เป็น variable declaration
กับ statement ที่เป็น assignment
การค้นหานี้ เราจะใช้ visitor pattern ในการค้นหา
(Note: เขียนแบบสั้นๆ พอให้ทำงานได้ ไม่เช็คเงื่อนไขใดๆทั้งสิ้น)

final ArrayList list = new ArrayList();
final HashMap map = new HashMap();

node.accept(new ASTVisitor() {
@SuppressWarnings("unchecked")
public boolean visit(VariableDeclarationStatement node) {
list.add(node);
return false;
}

public boolean visit(Assignment assignment) {
Expression left = assignment.getLeftHandSide();
map.put(left.toString(), assignment);
return false;
}
});

ขั้นสุดท้ายก็คือการแก้ไข AST ให้ตรงตามที่เราต้องการ
// loop ไปตาม declaration statement ที่หามาได้
for (Iterator iter = list.iterator(); iter.hasNext();) {

VariableDeclarationStatement stmt = (VariableDeclarationStatement) iter.next();
// เนื่องจาก declarable statement หนึ่งๆ
// มีได้หลายตัวแปรพร้อมๆกัน
// ดังนั้นเราต้อง loop ไปตามตัวแปรแต่ละตัวอีกที
List fragment = stmt.fragments();
for (Iterator iterator = fragment.iterator(); iterator.hasNext();) {
VariableDeclarationFragment f = (VariableDeclarationFragment) iterator.next();
//ทำเฉพาะ case ที่ตัวแปรไม่ได้มีการ initlize ไว้
if (f.getInitializer() == null) {
// ค้นหา assigment ที่มีตัวแปรเดียวกัน
Assignment assignment = (Assignment) map.get(f.getName().toString());
Expression right = assignment.getRightHandSide();
f.setInitializer(copyExpression(right));
assignment.getParent().delete();
}
}
}

จะเห็นว่า เราแค่ copy right hand side ของ Assignment statement
มาใส่ตรง Initialize value ของ Variable declaration Statement
จากนั้นก็ลบ assignment statement ทิ้งเสีย

สนใจรายละเอียด
ลองตามไปอ่าน Tutorial ของ Eclipse ที่ชื่อ Abstract Syntax Tree

Related link from Roti

Wednesday, November 22, 2006

F3

ห่างเหินจาก Swing มานาน
เลยพึ่งได้ยินข่าว F3
F3 ย่อจาก "Form Follows Function".
เป็น project ของ Chris Oliver
โดยเขา design declarative script language
สำหรับการสร้าง gui ขึ้นมา
จุดเด่นของมันก็คือ มัน support ทั้ง Swing และ Java2D

ลองดูตัวอย่าง ที่ผม copy มาจาก tutorial ของเขา
Frame {
title: "Hello World F3"
width: 200
content: Label {
text: "Hello World"
}
visible: true
}



จะเห็นว่า ภาษามีลักษณะคล้ายๆ javascript

ที่นี้ลองแบบมี model + binding ด้วย
class HelloWorldModel {
attribute saying: String;
}

var model = HelloWorldModel {
saying: "Hello World"
};

var win = Frame {
title: "Hello World F3"
width: 200
content: Label {
text: bind model.saying
}
visible: true
};



ลองดูลูกผสมระหว่าง 2D กับ Swing

Canvas {
content: Group {
transform: []
content:
[Rect {
x: 20
y: 20
height: 80
width: 300
arcHeight: 20
arcWidth: 20
fill: cyan
stroke: purple
strokeWidth: 2
},
Ellipse {
cx: 150
cy: 80
radiusX: 100
radiusY: 50
fill: orange
stroke: blue
strokeWidth: 2
},
View {
transform: translate(150, 70)
content: Button {
cursor: DEFAULT
text: "Click Me!"
}
},
Polygon {
points: [5, 5, 25, 5, 15, 25]
fill: gray
stroke: black
strokeWidth: 3
},
View {
transform: translate(100, 40)
content: TextField {
columns: 15
value: "This is a text field"
}
}]
}
}



ที่น่าสนใจก็คือ มันเป็น graphic ที่สามารถ transform ได้ด้วย
ทำให้เราทำ swing เอียงๆ แบบนี้ แล้วยังสามารถป้อนข้อมูลได้ด้วย

Related link from Roti

Tuesday, November 21, 2006

Saori

Saori ไม่ใช่ชื่อสาวญี่ปุ่น
แต่เป็นชื่อกรรมวิธีทอผ้าด้วยมือแบบหนึ่ง
ถ้าลองใช้ google ค้นหาคำนี้จากเฉพาะ site ในประเทศไทย
จะเห็นว่ามีอยู่ที่หนึ่งที่เกี่ยวข้องกับ ซึนามิ
นั่นคือโครงการ saori for tsunami thailand
ที่จัดตั้งโดย มูลนิธิมายา โคตมี



ใครเจอป้ายแบบนี้ ก็อุดหนุนเขาได้เลยครับ

จุดเด่นของเครื่องทอผ้าของ saori คือความง่ายในการใช้
เนื่องจากออกแบบมาโดยคำนึงถึงผู้พิการด้วย

ใน site www.saoriworcester.com เขาแปลหลักการ 4 ข้อ
"The way of saori" ไว้ดังนี้
1. Consider the differences between machines and people
ชอบตัวอย่างที่เขาพูดถึง
ปัจจุบันเรามีแนวโน้มจะมองทุกอย่างในแง่จักรกลมากขึ้น
เช่น ใช้ศัทพ์ว่า "human resources" แทนที่จะเรียกว่า "human beings"
2. Let's adventure beyond our imagination (Explore with all your might).
อย่างที่รู้กัน การทำงานสร้างสรรค์ ช่วยให้เราค้นพบ ธรรมชาติที่ซ่อนอยู่ในตัวตนของเรา
3. Let's look out through eyes that shine
อันนี้สิที่ผมชอบ ตรงกับที่ผมเคยเขียนในเรื่อง "อันเนื่องจากการเดิน"
ด้วย "eyes that shine" จะทำให้เราค้นพบ
สิ่งมหัศจรรย์เล็กๆน้อยๆรอบตัวเรา
ไม่ว่าจะเป็นลวดลายแตกร้าวของอิฐ, ดอกหญ้าเล็กๆข้างกำแพง
4. Let's learn from everyone in the group
friendships, friendships, friendships...
กิจกรรมที่ไม่เลือกชนชั้นอย่างนี้ เป็นสะพานเชื่อมความสัมพันธ์
ระหว่างคนต่างที่มาได้เป็นอย่างดี

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

ทอผ้ามันมีเสน่ห์ดี
สำหรับคนเมืองอย่างเรา การได้ใส่เสื้อที่ทำกับมือ
มันน่าจะให้ความรู้สึกที่ดีมากนะ



ภาพจาก Photogallery ของ saoriworcester.com

Related link from Roti

Friday, November 17, 2006

ถ้าเป็น yaws หล่ะ

เห็น Guruguru เขาลอง compare Rails กับ Web.py ดู
มีผลลัพท์จากคำสั่ง
ab -n 300 -c 100
เปรียบเทียบให้ดู
ผลลัพท์ ก็ไม่เป็นที่น่าแปลกใจสำหรับผมนะ
แต่ด้วยความมันส์ในอารมณ์ อยากรู้ว่า ถ้าเป็น yaws (web server ของ erlang) หล่ะ
ค่าที่ได้จะออกมาประมาณไหน

Disclaimer
เนื่องจากเครื่องก็คนละเครื่อง นำมาเปรียบอะไรกันไม่ได้หรอกนะ

source code
<erl>
out(A) ->
{ok, Name} = queryvar(A, "name"),
{html, f("hello ~s", [Name])}.
</erl>


ผลการทดสอบ

Server Software: Yaws/1.61
Server Hostname: 127.0.0.1
Server Port: 8000

Document Path: /hello.yaws?name=world,
Document Length: 13 bytes

Concurrency Level: 100
Time taken for tests: 0.319860 seconds
Complete requests: 300
Failed requests: 4
(Connect: 4, Length: 0, Exceptions: 0)
Write errors: 0
Total transferred: 52500 bytes
HTML transferred: 3900 bytes
Requests per second: 937.91 [#/sec] (mean)
Time per request: 106.620 [ms] (mean)
Time per request: 1.066 [ms] (mean, across all concurrent requests)
Transfer rate: 159.44 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 1.5 0 7
Processing: 1 8 3.3 8 22
Waiting: 1 7 3.2 7 21
Total: 2 8 4.1 8 22

Percentage of the requests served within a certain time (ms)
50% 8
66% 10
75% 10
80% 10
90% 14
95% 19
98% 20
99% 21
100% 22 (longest request)


ผลที่ได้ รู้สึกจะดีเกิดคาดไปหน่อย
หวังว่าคงไม่ได้ทำอะไรผิดนะ

Note: ทำไมตัวเลข total transfered ของเรา มันมากกว่าของเขา 5 เท่าหว่า
Document Length ก็เท่ากัน

Related link from Roti

IBOutlet

เมื่อวานไป train เรื่อง cocoa ที่ทาง Apple จัดให้ฟรีมา
มีประเด็นติดใจเรื่อง IBOutlet
ว่าจริงๆแล้วมันคืออะไร
ประเด็นหลักๆที่เป็นจุดเริ่มต้น ก็คือ
ใน XCode การ paint หน้าจอ gui เราจะทำผ่าน Interface Builder
ทุกอย่างที่ paint หรือ compose จะเก็บไว้ pack ในโครงสร้างที่เรียกว่า NIB file
ซึ่งมันตรงข้ามกับ approch ของ java ที่เวลาเรา paint gui
สิ่งต่างๆที่ paint หรือ setting จะถูก generate ออกมาในรูป java code
,ดังนั้นพวก widget ต่างๆของ swing
ที่เราเลือก paint ลงไป จะเห็นเป็นตัวแปรกันจะๆ ใน source code เลย
ถ้าอยาก customize widget ไหน
ก็สามารถเขียน code เข้าไปแก้ได้ทันที

ประเด็นคำถามที่เกิดขึ้นเมื่อวาน ก็คือ แล้วถ้าเราอยาก access object ต่างๆ
ที่อยู่ใน gui หล่ะ เราจะทำอย่างไร
เช่นอยากเข้าไป customize format ของ NSTextfield
มีคำตอบหนึ่งที่ได้มา ก็คือการใช้ IBOutlet
แต่ก็เป็นคำตอบที่เลาๆ คือไม่ exactly ว่า IBOutlet คืออะไร

Note: สำหรับคนที่ไม่รู้จัก IBOutlet
ลองดูตัวอย่างภาพการใช้งาน

@interface ConverterController : NSObject
{
IBOutlet NSTextField *amountField;
IBOutlet Converter *converter;
IBOutlet NSTextField *dollarField;
IBOutlet NSTextField *rateField;
}


ลองหานิยามของ IBOutlet ใน AppKit ดู
จะพบว่ามันอยู่ใน file
<AppKit/NSNibDeclarations.h>
และมีนิยามแบบนี้

#ifndef IBOutlet
#define IBOutlet
#endif

Bingo, มันคือ macro เปล่าๆ ที่ไม่มีผลในการ compile
ดังนั้นสำหรับผมแล้ว IBOutlet คือ marker อันหนึ่งใน source code
ที่ถูกใช้โดย interface builder
(IB ที่เห็นเป็น prefix ของ IBOutlet ย่อมาจาก Interface Builder)
ทำให้เราสามารถลากโยง(ผ่านทาง GUI)
ให้ instance (controller)
สามารถ access UI widget ที่เราต้องการได้
หรือถ้าพูดในมุมมองของ oop, IBOutlet
ช่วยให้เรากำหนด relationship ระหว่าง object
ผ่านทาง GUI tool (drag-drop).

สำหรับประเด็นในตอน runtime
นั้นขึ้นอยู่กับ strategy การเก็บ Nib file
ของ apple ว่าเป็นแบบ pure serialize, pure definition,
หรือ definition + serialize
(pure definition ถ้ายกตัวอย่างก็เช่น xul ของ mozzila)
ถ้าการเก็บมีรูปแบบของ definition เข้ามาปน
ก็จะมีประเด็นว่า หลังจาก initialize object ขึ้นมา จาก definition file แล้ว
ก็จะต้องมีการ set dependency ตาม relationshipt ที่ได้ define ไว้

Related link from Roti

ไม่แม่น

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

อย่างเช่น
ผมจำได้เลาๆว่า float มัน ไม่ exactly
เช่นค่า float 0.1 ใน computer มันไม่ใช่ 0.1 จริงๆ
แต่ก็ไม่รู้ว่า จริงๆแล้วมันเป็นเท่าไร
แต่ประเด็นก็คือ ความจำในส่วนนี้ผมผสมความรู้สึกมากไปหน่อย
ค่าที่อยู่ในใจ ก็เลยเพี้ยนค่อนข้างสูง เช่นเพี้ยนไปเป็น 0.103 อะไรแบบนี้

ปัญหาก็จะเกิดเวลาที่มีประเด็นปัญหาทาง software ให้ต้องวินิจฉัย
แล้วเรานำความรู้เลาๆไปร่วมวินิจฉัย
ซึ๋งบางครั้งความเพี้ยนนี้จะพาเราเข้าป่าไปได้เหมือนกัน

วันนี้ก็เลยนั่งหาว่า float 0.1 จริงๆแล้วมีค่าเท่าไร
คำตอบคือ ค่า float 0.1 คือ
1.10011001100110011001101 × 2^-4
หรือประมาณ 0.100000001

*อ้างอิง What Every Computer Scientist Should Know About Floating-Point Arithmetic

Note: Lew ก็เคยเขียนเรื่องนี้ใน in-depth ของ blognone
Decimal

Related link from Roti

Wednesday, November 15, 2006

ประสบการณ์

พึ่งโดดงานพาลูกไปเที่ยวทะเลมา 2 วัน
ได้อ่าน post เรื่อง ประสบการณ์ ที่ eYacht
ก็เลยซาบซึ้งเป็นพิเศษ

Related link from Roti

Tuesday, November 14, 2006

Ubuntu -> TIS-620

บริษัท ผมยังมีโปรแกรมเก่าๆ ที่ต้อง maintain ด้วย terminal อยู่
เดิมที่อยู่ใน windows ก็จะใช้พวก teraterm
ตอนนี้ย้ายมาใช้ ubuntu edgy ก็เจอปัญหาใหม่

ปัญหาแรก ก็คือจะ set encoding อย่างไร
ลองสั่ง locales -a แล้ว ก็มองไม่เห็น th_TH
ทดลองสั่ง
sudo dpkg-reconfigure locales
แทนที่จะขึ้นหน้าจอฟ้าๆมาให้เลือก
มันก็ดัน automatic generate en_*.utf8 ขึ้นมาเยอะแยะไปหมด

เอาใหม่ หันไปใช้ synaptic
เลือกลง language support th
อ้าได้ th_TH.utf8 มาแล้วโว้ย แต่ยังขาด TIS-620 อยู่

สุดท้ายก็ลองแก้ file
/var/lib/locales/supported.d/th
เพิ่มบรรทัด
th_TH.TIS-620 TIS-620
สั่ง sudo dpkg-reconfigure locales ซ้ำ

สำเร็จอย่างงงๆ
(เป็นการมั่วให้ได้ผลลัพท์ แต่ไม่เข้าใจว่าเกิดอะไรเบื้องหลังบ้าง
เข้าข่าย Programming by Coincidence)

Related link from Roti

The Pledge

วันนี้เปิด TV. ไปสะดุดตากับรายการ reality series ที่ชื่อ The Pledge
ที่ถ่ายทำคนธรรมดา 15 คน ที่ commit ว่าจะเข้าร่วมการแข่งขันไตรกีฬา
ที่สะดุดตา ก็เพราะ
  • รายการ Triathlon นี้จัดที่ภูเก็ต (Laguna Phuket Triathlon)
  • ผมก็เคย commit เข้าร่วมรายการนี้เหมือนกัน

เห็นแล้ว ก็เลยอยากนำมาเผยแพร่ต่อ

สาเหตุหลักๆที่ผมเคยเข้าแข่ง Triathlon ก็คือ
"ความอยากที่จะลองทำสิ่งที่ไม่คิดว่าจะทำได้"
อย่างว่ายน้ำ ผมมาหัดว่าย freestyle ตอนอายุ 25
เริ่มแรกนี่ สระ 10 เมตร ยังว่ายไม่ถึงเลย
หรือวิ่งก็เหมือนกัน เป็นคนไม่ชอบวิ่งมาแต่เด็ก
เวลาเล่นกีฬา ก็จะเลือกที่มันวิ่งน้อยสุด

ลองอ่านบันทึกผมในช่วงนั้นดูว่าบรรยากาศมันเป็นอย่างไร

เช้าวันที่ 2
ผมเข้าไปจัดของในบริเวณจุดเปลี่ยนการแข่งขั้น
ตอนประมาณ 6.30 น.
คนค่อยๆทยอยกันมา
บรรยากาศคึกคักมาก
ทั้งเจ้าหน้าที่และนักกีฬา
แต่ละคนอยู่ในชุดทะมัดทะแมงมากเลย
ผมได้ที่จอดจักรยานในสุด ช่วยให้
หลบความวุ่นวายใด้มากขึ้น
การเตรียมก็เป็นการเตรียมอุปกรณ์
ชุดต่างๆที่ต้องใช้ รวมทั้งอาหาร+น้ำดึ่ม
ช่วงนี้นักกีฬาต้องทยอยให้เจ้าหน้าที่เขียน
เบอร์ตามตัว มีเขียนที่ไหล่ซ้าย ใหล่ขวา
ต้นขาหน้าซ้าย ข้างขาขวา กับน่องขวา
ผมได้เบอร์ 311

ช่วง 7.15 ได้เวลาเดินทางไปจุดปล่อยตัว
ว่ายน้ำ วันนี้แสงสวยมากเลย ฟ้าครึ้มๆแต่
มีแสงแดดสาดลงมา ทำให้บรรยากาศดู
อิ่มตาดี ยิ่งมีนักกีฬา 400 คน ในชุดว่ายน้ำ
หลากสี + หมวกว่ายน้ำสีเหลือง + ทะเลเรียบ
บรรยากาศ สุดบรรยาย
ก่อนปล่อยตัว พวกนักกีฬาก็ลงไปว่ายวอร์ม
ในน้ำกัน ผมก็เลยถือโอกาศลงไปฉี่ด้วย
จากนั้นก็ขึ้นมากินน้ำไปอีกขวดเพื่อป้องกัน
ไม่ให้ปากเค็มมากนักเวลาว่ายน้ำ
8.00 น. เริ่มการปล่อยตัว ทุกคนวิ่งกรูกันลงน้ำ
ผมวิ่งช้าๆออกไป เพราะไม่อยากไปแย่งที่ว่าย
กับเขา ha ha แต่หลบไม่พ้นแฮะ การว่ายน้ำ
เป็นกีฬาที่ค่อนข้างโกลาหลเหมือนกัน ว่ายกัน
ไหล่เบียดใหล่ มือชนขาเลย ช่วงที่ว่ายอยู่
มีอยู่คนหนึ่งความเร็วเท่ากับผมเลยเจอกัน
ตลอดทาง เขาคอยตัดหน้าตัดหลังอยู่ตลอด
เวลา เดี๋ยวก็ว่ายไปทางซ้าย เดียวก็ตัดมาทาง
ขวา ไม่รู้ว่าผมว่ายเบี้ยวหรือเขาว่ายเบี้ยวกันแน่
ทุ่นแรกอยู่ห่างฝั่งไป 300 เมตร
ส่วนทุ่นที่สองวางขนานฝั่งไปอีก 400 เมตร
ทุ่น 3 ก็ว่างขนานฝั่งไปอีก 400 เมตร
กว่าจะถึงแต่ละทุ่น มองแล้วมองอีก
ทุ่นสุดท้ายห่างฝั่ง 330 เมตร คราวนี้ใจชื้น
แล้วมองเห็นฝั่งแล้ว เริ่ม speed ปนหอบเล็กๆ
ถึงผั่งได้รู้สึกดีใจมาก หันไปมองข้างหลังแล้ว
ต้องร้องไชโย เพราะพบว่ายังมีคนข้างหลังเราอีก
พอสมควร (ไม่เป็นที่โหล่แล้ว) จากนั้นก็วิ่งขึ้นฝั่งไป
ลง lagoon น้ำจึดเพื่อจะว่ายอีก 370 เมตร
อันนี้สิเริ่มเหนื่อยแล้ว เริ่มเสียอาการ (ว่ายเอียง
ไปเอียงมา ) อ้อข่วงที่ไกล้ๆผั่ง
เริ่มปวดฉี่อีกครั้ง แต่ฉี่ไม่ออก สงสัยกำลังเหนื่อย
มั้ง สมองคิดใหญ่ เฮ้ยถ้าไม่ฉี่เดี๋ยวตอนแข่ง
จักรยานจะฉี่ยังไงหล่ะ ก็เลยเบ่งใหญ่เลย
เฮ้อ เป็นการฉี่แบบระยะยาว ในทะเลนิดหนึ่ง
แล้วเก็บไปต่อในน้ำจึดอีกนิดหนึ่ง
พอถึงอีกฝั่ง ก็วิ่งช่วงสั้นๆเพื่อไปเอาจักรยาน
บรรยากาศบังคับให้ต้องวิ่ง ทั้งๆที่อยากจะเดิน
มากกว่า ไปถึงจักรยาน ใส่รองเท้า เสื้อกางเกง
พร้อมกับกินกล้วยไป 1 ลูก แล้วก็วิ่งพาจักรยาน
ออกมา แหมกองเชียร์คึกคักมาก
ทำให้วิ่งฉิวเลย (ปกติจะอู้มาก) ขึ้นจักรยานได้ก็
ไปโลด

ช่วงจักรยาน ระยะทาง 55 กิโลเมตร ช่วง 8
กิโลแรกเป็นภูเขาสูง
เจอเขาปุ๊บก็จอดเข็นปั๊บ : ) ไม่น่าเกลียดหรอก
เพราะมีเพื่อนเข็นเต็มเลย แต่ตอนลงเขานี่สิ
สนุกมาก แต่ต้องระวังแหกโค้ง
ช่วงที่ 2 เป็นเนินเล็ก เนินกลาง โค้งเล็กโค้งน้อย
เป็นทางถนัด ก็เลยค่อยๆใส่แซงไปเรื่อยๆ
ระหว่างทางจะผ่านชุมชนเป็นระยะระยะ
ก็จะมีเด็กคอย hello กับผู้ใหญ่นั่งดูเป็นกลุ่มๆ
หน้าที่เราก็คอยแจกยิ้มอย่างเดียว
พอช่วงกิโลที่ 40 แรงหมด (ซ้อมมาแค่นั้น)
หมดแรงนี่หมดจริงๆเลยนะ ป้อแป้ๆไปเรื่อยๆ
พวกที่เราแซงไปก็ค่อยๆทะยอยแซงกลับมาจนหมด
ผมก็กัดฟันขี่ต่อไปอีก 15 กิโล พวกกลุ่มหลังๆ
ก็เริ่มคุ้นหน้าคุ้นตากันตอนนี้แหล่ะ

พอกลับมาถึงจุดเปลี่ยนจักรยาน คราวนี้ใครจะวง
ใครจะวิ่ง กูไม่วิ่งแล้ว เดินจูงจักรยานไปเก็บ
นั่งพักเหนื่อยสักพัก (เอ้อระเหยเลยแหล่ะ)
คว้ากล้วยออกมากินอีกหนึ่งลูก
กิน chocolate อีก 4 อัน ค่อยๆเปลี่ยนเสื้อกับรองเท้า
ระหว่างนั้นก็จะมีคนเข้ามาเป็นระยะๆ แต่ละคน
เข้ามาปุ๊บก็วิ่งออกไปปั๋บ ไม่รู้จะรีบร้อนไปไหนกัน

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

Related link from Roti

Monday, November 13, 2006

Ruby 's Core Extension ใน Rails

เห็นคุณ Apirak post เรื่อง เจอของเจ๋งใน ruby
เลยขอโฆษณา feature ของ Rails หน่อย

ข้อดีอย่างหนึ่งของ Ruby ก็คือ Class มัน Open
ความหมายก็คือ เราสามารถ extends class ได้
โดยไม่ต้องไปยุ่งกับการ edit และ recompile ตัว source ของมัน

ถ้าเราไปดูใน Module ActiveSupport ของ rails
จะเห็นว่ามันมี directory อยู่อัน ที่ชื่อ core_ext
ตรงนี้คือที่ rails ใช้เก็บ extension ต่างๆ ที่ rails ต่อยอดจาก ruby core class



อย่างตัวอย่างที่คุณ Apirak ยกมา
x = x + 1.month 

ถ้าลองเปิด file core_ext/nummeric/times.rb ดู
จะเห็นว่าเขาเขียนไว้ประมาณนี้
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Numeric #:nodoc:

...
module Time

def seconds
self
end
alias :second :seconds

def minutes
self * 60
end
alias :minute :minutes

def hours
self * 60.minutes
end
alias :hour :hours

def days
self * 24.hours
end
alias :day :days

def weeks
self * 7.days
end
alias :week :weeks

...

จะเห็นว่าเขียนเป็น module อยู่
file ที่ include จริงๆ จะอยู่ใน core_ext/numeric.rb
class Numeric #:nodoc:
include ActiveSupport::CoreExtensions::Numeric::Time
include ActiveSupport::CoreExtensions::Numeric::Bytes
end


ลองดูตัวอย่างอื่นๆ ที่ rails เขา extends ไว้
1.even? # => false
2.odd? # => false

1.ordinalize # => "1st"
3.ordinalize # => "3.rd"

[1,2,3].to_sentence # => "1, 2, and 3"


ของที่บ้านเราใช้กัน ก็น่าจะพวก
11.to_thai # => สิบเอ็ด

Related link from Roti