อยากหาอะไรให้ Ruby Forum เล่นกันสนุกๆ (เห็นช่วงนี้เงียบมาก) ถ้าใครได้เริ่มเขียน Ruby มาบ้าง จะรู้ว่าเวลาทำอะไรแต่ละอย่าง มันมีวิธีแก้หลายวิธีเหลือเกิน (There’s more than one way to do it) บางทีก็เสียเวลากว่าจะตัดสินใจว่าควรจะใช้วิธีไหนดี ซึ่งบางคนก็เน้นว่าเขียนแบบนี้แหละรันเร็วสุด บางคนก็สนใจว่าเขียนแบบนี้สวยสุด บางคนก็บอกว่าใช้งานได้พอแล้ว :) (แนวใครแนวมัน)
เลยอยากมี Series โจทย์ไว้เล่นกันแต่ละอาทิตย์ ใครจะตั้งโจทย์ก็ได้(แล้วใครจะตั้งโจทย์? คุยกันอีกที) เรียกว่า CNRuby Series (Codenone Ruby Series) ละกัน
แต่ขอเป็นโจทย์ง่ายๆ (เน้นว่าง่ายๆ) แต่มีอะไรให้เล่น วิธีแก้(code) ออกมาไม่ควรเกิน 20 บรรทัด ไม่เอาแบบโจทย์โอลิมปิก codegolf..etc. เห็นแล้วเหนื่อย และหลายคนในนี้คงไม่ว่างทำ จุดประสงค์ก็เพื่อ อยากให้เห็นว่าปัญหาเล็กๆง่ายๆ มันก็มีวิธีแก้หลายแบบ มีข้อดีข้อเสียในแต่ละวิธีนั้นอย่างไรบ้าง Ruby โปรแกรมเมอร์แต่ละท่านเขียนกันอย่างไร อยากให้ทุกคนได้ร่วมตอบง่ายๆ ไม่ต้องเสียเวลาทำนาน
Winner จะมีสองแบบคือ 1.Style Award- เขียนสวยสุด (ช่วยกันเลือก?) 2.Performance Award-เขียนออกมาแล้วรันเร็วสุด (คนตั้งโจทย์มี benchmark code ให้) (ไม่มีรางวัล :) )
เริ่มต้นด้วย
CNRuby-1
โจทย์: กำหนัดตัวแปร str เป็น String นับจำนวนคำซ้ำที่อยู่ใน String นั้น และเก็บผลลัพท์ในโครงสร้างแบบ Hash
ตัวอย่างเช่น สมมติ ให้
str = "Ruby is a dynamic, open source programming language with a focus on simplicity and productivity."ให้แปลงเป็น
{"productivity"=>1, "and"=>1, "a"=>2, "focus"=>1, "language"=>1, "simplicity"=>1, "programming"=>1, "ruby"=>1, "on"=>1, "source"=>1, "dynamic"=>1, "with"=>1, "open"=>1, "is"=>1}
โค้ดไว้สำหรับทดสอบ speed
require 'benchmark' ITERATION = 500 def measure(&block) ITERATION.times { block.call } end str = "" 5000.times do str << (('a'..'z').to_a[rand(26)] + ('a'..'z').to_a[rand(26)] + ('a'..'z').to_a[rand(26)]+ ('a'..'z').to_a[rand(26)])[0..rand(3)] + ' ' end br = Benchmark.bmbm do |b| b.report("Running #{ITERATION} iterations on string length #{str.length}") do measure { # ใส่โค้ดเพื่อทดสอบตรงนี้ } end end
จะมีคนเล่นด้วยรึเปล่าเนี่ย
ป.ล. ผล benchmark รันบน OSX, Intel Core 2 Duo, 2.4 GHz, RAM 4 GB, Bus 800 MHz
ประกาศผล :P
[Speed Winner]
def count_word2(str) hash = Hash.new(0) str.each(' ') do |word| hash[word] += 1 end hash end
from pphetra
[Style Winner]
def count_word(str) hash = Hash.new(0) str.split.each do |word| hash[word] = hash[word] + 1 end hash end
from pphetra
กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น
ไม่มีใครตอบเลย ผมขอโซโล่แบบง่าย ๆ ก่อนเลยนะ :D ฮ่า ๆ
จะเริ่มเรียนแล้ว เดี๋ยวเรียนเสร็จแล้วค่อยโพสต่อ
ลืม , รึเปล่า
ลืมอ่ะ่ครับ โอ พึ่งนึกได้ ตอนเช้าเขียนแบบรีบจัด :p
RESULT: 3.797270
RESULT: 3.686017
RESULT: 2.999015
(จาก 3 code ด้านบน ผมชอบ style นี้มากสุด บังเอิญ..เร็วสุดด้วย 55+)
ด้วยคำรู้อันน้อยนิด ผมเพิ่งรู้ว่าทำแบบนี้ได้เลย โดยไม่ต้องเช็คว่า มี hash[word] ก่อนหรือปล่าว ตอนแรกเข้าใจว่า ถ้า hash[word] มันไม่มีค่า มันก็จะเป็น nil += 1 ซึ่งมันไม่น่าจะได้
Hash.new(0) จะให้ default value ของ hash เป็น 0 ( ถ้าเป็น {} default เป็น nil ) ก็เลยบวกกันได้
แบบนี้นี่เอง ปกติเวลาผมใช้ hash ผมจะใช้ เป็น var={} แบบนี้อย่างเดียว ไม่เคยใช้ Hash.new เลย มาคราวนี้ได้เปลี่ยนวิธีเขียนละ เย้
RESULT: 3.226794
RESULT: 5.686920
แบบนี้รันช้าจังแฮะ…
ผมชอบอันนี้อ๊ัะ >_<
ความจริงอันนี้อ่านเข้าใจง่ายดี
scan ผมก็ลอง ช้ากว่ากันเยอะ ส่วนที่ช้าสุด ก็คือ เขียน loop เอง แล้วใช้พวก [] slice คำออกมา ช้ากว่าเกือบ 4 เท่า
RESULT: 4.584874
อันนี้ก็ยังช้าอยู่ดี… แต่ก็สวยอีกแบบ
สรุปผลโจทย์ข้อนี้กันเลยมั้ยครับ… taiko_gogo , pphetra ช่วยเลือกหน่อยครับว่า code แบบไหนน่าจะได้ style winner
ส่วน speed winner ก็ยกให้ code
ของ pphetra
ผมขอโหวตอันที่ใช้ scan คร๊าบ + ขอโจทย์อันถัดไปด้วย :D
ผม vote str.split.each เพราะ อ่านเข้าใจง่ายดี
รอข้อต่อไปครับ :P
/me nudge zdk