Friday, February 10, 2006

เดินทาง



ไปสัก 10 วันก็พอ คิดถึงลูก

Related link from Roti

Thursday, February 09, 2006

Reversed Jingle Bellls

เคยฟังเพลง Jingle bell กลับด้านไหม
Play

Related link from Roti

ลอก list -> ruby (ต่อ)

ต่อจากเมื่อวาน ที่ลอก Permutation จาก lisp -> ruby
วันนี้ลองลอก function ที่ชื่อ make-perm บ้าง

Note: การลอกแบบนี้ เป็นการยิงปืนทีเดียวได้นก 2 ตัว
เพราะช่วยทำให้เราเข้าใจทั้ง ruby และ lisp ได้ดีขึ้น
วิธีการเรียนโดยการลอกนี้
ในพวกที่เรียกวิชาจิตรกรรม ก็มีการใช้เหมือนกัน
โดยให้นักเรียนไปนั่งลอกภาพ master piece ของ จิตรกรชั้นนำต่างๆ

ดูลักษณะการใช้งานของ make-perm ก่อน
irb(main):785:0> p = make_perm [1,2], ['s','m'], ['red','blue']
#
irb(main):786:0> p.call
[1, "s", "red"]
irb(main):787:0> p.call
[1, "s", "blue"]
irb(main):788:0> p.call
[1, "m", "red"]
irb(main):789:0> p.call
[1, "m", "blue"]
irb(main):790:0> p.call
[2, "s", "red"]
irb(main):791:0> p.call
[2, "s", "blue"]
irb(main):792:0> p.call
[2, "m", "red"]
irb(main):793:0> p.call
[2, "m", "blue"]
irb(main):794:0> p.call
nil


ใจความหลักก็คือ สิ่งที่ return กลับมาจาก make_perm ก็คือ lambda
ที่เราสามารถ call เพื่อขอสมาชิกตัวถัดไปได้
(เป็น iterate object แบบหนึ่ง)

ลองดู code

def make_perm(*ls)
return nil if ls.empty?

if ls.length == 1 :
list = ls[0].clone
return lambda {
list.shift
}
else
prm = make_perm(*ls[1..ls.length])
list = ls[0].clone
return lambda {
y = prm.call
if y
[list[0], *y]
else
list.shift
return nil if list.length == 0
prm = make_perm(*ls[1..ls.length])
[list[0], *prm.call]
end
}
end
end


ถ้าใครยังไม่คุ้นกับ return lambda แบบนี้ลองดูตัวอย่าง ง่ายๆ นี้ก่อน

def make_pok(ls)
lambda {
ls.shift
}
end

p = make_pok([1,2,3])
p.call # => 1
p.call # => 2
p.call # => 3


key หลัก ก็คือการ set ตัวแปร local ที่อยู่นอก block lambda
ซึ่ง lambda block จะอ้างถึงตัวแปรเหล่านี้

prm = make_perm(*ls[1..ls.length])
list = ls[0].clone
return lambda {
y = prm.call
...
[list[0], *y]


Note: ที่ใช้ clone เพราะผมใช้วิธี array.shift ซึ่งมันมี effect ต่อ array ต้นฉบับ

สังเกตุว่ามีการใช้ เครื่องหมาย '*'

[list[0], *y]

คือถ้าไม่ใส่ * ผลลัพท์จะได้แบบนี้แทน

[1, ["s", "red"]]

Related link from Roti

วันนี้อ่าน Permutation Utilities in Lisp
utility นี้ไว้ช่วยจัดการกับ combination ของ variables
(ถ้าในทาง database ก็เรียกว่า cartesian join)

ยกตัวอย่าง
กรณีเราต้องการ print combination ของ
สี, ขนาด
กำหนดให้สี => red, blue
ขนาด => s, m, l, xl
combination ของกรณีนี้ ก็คือ

red, s
red, m
red, l
red, xl
blue, s
blue, m
blue, l
blue, xl


อ่านเฉยๆ ไม่ลองเขียนก็ไม่ซาบซื้ง
ก็เลยลอง implement ด้วย ruby ดู

Note: ใน Permutation Utilities in Lisp
เขา implement ไว้ 2 แบบ แต่ผมจะลองเขียนแค่แบบเดียว คือแบบ maperm

ขั้นแรกลองแปลงแบบตรงๆก่อน
def maperm (func, *lists)  
case
when lists.empty? :
return nil
when lists.length == 1 :
lists[0].each do |elm|
func.call elm
end
else
lists[0].each do |elm|
wrapper = lambda {|*args|
func.call elm, *args
}

maperm(wrapper, *lists[1..lists.length])
end
end
nil
end

เวลาเรียกใช้ ก็เรียกแบบนี้
maperm(lambda {|a,b| puts "#{a},#{b}"},['red','blue'],['s','m','l','xl'])

หลักๆ ก็คือมันจะ iterate array ตัวแรก
โดยแต่ละ step ก็จะมีการ recursive call เพื่อที่จะ iterate array ตัวถัดไป
หัวใจหลัก ก็คือการ ห่อ (wrap) fีืunc ก่อนที่จะ recursive ไปยัง array ตัวถัดไป

wrapper = lambda {|*args|
func.call elm, *args
}

maperm(wrapper, *lists[1..lists.length])

พอ recursive มาจนถึง array ตัวสุดท้าย
ก็จะเกิดการเรียกใช้ function ที่ถูกห่อมา

when lists.length == 1 :
lists[0].each do |elm|
func.call elm
end


หลังจากลอง run ผ่านแล้ว
ก็เลยแปลง code ให้เป็น ruby มากขึ้น
โดยเปลี่ยนจาก lambda syntax ไปเป็น block syntax แทน

def maperm3(*lists, &blk)
case
when lists.empty?
return nil
when lists.length == 1
lists[0].each { |elm|
yield elm
}
else
lists[0].each { |elm|
maperm3(*lists[1..lists.length]) { |*args|
blk.call elm, *args
}
}
end
nil
end


เวลาเรียกใช้ ก็
maperm3(['red','blue'],['s','m','l','xl']) {|a,b| puts "#{a}, #{b}"}

Related link from Roti

Wednesday, February 08, 2006

เขียน javascript บน Emacs, run บน Firefox

เดิมทีเวลาจะทดลองอะไรที่เกี่ยวกับ javascript รู้สึกมันยุ่งยากพิกล
ต้องไปเขียนหลบๆซ่อนๆไว้ใน html file
ตอนนี้มี solution ใหม่แล้ว

Mozrepl



ใช้ emacs เป็น editor
แต่ส่งไป run บน firefox

การใช้งาน ก็เป็นไปใน style ค่อยๆเขียน ค่อยๆ compile ทีละ function
ตามทำนอง incremental compilation

ส่วนคำว่า repl ใน mozrepl น่าจะย่อจาก Read-eval-print loop

Note:
การติดตั้ง extension ใน firefox
ของผมใช้วิธี download xpi file มาแล้วค่อยลากลง firefox
(ทำไมเปิดจาก internet ตรงๆไม่ได้ ก็ไม่รู้)

Related link from Roti

Tuesday, February 07, 2006

ฟังหูไว้หู
เท็จจริงประการใดไม่ทราบ

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


จาก สมคิดกับสุลักษณ์

Related link from Roti

scheme newbies code

ใน Programming musings
เขาลงเรื่อง scheme code kata
ตั้งปัญหาว่า
ถ้ามี input นี้
(a 1 2 3 b 4 5 c d 8 9 e)
แล้วต้องการ convert ให้เป็นแบบนี้
((a (1 2 3)) (b (4 5)) (c ()) (d (8 9)) (e ()))
จะเขียนอย่างไงดี

ในฐานะ newbie อย่างเรา
ก็อดลองไม่ได้
ผลลัพท์ที่ run ผ่านครั้งแรก

(define (group ls)
(if (empty? ls)
'()
(let ((fst (first ls))
(rst (rest ls)))
(if (symbol? fst)
(cons (cons fst (list (scan-num rst))) (group rst))
(group rst)))))


(define (scan-num ls)
(if (empty? ls)
'()
(if (not (symbol? (first ls)))
(cons (first ls) (scan-num (rest ls)))
'())))

ได้คำตอบ แต่มี loop ที่ทำซ้ำ โดยไม่ได้ประโยชน์อะไรฝังอยู่ด้วย

ถ้ารู้ว่า พวกเก่งๆ เขาเขียนอย่างไร ก็ลองดูที่นี่
Link

Note: idea ของผม จะคล้ายๆของคนนี้ my_pengy
แต่ผมยังจัดความคิดได้ไม่เป็นระเบียบเท่าเขา

Related link from Roti

Monday, February 06, 2006

rails.el

https://opensvn.csie.org/mvision/emacs/.emacs.d/rails.el
Emacs Minor Mode ที่ช่วย develop Rails Application
feature ที่มี ก็คือ
  • switch ระหว่าง view กับ controller ด้วยการกด C-t
  • access config file, log file ได้ง่ายขึ้น (ผ่านทาง menu)
  • มี Abbrev ให้ใช้ จำนวนหนึ่ง
  • manage Webbrick process จากใน emacs

Related link from Roti

Sunday, February 05, 2006

Multi parameter กับ update_attributes ใน ActiveRecord

กำลังทำเรื่อง widget วันที่ ใน rails อยู่
ก็เลยพึ่งเห็นวิธีที่ rails ใช้จัดการกับวันที่

ใน rhtml template เราสามารถใช้ date_select
ช่วยการ render date widget ได้
หน้าตาที่ได้ ก็จะเป็น dropdown box แบบนี้



เมื่อเกิด form submit
ข้อมูลที่ post กลับมาจะอยู่ในรูปแบบนี้

{
"start_date(1i)"=>"2006",
"start_date(2i)"=>"1",
"start_date(3i)"=>"1"
}

สังเกตว่า มีวงเล็บ แปลกๆอยู่ด้วย

เมื่อ controller ได้รับข้อมูลกลับมา
ก็จะทำการ assign ให้กับ model
ซึ่งเราสามารถใช้คำสั่งแบบนี้ได้

mymodel.attributes = params[:mydate]
mymodel.save

หรือจะใช้

mymodel.update_attributes(params[:mydate])

ทั้งสองวิธีทำงานเหมือนกัน (วิธีที่ 2 code ข้างใน ก็ไปเรียกแบบที่ 1)

ประโยค
mymodel.attributes = ...

ไม่ได้เป็นการ assigment แบบธรรมดา
ภายใน ActiveRecord มีการ declare method นี้ไว้ดังนี้

def attributes=(attributes)
return if attributes.nil?
attributes.stringify_keys!

multi_parameter_attributes = []
remove_attributes_protected_from_mass_assignment(attributes).each do |k, v|
k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v)
end
assign_multiparameter_attributes(multi_parameter_attributes)
end

การทำงานก็คือ แต่ละ attribute ที่ pass เข้ามา
กรณีที่เป็น attribute แบบไม่มีเครื่องหมาย (
ก็จะถูกเรียกใช้แบบนี้ send(k+"=", v)

สมมติ attribute เป็น "name" => "pphetra"
ประโยค send(k+"=", v) ก็คือ send("name=", "pphetra)
ซึ่งเทียบได้กับ name = "pphetra"

แต่กรณีที่มันพบ attribute ที่มีเครืื่องหมาย "("
กรณีนี้จะถูก handle แบบพิเศษโดยใช้ assign_multiparameter_attributes
ซึ่งถ้าดูใน document ของ method นี้ เขาอธิบายการทำงานดังนี้

# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
# s for String, and a for Array. If all the values for a given attribute is empty, the attribute will be set to nil.

Related link from Roti

Update with ActiveRecord

ช่วงนี้เริ่มใช้ Rails มากขึ้น ก็เลยเริ่มมองเห็นภาพต่างๆชัดขึ้น
ลองดูวิธีการ update ของ ActiveRecord

ลองเทียบดูกับ Hibernate แล้วกัน
กรณี hibernate เราสามารถเลือก update model ได้ 2 แบบหลักๆคือ
  • วิธีที่ 1

    User user = session.load(User.class, id);
    user.setName(newName);
    session.update(user); // or session.flush();

  • วิธีที่ 2

    String hqlUpdate = "update Customer set name = :newName where name = :oldName";
    int updatedEntities = s.createQuery( hqlUpdate )
    .setString( "newName", newName )
    .setString( "oldName", oldName )
    .executeUpdate();



ใน ActiveRecord เราสามารถ update model ได้ 3 แบบ
  • วิธีที่ 1

    user = User.find(id)
    user.setName(newName)
    user.save

  • วิธีที่ 2

    user = User.find(1)
    user.update_attrubites("name"=>newName, ...)

  • วิธีที่ 3

    User.update(id, :name=>newName}

  • วิธีที่ 4

    User.update_all("name = '#{newName}'", "id = #{id}")


วิธีที่ 2 นี้เป็นวิธีที่ scaffold generate ออกมาให้

วิธีที่ 4 update เป็น set ได้ (เนื่องจากกำหนด where clause เอง)

วิธีที่ 2 กับ 3 ดูเหมือนๆกัน ต่างกันแค่อันหนึ่งเป็น class method อีกอันเป็น instance method
ซึ่งผมก็เดาว่า 2 วิธีนี้ น่าจะ share ใช้ source code ชุดเดียวกัน
แต่เปล่าเลย วิธีที่ 2 กับ 3 มี source code คนละชุด คนละ idea กันเลย
วิธีที่ 2 จะมีการ set structure ภายในบางอย่าง แล้วค่อยเรียก save method
ส่วนวิธีที่ 3 ใช้วิธีสร้าง sql ขึ้นมาแล้ว execute ตรงๆเลย

Related link from Roti