google "summer of Code"
Note: project ของ apache มี memtor แถมให้ด้วย
อิจฉาเด็กๆยุค internet เสียจริงๆ
ถ้ารักดีเสียอย่าง มีอะไรให้ทำเยอะแยะเลย
จำได้สมัยเรา แค่หาหนังสือดีๆมาอ่าน ก็ยากแล้ว
Thursday, June 02, 2005
SimpleDateFormat is not Synchronized
วันนี้อ่านเจอประเด็นนี้ใน Tapestry mailing list
ใน javadoc ก็ย้ำไว้ว่า
คุ้นๆว่าเคยประกาศ SimpleDateFormat ให้เป็น public static เหมือนกัน
(หวังดี อยากลด resource)
ทางเลือก
เปลี่ยนไปใช้ thread-safe FastDateFormat ของ jakarta commons lang แทน
SimpleDateFormat is not thread safe. You will get weird behavior
under high concurrency. It cannot be static in the Global
ใน javadoc ก็ย้ำไว้ว่า
Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.
คุ้นๆว่าเคยประกาศ SimpleDateFormat ให้เป็น public static เหมือนกัน
(หวังดี อยากลด resource)
ทางเลือก
เปลี่ยนไปใช้ thread-safe FastDateFormat ของ jakarta commons lang แทน
Related link from Roti
Ruby Message (method)
เอามาจาก "10 Things Every Java Programmer Should Know About Ruby"
ในการเรียกใช้ method
in ruby
in java
ซึ่งดูแล้วน่าจะเหมือนกัน
แต่ลองดูตัวอย่างนี้
class VCR มี method หลักๆอยู่อันเดียวคือ
play_back_to ส่วน method_missing
จะใช้กรณีที่มีการเรียกใช้ method ที่ไม่ได้มีการ definition ไว้
อธิบายการทำงานได้ดังนี้
initalize เรียกใช้เมื่อมีการ new object
โดยจะทำการ initialize Array ที่ชื่อ message
(เครื่องหมาย @ หมายถึง instance variable)
จากนั้นถ้ามีการ call method ที่ไม่ได้ประกาศไว้
ให้ทำการเก็บ list ของ method, param, block ไว้ใน message Array
กรณีที่ call playback ก็ให้นำ method ทั้งหมดที่เก็บไว้ใน
message Array ออกมาเรียกใช้กับ object ที่ pass มา
ลองดูการนำไปใช้
ผลลัพท์ที่ได้
อธิบายเพิ่มเติมสำหรับคนที่ยังไม่คุ้น syntax ruby
โดยหลังจากที่เรา new VCR object แล้ว
ก็เรียกใช้ method sub!, upcase, array assignment([]=), concat(<<)
ซึ่งทั้งหมดนี้ไม่ได้มีการประกาศไว้ใน VCR Class เลย
ดังนั้น method ทั้งหมดก็จะถูกเก็บไว้ใน messages array
จากนั้นเมื่อมีการเรียกใช้ play_back_to
ก็จะนำเอา method เหล่านี้ออกมาเรียกใช้กับ
String ที่ pass เข้าไป
sub! -> inplace substitution
upcase! -> inplace upper case
[11,5]="Universe" -> เป็นการแทน char ที่ตำแหน่ง 11 โดยแทนที่ 5 ตัวอักษร
<<"!" -> concat ต่อท้าย
ดูแล้ว ก็รู้สึกว่า language มัน dynamic ดีจัง
เขียนแล้วคงจะสนุกดี (เพราะมี choice ให้ creative ได้หลายแบบ)
แต่น่าจะปวดหัวพอสมควร สำหรับการใล่ code
ที่เขียนด้วย ruby
ในการเรียกใช้ method
in ruby
obj.method
in java
obj.method()
ซึ่งดูแล้วน่าจะเหมือนกัน
แต่ลองดูตัวอย่างนี้
class VCR
def initialize
@messages = []
end
def method_missing(method, *args, &block)
@messages << [method, args, block]
end
def play_back_to(obj)
@messages.each do |method, args, block|
obj.send(method, *args, &block)
end
end
end
class VCR มี method หลักๆอยู่อันเดียวคือ
play_back_to ส่วน method_missing
จะใช้กรณีที่มีการเรียกใช้ method ที่ไม่ได้มีการ definition ไว้
อธิบายการทำงานได้ดังนี้
initalize เรียกใช้เมื่อมีการ new object
โดยจะทำการ initialize Array ที่ชื่อ message
(เครื่องหมาย @ หมายถึง instance variable)
จากนั้นถ้ามีการ call method ที่ไม่ได้ประกาศไว้
ให้ทำการเก็บ list ของ method, param, block ไว้ใน message Array
กรณีที่ call playback ก็ให้นำ method ทั้งหมดที่เก็บไว้ใน
message Array ออกมาเรียกใช้กับ object ที่ pass มา
ลองดูการนำไปใช้
require 'src/vcr'
vcr = VCR.new
vcr.sub!(/Java/) { "Ruby" }
vcr.upcase!
vcr[11,5] = "Universe"
vcr << "!"
string = "Hello Java World"
puts string
vcr.play_back_to(string)
puts string
ผลลัพท์ที่ได้
Hello Java World
HELLO RUBY Universe!
อธิบายเพิ่มเติมสำหรับคนที่ยังไม่คุ้น syntax ruby
โดยหลังจากที่เรา new VCR object แล้ว
ก็เรียกใช้ method sub!, upcase, array assignment([]=), concat(<<)
ซึ่งทั้งหมดนี้ไม่ได้มีการประกาศไว้ใน VCR Class เลย
ดังนั้น method ทั้งหมดก็จะถูกเก็บไว้ใน messages array
จากนั้นเมื่อมีการเรียกใช้ play_back_to
ก็จะนำเอา method เหล่านี้ออกมาเรียกใช้กับ
String ที่ pass เข้าไป
sub! -> inplace substitution
upcase! -> inplace upper case
[11,5]="Universe" -> เป็นการแทน char ที่ตำแหน่ง 11 โดยแทนที่ 5 ตัวอักษร
<<"!" -> concat ต่อท้าย
ดูแล้ว ก็รู้สึกว่า language มัน dynamic ดีจัง
เขียนแล้วคงจะสนุกดี (เพราะมี choice ให้ creative ได้หลายแบบ)
แต่น่าจะปวดหัวพอสมควร สำหรับการใล่ code
ที่เขียนด้วย ruby
Related link from Roti
Wednesday, June 01, 2005
Tapestry and OutOfMemoryError
อ่านพบใน mailing list ของ Tapestry
ว่ามีคนสั่ง start server ด้วย parameter
-Dorg.apache.tapestry.disable-caching=ture
แล้วเกิด OutOfMemory ขึ้น
ตัว parameter disable-caching โดยปกติจะมีค่าเป็น false
ซึ่งหมายความว่าทุกๆ page นั้นจะมี การ cache page
เก็บไว้ใน page pool
เมื่อมี request มาก็จะดึง page object จาก pool
ออกมาแจกงานให้ทำ
โดยวัตถุประสงค์ของ cache ก็คือ เพื่อลดเวลา
ที่ต้องเสียไปในการ initialize page object
ทุกครั้งที่ request วิ่งเข้ามา
ในกรณีที่ developer มีการแก้ไขค่าใน html template
หรือ page specification บ่อยๆ ก็สามารถ
disable-caching เพื่อที่จะได้ไม่ต้อง restart web application กันบ่อยๆ
ประเด็นว่าทำไมจึงเกิด OOME (out of memory error)
มีคนคาดเดาว่า น่าจะเป็นที่ Permanent heap
เกิดเต็มขึ้นมา (ตัว Permanent Heap เป็นที่เก็บ
classes, types) ส่วนสาเหตุที่เต็มก็อาจเป็นเพราะ
ทุกครั้งที่เกิดการ setup page จะเกิดการสร้าง
Enhance Class จาก Page Class เดิม (dynamic extends)
ซึ่งน่าจะเป็นสาเหตุให้ Permanent Heap เต็มได้
ทางแก้ไม่มี (ถือเป็นกรรมของ developer
ว่าให้คิดก่อนแก้ อย่าแก้เล็กแก้น้อยบ่อยๆ)
แต่มีการแนะนำให้ยืดเวลาก่อนเต็มออกไปได้โดย
ระบุ parameter ของ jvm ในส่วนของ
-XX:MaxPermSize (default 64MB)
ว่ามีคนสั่ง start server ด้วย parameter
-Dorg.apache.tapestry.disable-caching=ture
แล้วเกิด OutOfMemory ขึ้น
ตัว parameter disable-caching โดยปกติจะมีค่าเป็น false
ซึ่งหมายความว่าทุกๆ page นั้นจะมี การ cache page
เก็บไว้ใน page pool
เมื่อมี request มาก็จะดึง page object จาก pool
ออกมาแจกงานให้ทำ
โดยวัตถุประสงค์ของ cache ก็คือ เพื่อลดเวลา
ที่ต้องเสียไปในการ initialize page object
ทุกครั้งที่ request วิ่งเข้ามา
ในกรณีที่ developer มีการแก้ไขค่าใน html template
หรือ page specification บ่อยๆ ก็สามารถ
disable-caching เพื่อที่จะได้ไม่ต้อง restart web application กันบ่อยๆ
ประเด็นว่าทำไมจึงเกิด OOME (out of memory error)
มีคนคาดเดาว่า น่าจะเป็นที่ Permanent heap
เกิดเต็มขึ้นมา (ตัว Permanent Heap เป็นที่เก็บ
classes, types) ส่วนสาเหตุที่เต็มก็อาจเป็นเพราะ
ทุกครั้งที่เกิดการ setup page จะเกิดการสร้าง
Enhance Class จาก Page Class เดิม (dynamic extends)
ซึ่งน่าจะเป็นสาเหตุให้ Permanent Heap เต็มได้
ทางแก้ไม่มี (ถือเป็นกรรมของ developer
ว่าให้คิดก่อนแก้ อย่าแก้เล็กแก้น้อยบ่อยๆ)
แต่มีการแนะนำให้ยืดเวลาก่อนเต็มออกไปได้โดย
ระบุ parameter ของ jvm ในส่วนของ
-XX:MaxPermSize (default 64MB)
Related link from Roti
Monday, May 30, 2005
เรื่องเบาๆบ้าง
วันนี้เปิดผ่านช่อง NHK (ปกติไม่ได้เปิดหรอก)
เจอข่าวสั้นๆ เป็นภาษาอังกฤษ
ข่าวแรก เป็นการประกวดไก่ขัน
ไม่ค่อยได้ยินเสียงไก่เท่าไร
แต่เท่าที่ฟังดูเสียงไม่เหมือนไก่บ้านเรา
รู้สึกผู้ชนะ จะขันยาว 20 กว่าวินาที (ฟังไม่ค่อยถนัด)
ไก่บ้าอะไรขันได้นานขนาดนี้วะ
นี้ถ้ามาขันในหมู่บ้านชาวเขา โดนต้มยำแน่นอน
(ชาวบ้านเขาถือว่าไก่ที่ขันไม่ปกติ ถือว่าไม่เป็นมงคล)
ส่วนข่าวถัดไปเป็นการแข่งขัน
ทารกร้องให้
โดยจับตู่เด็กให้หันหน้าเข้าหากัน
จากนั้นก็เขย่าๆไปเรื่อยๆ ใครร้องให้ก่อนชนะ
ได้ยินว่า คนญี่ปุ่นเชื่อว่า
เด็กที่ร้องให้ง่าย แสดงว่าเป็นเด็กที่สุขภาพแข็งแรง โตไว
ข่าวที่ 2 เด็กน่ารักดี เห็นแล้วต้องอดหัวเราะไม่ได้
(ใครที่ยังไม่มีลูก จะไม่ค่อยเข้าใจนัก
พวกมีลูกอ่อนนี่ เห็นเด็กที่ไหน จะรู้สึกว่า
ทำไมมันน่ารักอย่างนี้วะ)
เจอข่าวสั้นๆ เป็นภาษาอังกฤษ
ข่าวแรก เป็นการประกวดไก่ขัน
ไม่ค่อยได้ยินเสียงไก่เท่าไร
แต่เท่าที่ฟังดูเสียงไม่เหมือนไก่บ้านเรา
รู้สึกผู้ชนะ จะขันยาว 20 กว่าวินาที (ฟังไม่ค่อยถนัด)
ไก่บ้าอะไรขันได้นานขนาดนี้วะ
นี้ถ้ามาขันในหมู่บ้านชาวเขา โดนต้มยำแน่นอน
(ชาวบ้านเขาถือว่าไก่ที่ขันไม่ปกติ ถือว่าไม่เป็นมงคล)
ส่วนข่าวถัดไปเป็นการแข่งขัน
ทารกร้องให้
โดยจับตู่เด็กให้หันหน้าเข้าหากัน
จากนั้นก็เขย่าๆไปเรื่อยๆ ใครร้องให้ก่อนชนะ
ได้ยินว่า คนญี่ปุ่นเชื่อว่า
เด็กที่ร้องให้ง่าย แสดงว่าเป็นเด็กที่สุขภาพแข็งแรง โตไว
ข่าวที่ 2 เด็กน่ารักดี เห็นแล้วต้องอดหัวเราะไม่ได้
(ใครที่ยังไม่มีลูก จะไม่ค่อยเข้าใจนัก
พวกมีลูกอ่อนนี่ เห็นเด็กที่ไหน จะรู้สึกว่า
ทำไมมันน่ารักอย่างนี้วะ)
Related link from Roti
AppFuse กับ Struts-menu
วันนี้นั่งดู code ที่ generate โดย AppFuse
มีการใช้ struts-menu ในการ render menu
(เดิม struts-menu มี dependency กับ struts
แต่ version 2.3 มีการ remove dependency
ออกไปแล้ว)
วิธีการใช้งาน
add listener ใน web.xml เพื่อใช้ load configuration ของ menu
สร้าง file menu-config.xml ใต้ WEB-INF
ภายในมีเนื้อหาอยู่ 2 ส่วน คือส่วน displayers ซึ่งใช้
config ว่าจะ render menu ด้วยวิธีไหน
กับส่วนเนื้อหาของ menu (ในส่วนเนื้อหานี้จะ
เป็นการ list menu ทั้งหมด โดยไม่เกี่ยวข้องกับลำดับ
ในการแสดงผล)
ในส่วนการแสดงผลใน jsp เราจะใช้ taglib มาช่วย
เมื่อต้องการ render ก็เพียงแต่จับคู่เลือก displayer
กับ Menu Item ที่ต้องการแสดง
จะเห็นว่า menu ที่แสดงให้ดูโดยวิธีนี้ ยังมีลักษณะ
เป็น static อยู่ กรณีที่ต้องการ dynamic menu
(content menu เก็บอยู่ใน database)
ให้ดูที่นี่ Dynamic Menu
มีการใช้ struts-menu ในการ render menu
(เดิม struts-menu มี dependency กับ struts
แต่ version 2.3 มีการ remove dependency
ออกไปแล้ว)
วิธีการใช้งาน
add listener ใน web.xml เพื่อใช้ load configuration ของ menu
<listener>
<listener-class>net.sf.navigator.menu.MenuContextListener</listener-class>
</listener>
สร้าง file menu-config.xml ใต้ WEB-INF
ภายในมีเนื้อหาอยู่ 2 ส่วน คือส่วน displayers ซึ่งใช้
config ว่าจะ render menu ด้วยวิธีไหน
กับส่วนเนื้อหาของ menu (ในส่วนเนื้อหานี้จะ
เป็นการ list menu ทั้งหมด โดยไม่เกี่ยวข้องกับลำดับ
ในการแสดงผล)
<MenuConfig>
<Displayers>
<Displayer name="DropDown"
type="com.fgm.web.menu.displayer.DropDownMenuDisplayer"/>
<Displayer name="Simple"
type="com.fgm.web.menu.displayer.SimpleMenuDisplayer"/>
</Displayers>
<Menus>
<Menu name="FileUpload" title="menu.selectFile"
description="File Upload" page="/selectFile.html"/>
<Menu name="indexMenu" title="Examples">
<Item name="indexMenu1" title="Basic Example"
toolTip="Shows usage of the menu displayers using defaults."
page="/menutest1.jsp"/>
<Item name="indexMenu2" title="Advanced Example"
toolTip="Shows customized menu displays."
page="/menutest2.jsp"/>
</Menu>
<Menu name="ToDoListMenuFile" title="FILE" description="test" width="50">
<Item name="TDLnew" title="NEW">
<Item name="TDLnewcase" title="CASE" image="images/case-new.png"
location="NewCase.jsp"/>
</Item>
</Menu>
</Menus>
ในส่วนการแสดงผลใน jsp เราจะใช้ taglib มาช่วย
<%@ taglib uri="http://struts-menu.sf.net/tag" prefix="menu" %>
เมื่อต้องการ render ก็เพียงแต่จับคู่เลือก displayer
กับ Menu Item ที่ต้องการแสดง
<menu:useMenuDisplayer name="ListMenu" permissions="rolesAdapter">
<menu:displayMenu name="Home"/>
<menu:displayMenu name="About"/>
</menu:useMenuDisplayer>
จะเห็นว่า menu ที่แสดงให้ดูโดยวิธีนี้ ยังมีลักษณะ
เป็น static อยู่ กรณีที่ต้องการ dynamic menu
(content menu เก็บอยู่ใน database)
ให้ดูที่นี่ Dynamic Menu
Related link from Roti
Sunday, May 29, 2005
เรียนรู้ yield ใน ruby
วันนี้ได้ลองเรียนรู้ ruby ในส่วนของ yield
ซึ่งเป็นส่วนที่สงสัยมานานแล้ว (ไม่รู้ว่ามันคืออะไรแน่)
ลองดูที่ code นี้
ผลลัพท์ทีได้
ส่วน {puts "hello"} เราเรียกว่า block
การที่เราเรียกใช้ method three_times
จะเกิดการส่ง block ไปด้วย แต่ยังไม่มีการ execute
จนกว่าจะมีการสั่ง yield จึงจะทำงาน
ที่นี้ลองดูโจทย์ประเภทหาค่า Fibonacci กันบ้าง
ได้ผลลัพท์
จะเห็นว่า block สามารถ declare parameter ได้
เพื่อให้ yield สามารถส่งค่า parameter ไปให้
scope ของ block ก็น่าสนใจทีเดียว
ลองดู testcase นี้
จะเห็นว่าภายใน block สามารถอ้างถึง variable
ที่อยู่ภายนอกได้ รวมทั้งสามารถ modify ค่าได้ด้วย
ใน ruby เราจะเห็นว่ามีการใช้ block และ yield เต็มไปหมด
เช่นในเรื่องของ Array เราสามารถใช้ Enumeration#find
ในการค้นหาค่าที่ต้องการได้
ซึ่งในการ implement feature find ของ ruby ทำได้ดังนี้
ลองดูตัวอย่าง method ของ array ที่ชื่อ inject บ้าง
inject จะ pass parameter ให้ block 2 ตัว
โดยเอาค่ามาจาก array ที่ index = 0 และ 1
จากนั้นผลลัพท์ที่ได้จาก yield ก็จะนำมาเป็น
parameter ตัวที่ 1 และนำค่าจาก array ตัวถัดไป
มาเป็น parameter ตัวที่ 2 ทำซ้ำไปเรื่อยๆจนหมด array
ผลลัพท์ที่ได้ในตัวอย่างก็คือ ค่า sum ของทุกค่าสมาชิกใน array นั่นเอง
ตัว block สามารถเก็บเป็นตัวแปรไว้ได้เช่นกัน
โดยใช้ method ที่ชื่อ lambda
จากนั้นก็เรียกใช้โดยผ่าน method call
ซึ่งเป็นส่วนที่สงสัยมานานแล้ว (ไม่รู้ว่ามันคืออะไรแน่)
ลองดูที่ code นี้
def three_times
yield
yield
yield
end
three_times {puts "hello"}
ผลลัพท์ทีได้
Hello
Hello
Hello
ส่วน {puts "hello"} เราเรียกว่า block
การที่เราเรียกใช้ method three_times
จะเกิดการส่ง block ไปด้วย แต่ยังไม่มีการ execute
จนกว่าจะมีการสั่ง yield จึงจะทำงาน
ที่นี้ลองดูโจทย์ประเภทหาค่า Fibonacci กันบ้าง
def fib_up_to(max)
i1, i2 = 1, 1
while i1 <= max
yield i1
i1, i2 = i2, i1 + i2
end
end
fib_up_to(1000) {|f| print f, " "}
ได้ผลลัพท์
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
จะเห็นว่า block สามารถ declare parameter ได้
เพื่อให้ yield สามารถส่งค่า parameter ไปให้
scope ของ block ก็น่าสนใจทีเดียว
ลองดู testcase นี้
def test_yield1
i = 0
def x
yield
yield
yield
end
x {i = i + 1}
assert_equal(3, i)
end
จะเห็นว่าภายใน block สามารถอ้างถึง variable
ที่อยู่ภายนอกได้ รวมทั้งสามารถ modify ค่าได้ด้วย
ใน ruby เราจะเห็นว่ามีการใช้ block และ yield เต็มไปหมด
เช่นในเรื่องของ Array เราสามารถใช้ Enumeration#find
ในการค้นหาค่าที่ต้องการได้
b = [1, 3, 5, 7, 9].find {|v| v*v > 30}
assert_equal(7, b)
ซึ่งในการ implement feature find ของ ruby ทำได้ดังนี้
class Array
def find
for i in 0...size
value = self[i]
return value if yield(value)
end
return nil
end
end
ลองดูตัวอย่าง method ของ array ที่ชื่อ inject บ้าง
b = [1, 3, 5, 7].inject {|sum, element| sum+element}
assert_equal(16, b)
inject จะ pass parameter ให้ block 2 ตัว
โดยเอาค่ามาจาก array ที่ index = 0 และ 1
จากนั้นผลลัพท์ที่ได้จาก yield ก็จะนำมาเป็น
parameter ตัวที่ 1 และนำค่าจาก array ตัวถัดไป
มาเป็น parameter ตัวที่ 2 ทำซ้ำไปเรื่อยๆจนหมด array
ผลลัพท์ที่ได้ในตัวอย่างก็คือ ค่า sum ของทุกค่าสมาชิกใน array นั่นเอง
ตัว block สามารถเก็บเป็นตัวแปรไว้ได้เช่นกัน
โดยใช้ method ที่ชื่อ lambda
จากนั้นก็เรียกใช้โดยผ่าน method call
def test_lambda
def n_times(init)
return lambda {|n| init * n}
end
p1 = n_times(2)
assert_equal(6, p1.call(3))
assert_equal(8, p1.call(4))
end
Related link from Roti
Learning by Testing
ตอนนี้ว่างๆ ก็พยายามจะหัดใช้ ruby
ปกติเวลาผมหัดเขียนโปรแกรม ผมก็จะใช้วิธีเขียนโปรแกรม แล้วก็
dump ตัวแปร ออกมาดูว่าค่าต่างๆมีการเปลี่ยนไปอย่างไร
เช่น สมมติจะทดลองใช้ ruby array
ผมอาจจะเขียน
พอเห็นผลลัพท์ทีได้ เราก็จะรู้ว่ามันตรงกับที่ใจเราคาดหวังไว้หรือไม่
วันนี้อ่านเจอ วิธีการเรียน ruby ของ Mike Clark
เขาใช้ unit testing เข้ามาช่วย
แทนที่จะเห็นผลลัพท์ด้วยตา
ก็เปลี่ยนมาใช้การ assert แทน
จากตัวอย่างข้างบน ก็เปลี่ยนมาเขียนแบบนี้แทน
ซึ่งผมเห็นด้วยกับวิธีเรียนรู้แบบนี้เลยนะ
ลองเรียนรู้ด้วยวิธีนี้แล้ว รู้สึก productive ดี
นอกจากนั้นสิ่งที่เขียนเก็บไว้
ก็สามารถนำมาใช้อ้างอิงหรือทบทวนได้ง่ายกว่า
เนื่องจากมีค่า "ผลลัพท์ที่พึงได้" อยู่ใน source code ด้วยเลย
ปกติเวลาผมหัดเขียนโปรแกรม ผมก็จะใช้วิธีเขียนโปรแกรม แล้วก็
dump ตัวแปร ออกมาดูว่าค่าต่างๆมีการเปลี่ยนไปอย่างไร
เช่น สมมติจะทดลองใช้ ruby array
ผมอาจจะเขียน
a = [1, 2, 3, 4, 5, 6]
a[1..2] = "pok"
puts a
พอเห็นผลลัพท์ทีได้ เราก็จะรู้ว่ามันตรงกับที่ใจเราคาดหวังไว้หรือไม่
วันนี้อ่านเจอ วิธีการเรียน ruby ของ Mike Clark
เขาใช้ unit testing เข้ามาช่วย
แทนที่จะเห็นผลลัพท์ด้วยตา
ก็เปลี่ยนมาใช้การ assert แทน
จากตัวอย่างข้างบน ก็เปลี่ยนมาเขียนแบบนี้แทน
require 'test/unit'
class MyTest < Test::Unit::TestCase
def test_array
a= [1, 2, 3, 4, 5, 6]
a[1..2] = "pok"
assert_equal(5, a.length)
assert_equal("pok", a[1])
assert_equal(4, a[2])
end
end
ซึ่งผมเห็นด้วยกับวิธีเรียนรู้แบบนี้เลยนะ
ลองเรียนรู้ด้วยวิธีนี้แล้ว รู้สึก productive ดี
นอกจากนั้นสิ่งที่เขียนเก็บไว้
ก็สามารถนำมาใช้อ้างอิงหรือทบทวนได้ง่ายกว่า
เนื่องจากมีค่า "ผลลัพท์ที่พึงได้" อยู่ใน source code ด้วยเลย
Related link from Roti
Subscribe to:
Posts (Atom)