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”
Thursday, January 11, 2007
ประโยคนี้น่าสนใจนะ
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
Subscribe to:
Posts (Atom)