ยิ่งเขียนโปรแกรม ผมก็ยิ่งซึ้งกับเรื่อง "กรรม"
ยิ่ง maintain โปรแกรมเก่าเรามากเท่าไร ก็ยิ่งซึ้งมากขึ้นเท่านั้น
quick fix นี่ตัวดีเลย,
ยกตัวอย่าง quick fix หนึ่งของผม
สมัยเมื่อนานมาแล้ว
มีโปรเจคอยู่โปรเจคหนึ่งซึ่งเป็นโปรเจค ลูกเมียน้อย
(หมายความว่า ไม่ได้ถูกให้ความสำคัญทั้งจากคนจ้างและคนรับจ้าง)
ซึ่งผมต้องทำ prototype ไปให้ user ใช้
มีอยู่หน้าจอหนึ่ง มันขาด field ไป field หนึ่ง
เผอิญใน table มันมี field หนึ่งที่พึ่งถูกตัดทิ้งไป
ก็เลยหยิบเอามาใช้ก่อนเลย ทั้งที่ naming,datatype มันคนละเรื่องคนละราวเลย
เผลอแป๊บเดียว โปรเจคลูกเมียน้อย
เกิดกลายเป็นทายาทมรดกร้อยล้านขึั้นมา
data วิ่งเข้าไปเป็นหลาย ล้าน record แล้ว
โปรแกรมก็มีหน้าจอเกินหลักร้อยไปเยอะแล้ว
การ refactor เปลี่ยนชื่อ column ใน table กลายเป็นเรื่องใหญ่
ช่วงนี้มีการ upgrade ตัวโปรแกรม
จากหน้าจอ terminal ขึ้นมาเป็น web application
ชื่อ column ที่ quick fix ไว้ก็เลยโผล่ขึ้นมาหลอกหลอน
จริงๆกรรมนี้ถือเป็นกรรมเล็กๆน้อยๆ
กรรมใหญ่กว่านั้นนี่ยังมีอีกหลายนัก
Note: เรื่อง กรรม นี้ คงใช้ได้กับบุคคลที่ไม่ได้เปลี่ยนงานบ่อย
ส่วนพวกเปลี่ยนงานหลังโปรเจคจบ ก็สบายตัวไป
(ไปได้กรรมอีกประเภทคือ ไม่เคยได้อยู่ดู feedback
ซึ่งถือว่าเป็นประสบการณ์ที่ช่วยพัฒนาตัวเองดีนักแล)
Monday, February 12, 2007
Friday, February 09, 2007
CAL, haskell(like) on JVM
CAL เป็นส่วนหนึ่งของ Open Quark Framework for Java
มี syntax และ feature ไกล้เคียงกับ haskell มาก
แต่ compile เป็น bytecode และ run บน java virtual machine
code ข้างล่างเป็นตัวอย่างการแปลง array เป็น java ArrayList
ส่วน Quark Framework ยังอ่านไม่เข้าใจ
ดูเหมือนจะเป็นชุดเครื่องมือที่มุ่งเรื่อง reuse business logic
โดย define code unit เล็กๆที่เรียกว่า Gem (เขียนโดย CAL)
จากนั้นก็สามารถนำ Gem พวกนี้มาต่อๆกันเป็นสายของ process ได้
มี syntax และ feature ไกล้เคียงกับ haskell มาก
แต่ compile เป็น bytecode และ run บน java virtual machine
code ข้างล่างเป็นตัวอย่างการแปลง array เป็น java ArrayList
outputListWith :: [a] -> (a -> JObject) -> JList;
public outputListWith !list f =
let
javaList :: JList;
javaList = jArrayList_new;
outputListWithHelper :: [a] -> (a -> JObject) -> JList ->
JList;
outputListWithHelper !list elementMappingFunction !javaList =
case list of
[] -> javaList;
x : xs ->
if (jList_add javaList (elementMappingFunction x)) then
outputListWithHelper xs elementMappingFunction
javaList
else
error "Adding an element to a Java list failed.";
;
in
outputListWithHelper list f javaList;
ส่วน Quark Framework ยังอ่านไม่เข้าใจ
ดูเหมือนจะเป็นชุดเครื่องมือที่มุ่งเรื่อง reuse business logic
โดย define code unit เล็กๆที่เรียกว่า Gem (เขียนโดย CAL)
จากนั้นก็สามารถนำ Gem พวกนี้มาต่อๆกันเป็นสายของ process ได้
Related link from Roti
Thursday, February 08, 2007
Declarative with groovy
กำลังทำงานอยู่ชิ้นหนึ่ง งานนี้เป็นงานรับข้อมูลงบการเงินเข้ามา
แล้วทำการสรุปพวก financial ratio ต่างๆออกมา
โดยสูตรของการคำนวณขึ้นอยู่กับ context ของผู้ที่ส่งงบเข้ามาด้วย
(เช่น สูตร ratio ของงบการเงินของธนาคาร ย่อมต่างจาก งบการเงินของพวกโรงงาน)
รายละเอียดปลีกย่อยมียุบยับ แต่ละไว้แล้วกัน
ประเด็นก็คือ เราอยากเขียนโปรแกรมให้กำหนดสูตรในลักษณะ declarative ได้
แทนที่จะเขียนเป็น method หรือ class ใน java แบบปกติ
เมื่ออยากได้ solution ที่เป็น declarative ก็เลยต้องมองหาพวก script language เข้ามาช่วย
ก็เลยหยิบเอา groovy มาลองทดสอบดู
ในเบื้องต้น เรามีสูตรแบบนี้อยู่
เขียนเป็น groovy ตรงๆก็คือ
โดย method element คือ helper ที่ช่วยดึงข้อมูลจากงบการเงิน
แต่พอลองทดสอบดู ก็พบว่า เราไม่สามารถ bind method element เข้าไปตรงๆได้
ต้องแปลงสูตรให้เป็น
การ evaluate code นี้ทำได้โดย
ใช้งานได้ แต่ดูแล้ว การที่ต้องใช้ "env" ก่อนนั้นดูไม่งามเลย
นอกจากนี้ยังมีประเด็นเรื่อง fix ชื่อสูตรไว้ใน code ด้วย
ประกอบกับที่พอนำ idea ไปคุยกับ user, user บอกว่า
อยากให้ script มันสามารถ include กันได้ด้วย
เพราะว่า sector แต่ละ sector มันมี common ratio อยู่
ก็เลยออกแบบใหม่
เอา Closure เข้ามาช่วย
สุดท้ายได้หน้าตาประมาณนี้ออกมา
ซึ่งการ evaluate script นี้จะยังไม่ได้ผลลัพท์ทันที
แต่จะได้ Closure จำนวนหนึ่งออกมา ซึ่งเราจะเก็บไว้ก่อน
แล้วค่อยนำ Closure พวกนี้ไป run ใน context ใดๆที่เราต้องการ
code ที่ใช้อ่าน formula เข้ามา หน้าตาเป็นแบบนี้
พระเอกของเรื่องนี้คือ closure.setDelegate(..)
แล้วทำการสรุปพวก financial ratio ต่างๆออกมา
โดยสูตรของการคำนวณขึ้นอยู่กับ context ของผู้ที่ส่งงบเข้ามาด้วย
(เช่น สูตร ratio ของงบการเงินของธนาคาร ย่อมต่างจาก งบการเงินของพวกโรงงาน)
รายละเอียดปลีกย่อยมียุบยับ แต่ละไว้แล้วกัน
ประเด็นก็คือ เราอยากเขียนโปรแกรมให้กำหนดสูตรในลักษณะ declarative ได้
แทนที่จะเขียนเป็น method หรือ class ใน java แบบปกติ
เมื่ออยากได้ solution ที่เป็น declarative ก็เลยต้องมองหาพวก script language เข้ามาช่วย
ก็เลยหยิบเอา groovy มาลองทดสอบดู
ในเบื้องต้น เรามีสูตรแบบนี้อยู่
current_ratio = สินทรัพย์หมุนเวียน / หนี้สินหมุนเวียน
เขียนเป็น groovy ตรงๆก็คือ
current_ratio = element('asset') / element('liabilities')โดย method element คือ helper ที่ช่วยดึงข้อมูลจากงบการเงิน
แต่พอลองทดสอบดู ก็พบว่า เราไม่สามารถ bind method element เข้าไปตรงๆได้
ต้องแปลงสูตรให้เป็น
current_ratio = env.element('asset') / env.element('liabilities')การ evaluate code นี้ทำได้โดย
Binding binding = new Binding();
binding.setVariable("env", helperObject);
GroovyShell shell = new GroovyShell(binding);
shell.evaluate(src);
float current_ratio = binding.getVariable("current_ratio");
ใช้งานได้ แต่ดูแล้ว การที่ต้องใช้ "env" ก่อนนั้นดูไม่งามเลย
นอกจากนี้ยังมีประเด็นเรื่อง fix ชื่อสูตรไว้ใน code ด้วย
ประกอบกับที่พอนำ idea ไปคุยกับ user, user บอกว่า
อยากให้ script มันสามารถ include กันได้ด้วย
เพราะว่า sector แต่ละ sector มันมี common ratio อยู่
ก็เลยออกแบบใหม่
เอา Closure เข้ามาช่วย
สุดท้ายได้หน้าตาประมาณนี้ออกมา
formulas.declare {
include 'common.script'
formula('current ratio') {
element('assets')/element('liabilities')
}
formula('quick ratio') {
element('cash') + .... / element('liabilities')
}
}
ซึ่งการ evaluate script นี้จะยังไม่ได้ผลลัพท์ทันที
แต่จะได้ Closure จำนวนหนึ่งออกมา ซึ่งเราจะเก็บไว้ก่อน
แล้วค่อยนำ Closure พวกนี้ไป run ใน context ใดๆที่เราต้องการ
code ที่ใช้อ่าน formula เข้ามา หน้าตาเป็นแบบนี้
พระเอกของเรื่องนี้คือ closure.setDelegate(..)
public FormulaSet load(String name) throws IOException {
final FormulaSet ret = new FormulaSet();
Binding binding = new Binding();
binding.setVariable("formulas", new Delegate() {
public void declare(Closure c) {
c.setDelegate(this);
c.call();
}
public void formula(String name, Closure c) {
Formula f = new FormulaImpl(name, c);
ret.put(name, f);
}
public void include(String name) throws IOException {
FormulaSet set = FormulaLoaderImpl.this.load(name);
ret.add(set);
}
});
GroovyShell shell = new GroovyShell(binding);
InputStream src = getClass().getClassLoader().
getResourceAsStream(scriptPath + name);
if (src == null) throw new IOException("script " + name + " not found!");
Script script = shell.parse(src);
script.run();
return ret;
}
Related link from Roti
Labels:
groovy
Monday, February 05, 2007
ขั้นตอนการ compress javascript ของ dojo
มีคนเขียนมาถามว่า Dojo ใช้วิธีอะไรในการ compress javascript file
ลองไปดู build file ของเขากัน
จะเขาใช้ rhino เข้ามาช่วยครับ
แต่ไม่ใช่ rhino ธรรมดานะครับ เขาใช้ custom ของเขาเอง
คือตัว rhino มันคือ javascript interpreter
ซึ่งภายในต้องมี class ที่ทำหน้าที่ parse javascript อยู่แล้ว
เขาก็เลย hack เจ้า class นี้
เพิ่มส่วน writer ที่ write ออกมาในรูปแบบที่ต้องการ
(หลักการเดียวกันกับพวก pretty printer แต่แทนที่จะออกมาสวย
ก็ออกมาไม่สวยแทน)
เราสามารถเอา rhino-custom.jar ของเขามาใช้ใน project เรา
โดยเรียกใช้แบบนี้
ลองดูตัวอย่าง code custom rhino ที่เขาเขียน
ตอนที่ print out เขาจะ check token ก่อน ว่าเป็นประเภทไหน
ถ้าเป็น NAME ก็จะทำการ compress
จะเห็นว่าเรียกใช้ method printCompressed
ตามไปดู printCompressed จะเห็นว่าเขาเช็คก่อน
ว่าตัวแปรที่กำลังจะ print นั้นเป็น ตัวแปรที่พึ่งประกาศใหม่
หรือตัวแปรที่ถูกอ้างใช้
ลองไปดู build file ของเขากัน
<target name="-rhino-compress"
unless="nostrip">
<copy overwrite="true" file="${srcFile}" tofile="${dstFile}.uncompressed.js" />
<java jar="./lib/custom_rhino.jar" fork="true" output="${dstFile}">
<arg value="-c" />
<arg value="${srcFile}" />
</java>
</target>
จะเขาใช้ rhino เข้ามาช่วยครับ
แต่ไม่ใช่ rhino ธรรมดานะครับ เขาใช้ custom ของเขาเอง
คือตัว rhino มันคือ javascript interpreter
ซึ่งภายในต้องมี class ที่ทำหน้าที่ parse javascript อยู่แล้ว
เขาก็เลย hack เจ้า class นี้
เพิ่มส่วน writer ที่ write ออกมาในรูปแบบที่ต้องการ
(หลักการเดียวกันกับพวก pretty printer แต่แทนที่จะออกมาสวย
ก็ออกมาไม่สวยแทน)
เราสามารถเอา rhino-custom.jar ของเขามาใช้ใน project เรา
โดยเรียกใช้แบบนี้
java -jar custom_rhino.jar -c infile.js > outfile.js 2>&1
ลองดูตัวอย่าง code custom rhino ที่เขาเขียน
ตอนที่ print out เขาจะ check token ก่อน ว่าเป็นประเภทไหน
ถ้าเป็น NAME ก็จะทำการ compress
case Token.NAME:
if(Token.OBJECTLIT == source.charAt(jumpPos)){
i = printSourceString(source, i + 1, false, result);
}else{
i = tm.printCompressed( source, i + 1, false, result, prevToken,
inArgsList, braceNesting);
}
continue;
case Token.STRING:
... # a lot of case
จะเห็นว่าเรียกใช้ method printCompressed
ตามไปดู printCompressed จะเห็นว่าเขาเช็คก่อน
ว่าตัวแปรที่กำลังจะ print นั้นเป็น ตัวแปรที่พึ่งประกาศใหม่
หรือตัวแปรที่ถูกอ้างใช้
public int printCompressed(String source,
int offset,
boolean asQuotedString,
StringBuffer sb,
int prevToken,
boolean inArgsList,
int currentLevel){
....
// ถ้ามี var นำหน้า หรือ อยู่ใน argement list
// ให้สร้าง mapping ขึ้นมาใหม่
if(((prevToken == Token.VAR)&&(!hasLocalTokenMapping(sourceStr)))||(inArgsList)){
newMapping = true;
}
str = this.getMappedToken(str, newMapping);
....
}
private String getMappedToken(String token, boolean newMapping){
String nt = null;
HashMap tokens = (HashMap)scopeReplacedTokens.get(scopeReplacedTokens.size()-1);
if(newMapping){
lastTokenCount++;
// ตั้งชื่อตัวแปร โดยมี _ นำหน้า
// แล้วตามด้วย เลข running ที่แปลงให้เป็น hex
nt = new String("_"+Integer.toHexString(lastTokenCount));
if(nt.length() >= token.length()){
nt = token;
}
tokens.put(token, nt);
return nt;
}
if(hasTokenMapping(token)){
return getTokenMapping(token);
}else{
return token;
}
}
Related link from Roti
Labels:
dojo,
javascript
Friday, February 02, 2007
ผ่านไปอีกหนึ่ง
เมื่อวันก่อน Application ที่เขียนโดยใช้ opensource ล้วนๆ
(เดิมก็ใช้ opensource แหล่ะครับ, แต่คราวนี้ไม่ต้องใช้ db2, websphere)
ได้ run production กับ load ที่มากสุดของช่วงปีนี้แล้ว (31 มกราคม)
วันนั้นมี transaction ใบเสร็จป้อนเข้ามา หมื่นกว่าใบ
ผ่านไปด้วยความสบายใจ
ลองดูว่า project นี้ใช้ opensource อะไรบ้าง
ยังมีตัวเล็กตัวน้อยอีกจำนวนหนึ่ง แต่ไม่สำคัญเท่าไร
(เดิมก็ใช้ opensource แหล่ะครับ, แต่คราวนี้ไม่ต้องใช้ db2, websphere)
ได้ run production กับ load ที่มากสุดของช่วงปีนี้แล้ว (31 มกราคม)
วันนั้นมี transaction ใบเสร็จป้อนเข้ามา หมื่นกว่าใบ
ผ่านไปด้วยความสบายใจ
ลองดูว่า project นี้ใช้ opensource อะไรบ้าง
- ในส่วนของ browser บังคับให้ใช้ Firefox
(เขียนให้ใช้ได้ทั้ง IE และ Firefox, แต่พอเกิดปัญหา ก็จะใล่ให้ไปลง firefox ก่อน
แล้วค่อยคุยกัน) - PDF Viewer ใช้ FoxIt
ตัวเล็กดี - Server ใช้ OpenSUSE
- Database ใช้ Ingres
- App Server ใช้ Tomcat 5.5
ต่อเป็น cluster 2 server - Web Server ใช้ Apache 2.x
- Single Sign On ใช้ CAS
- ORM ใช้ Hibernate 3.2
- Container ใช้ Spring Framework 2.0
- Presentation Layer ใช้ Tapestry 4.0.2 ผสมกับ Spring
- Report ใช้ JasperReport
- Javascript Framework ใช้ Dojo
- build tool ใช้ maven 2
- continuous build ใช้ CruiseControl
- Unit Testing ใช้ TestNG กับ EasyMock
- Integrated Test ใช้ Selenium + Ruby
ยังมีตัวเล็กตัวน้อยอีกจำนวนหนึ่ง แต่ไม่สำคัญเท่าไร
Related link from Roti
Thursday, February 01, 2007
paradox of the active user
paradox of the active user
เจอใน Signal vs. Noise
อ่านแล้วนึกถึงพฤติกรรมของตัวเอง
ยกตัวอย่างล่าสุด ก็คือการใช้ emacs ของตัวเอง
ที่พอใช้ถึงจุดหนึ่ง (คือทำงานได้แล้ว) ก็หยุดที่จะพัฒนาปรับปรุงการใช้ให้มีประสิทธิภาพขึ้น
อย่างเมื่อก่อนเวลาที่เปิดหลายหน้าต่างพร้อมๆกัน
เวลาอยากจะ scroll อีกหน้าต่างหนึี่ง ก็ต้อง switch ไปหน้าต่างนั้นก่อน
แล้วค่อยกดปุ่ม page down
ทั้งๆที่รู้ว่ามันมีคำสั่งที่ทำได้ แต่ก็ขี้เกียจเปิดคู่มือหา
มาเมื่อวาน ฮึดออกแรงค้นหาดู
ใช้เวลา 2 นาที
ก็เลยพบว่ามันทำได้ง่ายๆเพียงแค่กด Meta-pagedown แค่นั้นเอง
ชีวิตมันต่างกันเยอะเลยนะ เวลาที่รู้คำสั่งนี้แล้ว
ระหว่างกด 3 sequence ก็เหลือ sequence เดียว
เจอใน Signal vs. Noise
อ่านแล้วนึกถึงพฤติกรรมของตัวเอง
ยกตัวอย่างล่าสุด ก็คือการใช้ emacs ของตัวเอง
ที่พอใช้ถึงจุดหนึ่ง (คือทำงานได้แล้ว) ก็หยุดที่จะพัฒนาปรับปรุงการใช้ให้มีประสิทธิภาพขึ้น
อย่างเมื่อก่อนเวลาที่เปิดหลายหน้าต่างพร้อมๆกัน
เวลาอยากจะ scroll อีกหน้าต่างหนึี่ง ก็ต้อง switch ไปหน้าต่างนั้นก่อน
แล้วค่อยกดปุ่ม page down
ทั้งๆที่รู้ว่ามันมีคำสั่งที่ทำได้ แต่ก็ขี้เกียจเปิดคู่มือหา
มาเมื่อวาน ฮึดออกแรงค้นหาดู
ใช้เวลา 2 นาที
ก็เลยพบว่ามันทำได้ง่ายๆเพียงแค่กด Meta-pagedown แค่นั้นเอง
ชีวิตมันต่างกันเยอะเลยนะ เวลาที่รู้คำสั่งนี้แล้ว
ระหว่างกด 3 sequence ก็เหลือ sequence เดียว
Related link from Roti
Wednesday, January 31, 2007
เรียนรู้ Javaspaces โดยไม่ต้องเขียน Javaspaces
เห็น javaspaces มานานแล้ว แต่ก็ยังไม่เคยเข้าไปแตะสักที
เมื่อวันก่อนทดลองทำ distributed program ด้วย ruby
ก็เลยได้รู้จักเจ้า Rinda
ตอนแรกก็ยังไม่รู้หรอกว่า Rinda มีความเป็นมาอย่างไร
แค่เอามาใช้งานได้, ก็พอใจระดับหนึ่ง
จนเมื่อคุณ sugree post ถามใน codenone forums
ก็เลยเริ่มเกิดความสงสัย
ในที่สุดก็รู้ว่า Rinda เลียนแบบมาจาก Linda
ในเอกสารที่อธิบายถึง Linda ได้บอกไว้ด้วยว่า,
javaspace ก็เลียนแบบ pattern มาจาก Linda ด้วยเช่นกัน
เห็นเท่านี้ก็เลยเกิดอาการ satori นิดๆขึ้นมา
น่าสนใจนะ ประเด็นเรื่องการเรียนรู้ pattern ผ่านภาษาอื่น
แล้วนำกลับมา apply ทำความเข้าใจกับ implement ในภาษาหลักของตนเอง
เพราะถ้าให้ผมเริ่มทดลองกับ javaspace ก่อน
ผมคิดว่าผมก็คงไปได้ไม่ไกลถึงไหนหรอก
เพราะแค่ setup jini อย่างเดียว ก็คงเสียเวลาไปหลายวันแล้ว
กว่าจะผ่านพิธีรีตองแบบ java อีก (ทำให้พาลเบื่อเลิกไปเสียก่อน)
เทียบกับ ruby ที่ผมสามารถทดลองเขียน Rinda เพื่อทดลอง Matrix multiplication ง่ายๆได้ในวันเดียว
แถมยังได้ความรู้กลับมาทำความเข้าใจ javaspace ได้อีกด้วย
เมื่อวันก่อนทดลองทำ distributed program ด้วย ruby
ก็เลยได้รู้จักเจ้า Rinda
ตอนแรกก็ยังไม่รู้หรอกว่า Rinda มีความเป็นมาอย่างไร
แค่เอามาใช้งานได้, ก็พอใจระดับหนึ่ง
จนเมื่อคุณ sugree post ถามใน codenone forums
ก็เลยเริ่มเกิดความสงสัย
ในที่สุดก็รู้ว่า Rinda เลียนแบบมาจาก Linda
ในเอกสารที่อธิบายถึง Linda ได้บอกไว้ด้วยว่า,
javaspace ก็เลียนแบบ pattern มาจาก Linda ด้วยเช่นกัน
เห็นเท่านี้ก็เลยเกิดอาการ satori นิดๆขึ้นมา
น่าสนใจนะ ประเด็นเรื่องการเรียนรู้ pattern ผ่านภาษาอื่น
แล้วนำกลับมา apply ทำความเข้าใจกับ implement ในภาษาหลักของตนเอง
เพราะถ้าให้ผมเริ่มทดลองกับ javaspace ก่อน
ผมคิดว่าผมก็คงไปได้ไม่ไกลถึงไหนหรอก
เพราะแค่ setup jini อย่างเดียว ก็คงเสียเวลาไปหลายวันแล้ว
กว่าจะผ่านพิธีรีตองแบบ java อีก (ทำให้พาลเบื่อเลิกไปเสียก่อน)
เทียบกับ ruby ที่ผมสามารถทดลองเขียน Rinda เพื่อทดลอง Matrix multiplication ง่ายๆได้ในวันเดียว
แถมยังได้ความรู้กลับมาทำความเข้าใจ javaspace ได้อีกด้วย
Related link from Roti
Tuesday, January 30, 2007
การสอนคณิตศาสตร์
ไม่ใช่บ้านเราเท่านั้นที่มีปัญหา
อ่านเบื้องต้นได้ที่นี่ East meets West: Fundamental Differences in Math Teaching
Only 43% U.S. math teachers arrived at the correct answer to: 1 3/4 divided by 1/2 compared to 100% of the Chinese teachers.
อ่านเบื้องต้นได้ที่นี่ East meets West: Fundamental Differences in Math Teaching
Related link from Roti
Labels:
learning
Monday, January 29, 2007
AOP in emacs
ตอนที่เริ่มรู้จัก AOP ใน java ก็รู้สึกว่ามันเจ๋งดี
มาวันนี้ระหว่างนั่งเปิดคู่มือ emacs-lisp หา function ที่ต้องการอยู่
สายตาก็เหลือบไปเห็นคำว่า advice, before, after, around
อ้าว นี่มัน AOP นี่หว่า
กลับบ้านมาก็เลยทดลองเสียหน่อย
เอาโจทย์ที่เคยตอบคุณ sugree ใน codenone
ตอนนั้นประเด็นคำถามก็คือ behavior ของ M-f มันไม่เหมือนกับ 'w' ใน vi
วิธีเดิมที่ผมตอบคุณ sugree ไป ก็คือใช้การ define function ใหม่
แล้วก็ re-define map key ให้ชี้ไปที่ function ใหม่เอา
แต่ถ้าเปลี่ยนมาใช้ Advice ก็จะทำได้ดังนี้
การประกาศ defadivce นั้น, advice นั้นจะยังไม่ทำงานทันที
ต้องมีการสั่ง activate ก่อน
Note: อ่านดูใน file ของ emacs แล้วพบว่า feature นี้เขียนตั้งแต่ปี 1992
มาวันนี้ระหว่างนั่งเปิดคู่มือ emacs-lisp หา function ที่ต้องการอยู่
สายตาก็เหลือบไปเห็นคำว่า advice, before, after, around
อ้าว นี่มัน AOP นี่หว่า
กลับบ้านมาก็เลยทดลองเสียหน่อย
เอาโจทย์ที่เคยตอบคุณ sugree ใน codenone
ตอนนั้นประเด็นคำถามก็คือ behavior ของ M-f มันไม่เหมือนกับ 'w' ใน vi
วิธีเดิมที่ผมตอบคุณ sugree ไป ก็คือใช้การ define function ใหม่
แล้วก็ re-define map key ให้ชี้ไปที่ function ใหม่เอา
(defun my-forward-word()
(interactive)
(forward-word)
(skip-chars-forward " ")
(set-mark-command nil)
(forward-char))
แต่ถ้าเปลี่ยนมาใช้ Advice ก็จะทำได้ดังนี้
(defadvice forward-word (after forward-like-vim (arg))
(skip-chars-forward " ")
(set-mark-command nil)
(forward-char))
การประกาศ defadivce นั้น, advice นั้นจะยังไม่ทำงานทันที
ต้องมีการสั่ง activate ก่อน
(ad-activate 'forward-word)
Note: อ่านดูใน file ของ emacs แล้วพบว่า feature นี้เขียนตั้งแต่ปี 1992
Related link from Roti
Labels:
emacs
Wednesday, January 24, 2007
โปรแกรมเมอร์กับการเมือง
ไม่อยากใช้คำว่า "การเมือง" เลยเพราะมันกว้างไปหน่อย
การเมืองในที่นี่ หมายถึง สถานะ ความสัมพันธ์ระหว่างองค์กร ในแง่ อำนาจการต่อรอง
ช่วงนี้ผมได้รับคำขอร้องให้ช่วยแก้ไข application หนึ่ง
การแก้ไขนี้เกิดขึ้นเพราะ application ที่ต้องส่งมอบ ของบริษัทอีกบริษัทหนึ่งมันส่งมอบไม่ได้
(บริษัทที่ไม่ได้เป็นพวก developer ก็มักจะเป็นอย่างนี้แหล่ะ ไม่ค่อยจะอ่าน spec ให้ดีก่อน
,ส่วน spec ราชการก็สุดๆเหมือนกัน คลุมๆเหมาๆ)
แน่นอนในแง่การเมือง บริษัทฯผมมีอำนาจต่อรองในมือสูงมาก
แต่ทางบริษัทฯผมก็เลือกวิธีสบายๆ ก็คือไม่ได้ลูกเล่นอะไร อะไรพอช่วยได้ก็จะช่วยทำให้
แต่ถ้ามากไปก็ไม่ทำ (เพราะมันไม่ได้ตังค์)
การทำงานร่วมกัน ก็มีอยู่ 3 กลุ่ม
ผม, IBM, บริษัท A (บริษัทที่ส่งมอบไม่ได้)
ผมทำงานกับ IBM มาสักพัก ไม่เกิดปัญหาอะไร
งานในส่วนเขา แต่เขาทำไม่ได้
ผมพอจะทำอันไหนให้ได้ ก็ช่วยทำไป
มาวันนี้ บริษัท A หาโปรแกรมเมอร์ได้
ก็เลยต่อสายให้คุยประสานงานกับผม
คุยครั้งที่ 1 มาถึงคุณท่านก็สั่งเลย ผมจะทำอย่างนี้, ขอ file นี้, ขอ file นั้น
ผมพยายามอธิบาย แต่มันก็ไม่ค่อยจะยอมฟัง
สุดท้ายผมก็หาทางอธิบายได้ว่า solution ไม่ใช่อย่างที่คุณคิดหรอก มันง่ายกว่านั้น
จบการคุยครั้งที่ 1 ด้วยความตระหงิดๆของผม
คุยครั้งที่ 2 มาอีกหรอบเดิม ผมทำอย่างนี้แล้ว คุณทำอย่างนี้นะ
มาเป็นชุด ผมก็พยายามอธิบายเงื่อนไขให้ฟัง
มันก็ไม่ยอมฟัง สุดท้ายต้องตวาด "เงียบแล้วฟังผมพูด"
ได้ผลหน่อย ยอมฟังบ้าง
แต่ยอมได้ไม่นาน ก็เริ่มสั่งกลับมาอีกแล้ว
สุดท้ายผมก็ฟิวส์ขาด บอกไปว่า "ผมไม่คุยกับคุณแล้ว"
แล้วขอคุยกับ project manager แทน
ประเด็นจริงๆที่จะบอก ก็คือ ไม่ใช่ว่า programmer คนนั้นพูดไม่รู้เรื่อง
,ประเด็นเขามี, ความรู้เขามี
แต่สิ่งที่เขาขาดก็คือ การฟัง และท่าทีในการร่วมมือ
รวมทั้ง sense ในระดับภาพรวมด้วยว่า ขณะนี้ทิศทางการเมืองเป็นอย่างไร
หลังจากโมโหอยู่พักใหญ่ (ผ่านไป 4 ชั่วโมง ก็เริ่มส่งสัยว่าทำไมต้องโมโหด้วยวะ
สงสัยต่อม ego แตก) ก็นั่งดับโมโหด้วยการแก้โปรแกรม แล้วก็ส่งไปให้บริษัท A ซะ
เรื่องนี้สอนให้รู้ว่า "ผมเป็นโคนันทวิศาล"
การเมืองในที่นี่ หมายถึง สถานะ ความสัมพันธ์ระหว่างองค์กร ในแง่ อำนาจการต่อรอง
ช่วงนี้ผมได้รับคำขอร้องให้ช่วยแก้ไข application หนึ่ง
การแก้ไขนี้เกิดขึ้นเพราะ application ที่ต้องส่งมอบ ของบริษัทอีกบริษัทหนึ่งมันส่งมอบไม่ได้
(บริษัทที่ไม่ได้เป็นพวก developer ก็มักจะเป็นอย่างนี้แหล่ะ ไม่ค่อยจะอ่าน spec ให้ดีก่อน
,ส่วน spec ราชการก็สุดๆเหมือนกัน คลุมๆเหมาๆ)
แน่นอนในแง่การเมือง บริษัทฯผมมีอำนาจต่อรองในมือสูงมาก
แต่ทางบริษัทฯผมก็เลือกวิธีสบายๆ ก็คือไม่ได้ลูกเล่นอะไร อะไรพอช่วยได้ก็จะช่วยทำให้
แต่ถ้ามากไปก็ไม่ทำ (เพราะมันไม่ได้ตังค์)
การทำงานร่วมกัน ก็มีอยู่ 3 กลุ่ม
ผม, IBM, บริษัท A (บริษัทที่ส่งมอบไม่ได้)
ผมทำงานกับ IBM มาสักพัก ไม่เกิดปัญหาอะไร
งานในส่วนเขา แต่เขาทำไม่ได้
ผมพอจะทำอันไหนให้ได้ ก็ช่วยทำไป
มาวันนี้ บริษัท A หาโปรแกรมเมอร์ได้
ก็เลยต่อสายให้คุยประสานงานกับผม
คุยครั้งที่ 1 มาถึงคุณท่านก็สั่งเลย ผมจะทำอย่างนี้, ขอ file นี้, ขอ file นั้น
ผมพยายามอธิบาย แต่มันก็ไม่ค่อยจะยอมฟัง
สุดท้ายผมก็หาทางอธิบายได้ว่า solution ไม่ใช่อย่างที่คุณคิดหรอก มันง่ายกว่านั้น
จบการคุยครั้งที่ 1 ด้วยความตระหงิดๆของผม
คุยครั้งที่ 2 มาอีกหรอบเดิม ผมทำอย่างนี้แล้ว คุณทำอย่างนี้นะ
มาเป็นชุด ผมก็พยายามอธิบายเงื่อนไขให้ฟัง
มันก็ไม่ยอมฟัง สุดท้ายต้องตวาด "เงียบแล้วฟังผมพูด"
ได้ผลหน่อย ยอมฟังบ้าง
แต่ยอมได้ไม่นาน ก็เริ่มสั่งกลับมาอีกแล้ว
สุดท้ายผมก็ฟิวส์ขาด บอกไปว่า "ผมไม่คุยกับคุณแล้ว"
แล้วขอคุยกับ project manager แทน
ประเด็นจริงๆที่จะบอก ก็คือ ไม่ใช่ว่า programmer คนนั้นพูดไม่รู้เรื่อง
,ประเด็นเขามี, ความรู้เขามี
แต่สิ่งที่เขาขาดก็คือ การฟัง และท่าทีในการร่วมมือ
รวมทั้ง sense ในระดับภาพรวมด้วยว่า ขณะนี้ทิศทางการเมืองเป็นอย่างไร
หลังจากโมโหอยู่พักใหญ่ (ผ่านไป 4 ชั่วโมง ก็เริ่มส่งสัยว่าทำไมต้องโมโหด้วยวะ
สงสัยต่อม ego แตก) ก็นั่งดับโมโหด้วยการแก้โปรแกรม แล้วก็ส่งไปให้บริษัท A ซะ
เรื่องนี้สอนให้รู้ว่า "ผมเป็นโคนันทวิศาล"
Related link from Roti
Labels:
rant
Tuesday, January 23, 2007
Bug ข้าม Application
วันนี้ระบบที่ deploy production เกิดทำงานผิดผลาดขึ้นมา
ลูกค้าแจ้งว่า วันที่ที่แสดงในหลายๆหน้าจอ แสดงวันที่ผิดปี
ยุ่งหล่ะสิ ระบบนี้ไม่ใช่พึ่งใช้งานด้วย ใช้งานมาไม่ต่ำกว่า 2 เดือนแล้ว
โชคดีที่มี fallback, ระบบสำรอง ก็เลยปิดระบบใหม่บังคับให้ user ไปใช้ระบบเก่าไปก่อน
หลังจากนั่นค้นหา changeset ว่าเมื่อคืน มีใคร deploy อะไรใหม่ๆลงไปบ้าง,
ทดสอบระบบที่ test server, ทดสอบระบบที่เครื่องตัวเอง
กว่าจะหาสาเหตุเจอ ก็ผ่านไป 4 ชั่วโมง
สาเหตุนั้นน่าสนใจมาก ตรงประเด็นที่ว่า
เรามักจะมี assumption ว่า bug เกิดที่ระบบใด ส่วนที่ผิด ก็ควรจะอยู่ในระบบนั้น
แต่ bug ตัวนี้ดันเกิดจาก application อีกตัว ที่พึ่ง deploy ขึ้นไปเมื่อวาน
ภายในมัน set locale ให้เป็น "th"
โชคร้ายที่ application เก่า เรามี assumption ในการเขียนว่า
locale เป็น "en"
เมื่อ deploy บน virtual machine เดียวกัน ก็เลย conflict กัน
ลูกค้าแจ้งว่า วันที่ที่แสดงในหลายๆหน้าจอ แสดงวันที่ผิดปี
ยุ่งหล่ะสิ ระบบนี้ไม่ใช่พึ่งใช้งานด้วย ใช้งานมาไม่ต่ำกว่า 2 เดือนแล้ว
โชคดีที่มี fallback, ระบบสำรอง ก็เลยปิดระบบใหม่บังคับให้ user ไปใช้ระบบเก่าไปก่อน
หลังจากนั่นค้นหา changeset ว่าเมื่อคืน มีใคร deploy อะไรใหม่ๆลงไปบ้าง,
ทดสอบระบบที่ test server, ทดสอบระบบที่เครื่องตัวเอง
กว่าจะหาสาเหตุเจอ ก็ผ่านไป 4 ชั่วโมง
สาเหตุนั้นน่าสนใจมาก ตรงประเด็นที่ว่า
เรามักจะมี assumption ว่า bug เกิดที่ระบบใด ส่วนที่ผิด ก็ควรจะอยู่ในระบบนั้น
แต่ bug ตัวนี้ดันเกิดจาก application อีกตัว ที่พึ่ง deploy ขึ้นไปเมื่อวาน
ภายในมัน set locale ให้เป็น "th"
โชคร้ายที่ application เก่า เรามี assumption ในการเขียนว่า
locale เป็น "en"
เมื่อ deploy บน virtual machine เดียวกัน ก็เลย conflict กัน
Related link from Roti
Monday, January 22, 2007
malbolge
malbolge เป็น programming language ตัวหนึ่ง
สิ่งที่น่าสนใจก็คือ มันถูกออกแบบมาให้ใช้งานได้ยาก (designed to be difficult)
ภาษานี้ถูก design ในปี 1998
แต่โปรแกรม helloworld ตัวแรก ถูก solve ได้ในปี 2000
ถ้าอยากรู้ว่ามันยากอย่างไร ลองดูการเขียน helloworld ได้ที่นี่ malbolge: hello world
ตัวโปรแกรม ยกมาให้ดู สำหรับคนที่ขี้เกียจตาม link
(perl กรุณาชิดซ้ายด้วย)
สำหรับ implement ที่ยกมา คนเขียนเขาเขาต้องใช้ lisp ช่วยคำนวณก่อน
จากนั้นจึงค่อย generate code มันออกมา
แต่มีอยู่คนหนึ่ง (Antwon) เขา claim ว่า
แต่เขาก็ไม่ได้เปิดเผยเคล็ดลับนั้นออกมา
Links malbolge in wikipedia
สิ่งที่น่าสนใจก็คือ มันถูกออกแบบมาให้ใช้งานได้ยาก (designed to be difficult)
ภาษานี้ถูก design ในปี 1998
แต่โปรแกรม helloworld ตัวแรก ถูก solve ได้ในปี 2000
ถ้าอยากรู้ว่ามันยากอย่างไร ลองดูการเขียน helloworld ได้ที่นี่ malbolge: hello world
ตัวโปรแกรม ยกมาให้ดู สำหรับคนที่ขี้เกียจตาม link
(=<`$9]7<5YXz7wT.3,+O/o'K%$H"'~D|#z@b=`{^Lx8%$Xmrkpohm-kNi;gsedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543s+O<oLm(perl กรุณาชิดซ้ายด้วย)
สำหรับ implement ที่ยกมา คนเขียนเขาเขาต้องใช้ lisp ช่วยคำนวณก่อน
จากนั้นจึงค่อย generate code มันออกมา
แต่มีอยู่คนหนึ่ง (Antwon) เขา claim ว่า
I beat Malbolge with a stick and mastered its secrets.
แต่เขาก็ไม่ได้เปิดเผยเคล็ดลับนั้นออกมา
Links malbolge in wikipedia
Related link from Roti
Friday, January 19, 2007
code vs comment
ปกติเวลาเราเขียนโปรแกรม
source code คือเนื้อหาหลัก ส่วน comment คือเนื้อหารอง
,comment จะแทรกอยู่ใน source code โดยมี prefix พิเศษนำหน้า
haskell มันมี file format อยู่อันหนึ่งที่เรียกว่า "lhs"
ถ้าอยู่ใน format นี้ source code จะหลายเป็นเนื้อหารองแทน
(คือต้องมี prefix นำหน้า)
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 เราก็เลยใส่
ถ้าเรานำ code นี้ไป run ผ่าน xmp process
เราก็จะได้ output แบบนี้
แต่ที่ชอบมากที่สุด ก็คือมัน integrate เข้ากับทั้ง vi และ emacs
อย่างใน emacs เราสามารถสั่ง evaluate โดยใช้
ทดสอบ 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 ดังนี้
ดูแผนที่ที่เขาไป มีไทยด้วยนะครับ
Note: mnesia เป็นชื่อ database ใน 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
Labels:
erlang
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
จำนวน 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 นี่มันได้อารมณ์เหมือน ไฮกุ
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"
ถ้าไม่เริ่มทำ ก็ไม่มีจุดเริ่มต้น
ส่วนอาวุธที่จะจัดการ นิสัยที่ไม่ดี ก็คือ สติ
(คำถามที่น่าสนใจ องค์กร จะมี สติ ได้ไหม
สติ ของ องค์กร เป็นอย่างไร)
ปิด ด้วยคำคม
เรื่อง Habit มีประเด็นน่าสนใจอีกเยอะ
ควรอ่านเพิ่มเติมอย่างยิ่ง
The Laws of Habit
Tacit knowledge and habit
ซึ่งถ้าให้เหลือคำเดียว ก็คือคำว่า 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
Labels:
learning,
phychology
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
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
Labels:
fun
Wednesday, January 03, 2007
Ninety-nine Lisp Problem #P07
วันนี้นั่งทำโจทย์ข้อที่ 7, ในแง่ของ clisp กับ erlang
ข้อนี้ไม่ยากนัก แต่ในแง่ของ haskell แล้ว program ออกมาไม่สวยนัก
ก็เลย mail ไปถามใน mailing list ของ haskell ดู
ซึ่งก็ได้ผล มีคนเก่งๆมาช่วยไขความกระจ่าง
ถือเป็น mailing list ที่น่าประทับใจอันหนึ่ง
ลองดูโจทย์
CLisp
ใช้ append ในการ ต่อ list 2 อันเข้าด้วยกัน
Erlang
erlang ก็ใช้วิธีตามแบบ lisp มาติดๆ
แต่ด้วยการใช้ guard + pattern matching เข้ามาช่วย
ทำให้ดูง่ายกว่า clisp
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 แบบนี้
จากนั้นก็เขียน function
จะเห็นว่ามันยืดยาวไม่สวยงาม
ผมก็เลยเขียนไปถามใน mailing list
ลองอ่านที่เขาตอบมาดู
flatten a nested list
ผมชอบ solution ที่ Conor McBride ตอบมากสุด
ทำให้รู้ด้วยว่า list มันเป็น monadic structure ด้วย
magic อยู่ที่ function
ซึ่งถ้าไปดูนิยามมันใน library ดู จะเห็นนิยามมันเป็นแบบนี้
ข้อนี้ไม่ยากนัก แต่ในแง่ของ 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 กล่อง ไปวาง)
แล้วก็ใช้เป็นสถานีดูเมฆ
ตอนนั้นจำได้ว่าได้แรงบันดาลใจจากบทความว่าด้วยประเภทของเมฆ ในวารสาร "ทักษะ"
หนังสือซึ่งใครที่แก่สักหน่อยคงจะรู้จัก
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
ในส่วนของการได้มาซึ่ง rules มันดูแปลกๆ
ก็เลยไปนั่งไล่ code ดูว่ามันมาจากไหนกันแน่
เริ่มแรกสุด method
ใน class
ดังนั้นถ้าจะเขียนให้สื่อหน่อย อาจจะเขียนอย่างนี้แทน
method
แต่ delegate งานไปให้
ซึ่ง
ว่าจะใช้ rule file ตัวไหนดี
จากการ debug, ตัว rule file จะใช้ file
ภายในเป็น binary file ก็เลยไม่รู้ว่ามีเนื้อหาอะไรบ้าง
เมื่อได้ rule มาแล้ว
Note: สังเกตุ comment ใน Todo ด้วย
ดังนั้นตัวอย่างของ bact'
ถ้าเขียนใหม่ให้ชัดเจนไปเลยว่า rule มาจากไหน
อาจจะเขียนดังนี้
เห็น 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 นี้ดู
พลเอกบัญชร เขาเขียนให้ความเห็นว่า
น่าสนใจมาก
plot เรื่องเน่าสนิท เหมือนละครก่อน/หลังข่าวเลย
ของ พลเอกบัญชร ชวาลศิลป์ ในมติชนสุดสัปดาห์
ที่เขียนเล่ากรณี การจราจลที่เกิดจากการประท้วงเรื่องโรงงานแทนทาลัมที่ภูเก็ต
แล้วเหนื่อยใจ
ก็พอรู้นะว่า กระทรวงนี้ คาดหวังอะไรไม่ได้
แต่ไม่นึกว่า มันจะแย่ขนาดนี้
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
กำหนด series ซึ่งเป็นชุดของข้อมูลที่จะ plot
(graph หนึ่งๆสามารถมีได้หลาย series)
กำหนด Axis (แกน x, แกน y)
สร้าง Plot object ซึ่งเป็นการจับคู่แกน axis
และกำหนด series ให้กับ Plot object
(1 Plot สามารถมีได้หลาย Series)
กำหนด PlotArea
สุดท้ายก็คือ Chart object
โดย 1 chart สามารถมีได้หลาย PlotArea
เวลาจะแสดง graph ก็แค่ bind object เข้ากับ dom element ที่ต้องการ
ตอนนี้มีทางเลือกง่ายๆอีกทางแล้ว
ใน 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
Labels:
chart,
dojo,
javascript
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 แบบเดียวกับ haskellNote: ธรรมชาติคนเรามักจะเคยชินกับนับเลขไปข้างหน้า
คิดครั้งแรกผมก็ใช้วิธี +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
ใช้ดอกไม้ไฟช่วยระบาย
แต่งาน Head On ของ Cai Guo-Qiang นี่สะดุดตาดีนะ

Note: ชอบงานนี้อีกชิ้นหนึ่ง
Same Word, Same Seed, Same Root
ใช้ดอกไม้ไฟช่วยระบาย
Related link from Roti
Labels:
art
Thursday, December 21, 2006
Hibernate Lucene Search
ผมมี Idea เรื่องที่จะใช้ Lucene ทำ search index
ของ Domain model มานานแล้ว
แต่ก็ไม่ได้โอกาสทำเสียที
มาตอนนี้ Hibernate 3.2 เขาทำ feature นี้ออกมาให้ใช้เรียบร้อยแล้ว
แถม integrate ได้เนียนดีด้วย
ลองดูตัวอย่างการใช้งาน
ขั้นแรกสุด ก็คือ "การระบุว่า domain ไหนที่ต้องการทำ index"
ตรงนี้เขาใช้ annotation
lucene มองสิ่งที่ถูก index ว่าเป็น document
ดังนั้นก็เลยต้องมีการระบุ tag
ไว้ที่ primary key ด้วย
field ไหนที่ต้องการทำ index ก็ต้องระบุด้วย tag
เพื่อให้ lucene ทำ index โดยอัตโนมัติ หลังจากที่เรา hibernate insert,update หรือ delete
เราก็ต้อง config add listenter เข้าไป
การ config ระบุว่าจะเก็บ index file ที่ไหน ทำผ่าน
ส่วนการสืบค้นข้อมูล จะเห็นว่าเขาพยายามดึงให้เข้าไปใช้
ลองดูตัวอย่าง
ของ 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_providerhibernate.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 เขาพูดถึงประเด็นเรื่องการฝึกเขียนโปรแกรม หรือฝึกคิดแก้โจทย์
โดยคุณสมบัติที่เขากำหนดไว้ ก็คือ
เห็นด้วยกับเขานะ เพราะส่วนใหญ่
พวกเรามักจะฝึกเขียนโปรแกรม ด้วยการทำงานจริง
การทำงานจริง มันจะมีขอบเขต,กับปัจจัยบางอย่างกำหนดอยู่ (เช่นเวลา)
ทำให้เราไม่สามารถทดลอง หรือเล่นได้อย่างอิสระ
อย่างกรณีของผม
ยิ่งผมเรียนรู้ ภาษา, framework หลายๆแบบยิ่งขึ้น
สัดส่วนของความชำนาญก็จะลดลง
ตอนนี้ก็เลยต้องให้ความสำคัญกับ
"การสร้างความชำนาญ" มากขึ้น
เบื้องต้นก็คือ พยายามหาโจทย์ มาฝึกทำ
อย่างโจทย์ของที่นี่ ก็น่าสนใจดี
L-99: Ninety-Nine Lisp Problems
วันนี้ก็เลยเลือกข้อง่ายสุด มาทำ 2 ข้อ
เริ่มด้วย clisp
haskell
erlang
โจทย์แรกนี่ technic การใช้ pattern matching ชนะแฮะ
haskell ดู clean สุด
ส่วน erlang ถึงแม้จะใช้วิธีเดียวกับ haskell
แต่ syntax ไม่ clean เท่า
โจทย์ข้อต่อไป, โจทย์นี้ไม่ต่างจากโจทย์แรกเท่าไร
clisp
haskell
erlang
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
ฝั่งของ Seaside จะมีแค่ file เดียว
เพราะ Seaside ไม่ได้ใช้ template
(อันนี้เป็นประเด็นทางศาสนา ฝ่ายสนับสนุน
เชื่อว่า pure smalltalk มันงามแท้ๆ)
ลองดูความสวยงาม(ที่เขาว่ากัน) ของการ render html ใน method renderContentOn:
ถ้าตัดประเด็นส่วน syntax ที่ไม่เหมือนกัน
กับ Technique การ implement ที่แตกต่างกันออกไป
ก็จะเห็นว่าทั้งสองฝ่ายมีแนวคิดไปในทำนองเดียวกัน นั่นคือ
ลองดูการนำ component มาใช้งานบ้าง
ในตัวอย่างนี้ ก็คือการนำ Counter Component มาวางเรียงกันหลายๆอัน

(ซึ่งเวลาทำงานแล้ว แต่ละ component ก็จะ maintain state ของตัวเอง)
เริ่มด้วย Tapestry
เนื่องจากหน้าจอเราไม่ซับซ้อน ดังนั้นทางฝั่ง tapestry จึงใช้แค่ file เดียว
นั่นคือ html template file
จะเห็นว่าเราใช้
ลองกลับไปดูที่ฝั่ง smalltalk บ้าง
ฝั่ง smalltalk จะยุ่งยากกว่าหน่อย
ตรงที่เราต้อง maintain ว่า component MultiCounter ของเรา
มี sub component อะไรบ้าง
โดยการ maintain, seaside กำหนดให้เราต้อง
implement method
ที่ต้อง return sub component ทั้งหมดที่อยู่ภายใต้ component เรา
สิ่งที่แตกต่างกันมากที่สุด สำหรับ seaside กับ tapestry
ก็คือเรื่อง flow ระหว่าง page
ในฝั่ง tapestry flow ระหว่าง page
ลองดู code ฝั่ง tapestry เวลาที่เรา flow เปลี่ยน page
จะเห็นว่า tapestry กำหนดไว้ว่า ถ้า method ที่ถูกเรียกใช้
return page ไหนออกมา ก็ให้ render page นั้นแทนที่ page ปัจจุบัน
ดูฝั่ง smalltalk บ้าง ลองดูตัวอย่าง การ flow ของหน้าจอขายของ
WAStoreFillCart, WAStoreCartConfirmation, WAStoreAddressEditor
คือหน้าจอย่อยๆ แต่ละหน้าจอ
จะเห็นว่าเราสามารถผูกหน้าจอต่างๆเข้าเป็น flow ได้
เวลาจะกระโดดไปหน้าจอไหน ก็ใช้ method call
แถมยังรับ return ค่ากลับจากหน้าจอนั้นได้อีก
ซึ่งถือว่าเป็น feature ที่ดูเด่นกว่า tapetry อย่างเห็นได้ชัด
ที่ผมไปทำ 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 เยอะแน่ๆ
แต่ไม่รู้ว่าเยอะขนาดนี้
architecture เป็นไงบ้างหรือ
แค่เห็นก็เหนื่อยแล้ว นี่ยังไม่พูดเรื่อง 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 แบบไหน
แต่ไม่รู้ว่าเยอะขนาดนี้
- 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
Breaking Barriers
Breaking Barriers
"The Potential of Free and Open Source Software
for Sustainable Human Development"
เป็นเอกสาร case studies project ที่ใช้ FOSS
"The Potential of Free and Open Source Software
for Sustainable Human Development"
เป็นเอกสาร case studies project ที่ใช้ FOSS
Related link from Roti
Labels:
foss
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 ไว้แบบนี้ (ผมยังไม่ได้ลองดูนะว่าทำได้จริงหรือไม่)
(อ้างอิง จาก [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 เรื่องคือ
ดูแล้วการ custom ให้ใช้ภาษาไทย ก็ไม่น่าจะยากเกินไปนัก
(คุณ Vee เสร็จ thesis เมื่อไรก็บอกด้วยนะครับ
ผมจะชวนทำเรื่องนี้ด้วยกัน)
Note: ใน OLPC ก็มี squeak ฉบับ Etoys ลงไว้ด้วยเหมือนกัน
ไม่รู้ทางทืม nectec ได้เตรียมเรื่องภาษาไทยไว้บนนั้นหรือยัง
คุณ 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 อันคือ
ใช้ extract parameter ที่ส่งเข้ามา
โดยเก็บค่าที่ได้ไว้ใน instance variable ที่ชื่อ 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)
ได้ผลลัพท์ดังนี้
ข้อสังเกต
ประเด็นแรกที่ผมต้องการรู้ก็คือ
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 ดูเสียก่อน
จะเห็นว่าเขาไม่ได้ปน annotation เข้าไปกับ bean code ตรงๆ
แต่เป็นการแยก configuration ออกมาต่างหาก
ทำให้ออกไปในแนวของ DSL
ข้อดีที่แน่นอนอย่างหนึ่ง ก็คือ spelling
เป็น java class อย่างนี้แล้ว
ไม่มีกรณีสะกดผิดแน่นอน
โดยใช้ 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 มาอื้อเลย
ซึ่งเป็น 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 เขาบอกว่า
ดังนั้นประโยค f x y
เวลา evaluate จะทำตามลำดับแบบนี้
((f x) y)
เครื่องหมาย + ก็เป็น function ด้วย
3 + 2 เวลา run ใน haskell
ก็ควรจะเริ่ม evaluate (3 +) ผลลัพท์ที่ได้คือ function
จากนั้นค่อย call function นั้น ด้วย parameter 2 อีกที
ลองดูสิว่าประโยค 3 + 2
เขียนแบบไหนได้อีกบ้าง
Note: ขออภัยแฟนๆ python ที่เอา "many way to do one thing" มาให้ดู
ส่วน statement แบบนี้
เห็นแล้วอย่าสับสน
ใน haskell, (3,2) คือ datatype ชนิด tuple
ไม่ได้เป็น parameter syntax แบบที่เรารู้จัก
ใน 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 ถามมา
ถ้าใช้ method serialize ใน Form object
มันจะทำให้เราเองครับ
ลองไปทดสอบดูได้ที่ web นี้ครับ
Prototype: Form Serialization and Deserialization
มีหน้าจอให้เราลอง submit ดูด้วย
method ข้างบน มันจะให้ค่าทั้งหมดใน form กลับมา
แต่ถ้าเกิดเราอยากได้แค่ array ของเฉพาะตัวแปรนั้นๆชุดเดียว
ก็ลองดูที่ blog นี้ครับ
Quick Tip: Getting array of checked values using prototype
คือเวลาผมใช้ 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 ทำงานแยกกัน
คนที่เคยใช้ ant กับ project ที่มีลักษณะแบบนี้
จะรู้ว่าการ maintain ant file เป็นเรื่องน่ารำคาญใจเรื่องหนึ่งทีเดียว
การเขียน ant script จะใช้วิธี copy & paste ไปมา
ถ้าเปิดดู build file แต่ละตัว ก็จะเห็นว่ามีส่วนที่ซ้ำกันไปซ้ำกันมาเต็มไปหมด
jar file ของ subproject ก็ซ้ำๆกัน
แถมยังถูก check-in เข้า version control
นอกจากนี้ยังต้องทำ ant script ตัวใหญ่
ไว้คอยคุมลำดับการ build ของโปรเจค, ฯลฯ
maven ก็เลยเกิดมา เพื่อเติมรอยโหว่ในจุดนี้
ลองดูวัตถุประสงค์ของ maven
feature ที่ผมชอบใน maven ก็มี
ส่วนที่ไม่ค่อยชอบ
ที่นี้ก็มาถึงคำถามสำหรับการเลือกใช้
สำหรับผมนะ
project เขียนคนเดียว ไม่ยั่งยืน ผมใช้ build ของ IDE
project เล็กๆที่เผยแพร่ให้คนอื่น ใช้ ant
project กลางๆ ถ้ารีบก็ใช้ ant, ถ้าไม่รีบก็ maven
(เนื่องจากการเรียนรู้ maven ใช้เวลามากกว่า ant)
project ใหญ่เป็นเรื่องเป็นราว ใช้ maven
เกิดต้องหันมาจับ 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
Subscribe to:
Posts (Atom)