Number to word
Posted by Revolution
Source จาก http://sohne.net/articles/2006/04/30/convert-numbers-to-words/ เท่าที่แกะดูก็ไม่ยากแต่ไม่เข้าใจว่า หลายที่เงื่อนไขไม่ลงเลย สงสัยทำเผื่อไว้
Source from http://sohne.net/articles/2006/04/30/convert-numbers-to-words/ I have some confuse with this code, some condition will not may happen but i still be.
มีที่ดีกว่านี้ที่ http://www.deveiate.org/projects/Linguistics/wiki/English
class Number def self.to_words(number) Number.new.to_s(number) end def self.commify(number) (s=number.to_s;x=s.length;s).rjust(x+(3-(x%3))).gsub(/(\d)(?=\d{3}+(\.\d*)?$)/, '\1,') end def initialize @unit = %w[zero one two three four five six seven eight nine] @teen = %w[ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen] @tens = %w[zero ten twenty thirty fourty fifty sixty seventy eighty ninety] @qtys = %w[hundred thousand million billion trillion quadrillion quintillion] @zero = ["zero"] @hundred = "hundred" @sepr = "and" end def to_s(number) out = quantify(number).flatten for x in 0 .. out.length - 1 out[x] = nil if out[x] == @sepr && out[x+1] == @sepr out[x] = nil if out[x] == "," && out[x+1] == "," end out.compact! out = @zero if out.length == 1 && out[0] == @sepr out.pop while out.last == @sepr out.shift while out.first == @sepr out.join(' ').gsub(/ ,/,',') end private def padded_groups(v) #make number to be 3 units per slot by fill zero infront (x%3 =0) out = [] padded = (s=v.to_s;x=s.length;s).rjust(x+(3-(x%3))).gsub(/ /,'0') padded.scan(/.{3}/) end def wordify(v) #Get 3 unit of slot and compare by ascii code, then word it out out = [] zero = '0'[0] h, t, u = v[0] - zero, v[1] - zero, v[2] - zero if h != 0 out << @unit[h] out << @hundred end #"and" word will add when hundred appear and unit appear out << @sepr if h != 0 && (t != 0 || u != 0) out << @sepr if h == 0 && t == 0 && u != 0 if t == 1 out << @teen[u] else out << @tens[t] if t != 0 out << @unit[u] if u != 0 end return out end def quantify(v) #make ach 3 of each slot to be a word by calling wodify v = padded_groups(v).reverse cur = pos = v.length - 1 out = [] while pos >= 0 if v[pos] == ',' out << ',' next end word = wordify(v[pos]) if word[0] != nil out << word out << @qtys[cur] if cur != 0 else out << @sepr end cur -= 1 pos -= 1 end return out end end puts Number.to_words(1234567890) puts Number.commify(1234567890)
ถ้าอยากรู้ว่า code ทำงานอย่างไรอ่านต่อข้างใน
แรกสุดเลยหลังจากที่มีตัวเลขเข้ามา method แรกที่ทำงานคือ padded_groups ทำหน้าที่แบ่งเลขออกเป็นชุด ชุดละ 3 หลัก
def padded_groups(v) #make number to be 3 units per slot by fill zero infront (x%3 =0) out = [] padded = (s=v.to_s;x=s.length;s).rjust(x+(3-(x%3))).gsub(/ /,'0') padded.scan(/.{3}/) end
โดยจะทำการเติม 0 ไว้ข้างหน้าในกรณีที่เมื่อแบ่ง 3 แล้วไมเต็ม แลวทำการแยกออกมาเป็นชุดๆ จากคำสั่ง scan(/.{3}/) เราจะได้ array ที่แต่ละตำแหน่งมีเลข 3 หน่วย /.{3}/ เป็น Regexp หมายถึงจับ 3
method padded_groups จะถูก method quantify เรียกใช้
def quantify(v) v = padded_groups(v).reverse cur = pos = v.length - 1 out = [] while pos >= 0 if v[pos] == ',' out << ',' next end word = wordify(v[pos]) if word[0] != nil out << word out << @qtys[cur] if cur != 0 else out << @sepr end cur -= 1 pos -= 1 end return out end
quantify จะเลือกทำจากหน่วยสูงสุดก่อน ถ้าจังหวะนั้นไปเจอจุลภาค (,) ก็จะใส่ต่อแล้วข้ามไป จากนั้นก็จะไปเรียก method wordify ซึ่งมีหน้าที่เปลี่ยนตัวเลขเป้นคำโดยเอาคำมาจาก method initialize เมื่อได้คำมาจาก wordify แล้วจะทำการใส่กำลังให้ตามตำแหน่งเช่น ร้อย พัน ล้าน และก็ใส่ and ถ้าเป็นหลักสุดท้ายในชุดนั้นๆ
ทีนี้ก็ต้องมาดูต่อที่ wordify เขียนได้จ๊าบจริงๆ ตรรกเจ๋งมาก
def wordify(v) #Get 3 unit of slot and compare by ascii code, then word it out out = [] zero = '0'[0] h, t, u = v[0] - zero, v[1] - zero, v[2] - zero if h != 0 out << @unit[h] out << @hundred end #"and" word will add when hundred appear and unit appear out << @sepr if h != 0 && (t != 0 || u != 0) out << @sepr if h == 0 && t == 0 && u != 0 if t == 1 out << @teen[u] else out << @tens[t] if t != 0 out << @unit[u] if u != 0 end return out end
ค่าที่ได้จาก padded_groups คือแต่ละชุดจะมี 3 หลัก เขาก็คิดว่ามันคือ ร้อย สิบ หน่วย แล้วก็เอาค่าแต่ละอันมาหักลบจากค่า acsii ของ 0 (acsii = 48) แล้วก็เอาแต่ละหลักมาใส่กำลัง ตรงหลักสิบคือปัญหาเพราะภาษาอังกฤษมันมีพวก teen คือ 11 ถึง 19 แล้วก็พวก ten คือ 10 20 30.. 90 แต่ก็แค่เอาค่าหลักสิบมาเทียบว่าถ้าเป็น 1 คือ teen นั่นเอง
จากนั้นก็มาสู่ method to_s หลัก เอาค่าที่เป็นคำแล้วมาเรียบเรียง
def to_s(number) out = quantify(number).flatten for x in 0 .. out.length - 1 out[x] = nil if out[x] == @sepr && out[x+1] == @sepr out[x] = nil if out[x] == "," && out[x+1] == "," end out.compact! out = @zero if out.length == 1 && out[0] == @sepr out.pop while out.last == @sepr out.shift while out.first == @sepr out.join(' ').gsub(/ ,/,',') end
สุดท้ายก็เอาคำ "and" ที่อยู่ข้างหน้าและข้างหลังออกและทำการรวบ array ทุกตำแหน่งให้เป็นคำเดียว
เจ๋งจริงๆ ได้แนวความคิดมาเขียนเป็นภาษาไทยได้เลย รอหน่อยแล้วผมจะเอา code สำหรับภาษาไทยมาแปะ





taiko said 2 days later: