Khi dịch thoughts thành code, bạn thường dùng methods bạn familiar nhất. Những method top of mind, tự động đến: thấy string cần clean up, ngón tay type methods cho result.
Thường, methods tự động đó là generic Ruby methods, vì chúng ta read/write chúng nhiều hơn, ví dụ #gsub generic method substitute characters in strings. Nhưng Ruby offer nhiều hơn, với specialized convenience methods cho standard operations.
Tôi yêu Ruby's rich idiom vì nó làm code elegant, dễ đọc. Muốn benefit từ richness, cần spend time refactor even simplest parts — ví dụ clean up string — và cần effort expand vocabulary. Câu hỏi: extra effort worth it?
Bốn Cách Xóa Space
String đại diện credit card number: "055 444 285". Để work với nó, muốn remove spaces. #gsub làm được; với #gsub substitute anything với everything. Nhưng có options khác.
string = "055 444 285"
string.gsub(/ /, '')
string.gsub(' ', '')
string.tr(' ', '')
string.delete(' ')
# => "055444285"
Expressiveness là điều tôi thích nhất convenience methods. Cái cuối ví dụ tốt: không obvious hơn "delete spaces". Về trade-offs, readability là priority đầu, trừ khi gây performance problems. Xem #delete favorite của tôi cause bao nhiêu pain.
Benchmark các example. Method nào bạn nghĩ fastest?
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report('gsub') { string.gsub(/ /, '') }
x.report('gsub, no regex') { string.gsub(' ', '') }
x.report('tr') { string.tr(' ', '') }
x.report('delete') { string.delete(' ') }
x.compare!
end
Đoán order từ most đến least performant. Mở toggle xem result
Comparison:
delete: 2326817.5 i/s
tr: 2121629.8 i/s - 1.10x slower
gsub, no regex: 868184.1 i/s - 2.68x slower
gsub: 474970.5 i/s - 4.90x slower
Tôi không surprised về order, nhưng speed differences vẫn surprise. #gsub không chỉ slower, mà cũng require extra effort cho reader 'decode' arguments. Xem comparison khi clean up hơn spaces.
Pick Your Numbers
Phone number: '(408) 974-2414'. Chỉ cần numericals => 4089742414. Thêm #scan vì thích nó express clearly aim for particular things, thay vì remove unwanted things.
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report ('gsub') { string.gsub(/[^0-9] /, '') }
x.report('tr') { string.tr("^0-9", "") }
x.report('delete_chars') { string.delete("^0-9") }
x.report('scan') { string.scan(/[0-9]/).join }
x.compare!
end
Lại đoán order, mở toggle xem answer
Comparison:
delete_chars: 2006750.8 i/s
tr: 1856429.0 i/s - 1.08x slower
gsub: 523174.7 i/s - 3.84x slower
scan: 227717.4 i/s - 8.81x slower
Dùng regex slow down, không surprising. Intention revealing expressiveness của #scan cost dearly. Nhưng xem specialized methods Ruby handle cleanup, cho tôi taste for more.
On the Money
Thử ways remove substring "€ " từ string "€ 300". Một số solutions chỉ định exact substring "€ ", số khác remove all currency symbols hoặc all non-numerical characters.
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report('delete specific chars') { string.delete("€ ") }
x.report('delete non-numericals') { string.delete("^0-9") }
x.report('delete prefix') { string.delete_prefix("€ ") }
x.report('delete prefix, strip') { string.delete_prefix("€").strip }
x.report('gsub') { string.gsub(/€ /, '') }
x.report('gsub-non-nums') { string.gsub(/[^0-9]/, '') }
x.report('tr') { string.tr("€ ", "") }
x.report('slice array') { string.chars.slice(2..-1).join }
x.report('split') { string.split.last }
x.report('scan nums') { string.scan(/\d/).join }
x.compare!
end
Bạn expect, correctly, winner là một trong #deletes. Nhưng variant nào của #delete bạn expect fastest? Plus: method khác nhanh hơn một vài #deletes. Cái nào?
Đoán và mở.
Comparison:
delete prefix: 4236218.6 i/s
delete prefix, strip: 3116439.6 i/s - 1.36x slower
split: 2139602.2 i/s - 1.98x slower
delete non-numericals: 1949754.0 i/s - 2.17x slower
delete specific chars: 1045651.9 i/s - 4.05x slower
tr: 951352.0 i/s - 4.45x slower
slice array: 681196.2 i/s - 6.22x slower
gsub: 548588.3 i/s - 7.72x slower
gsub-non-nums: 489744.8 i/s - 8.65x slower
scan nums: 418978.8 i/s - 10.11x slower
Surprised even slicing array faster than #gsub và pleased thấy #split fast. Note delete all non-numericals faster than delete specific substring.
Follow the Money
Remove currency after number. (Bỏ slower #gsub variants.)
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report('gsub') { string.gsub(/ USD/, '') }
x.report('tr') { string.tr(" USD", "") }
x.report('delete_chars') { string.delete("^0-9") }
x.report('delete_suffix') { string.delete_suffix(" USD") }
x.report('to_i.to_s') { string.to_i.to_s }
x.report("split") { string.split.first }
x.compare!
end
Có draw giữa winners. Hai method nào bạn expect compete fastest?
Và: đoán bao nhiêu slower #gsub ở đây.
Comparison:
delete_suffix: 4354205.4 i/s
to_i.to_s: 4307614.6 i/s - same-ish: difference falls within error
split: 2870187.8 i/s - 1.52x slower
delete_chars: 1989566.1 i/s - 2.19x slower
tr: 1853957.1 i/s - 2.35x slower
gsub: 524080.6 i/s - 13.22x slower
Không luôn có specialized method suit needs. Không dùng #to_i nếu cần keep leading "0". Và #delete_suffix heavily lean on assumption currency là US Dollars.
Specialized methods như precision tools — suitable specific task specific context. Sẽ luôn có cases #gsub chính xác cần. Nó versatile, luôn top of mind. Nhưng harder to process và thường slower, even slower hơn expected. Đối với tôi, Ruby's richness cũng là reason làm nó fun to work with. Speed wins là nice bonus.