Khi dịch suy nghĩ của bạn thành mã, rất có thể, bạn sử dụng các phương pháp mà bạn quen thuộc nhất. Đây là những phương pháp quan tâm hàng đầu và tự động đến với bạn:bạn thấy một chuỗi cần dọn dẹp và ngón tay của bạn gõ các phương thức sẽ nhận được kết quả.
Thông thường, các phương thức bạn nhập tự động là các phương thức Ruby chung chung nhất, vì chúng là những phương thức mà chúng ta đọc và viết nhiều hơn những phương thức khác, ví dụ:#gsub
là một phương thức chung để thay thế các ký tự trong chuỗi. Nhưng, Ruby có rất nhiều thứ để cung cấp, với nhiều phương pháp tiện lợi chuyên biệt hơn cho các hoạt động tiêu chuẩn.
Tôi yêu thích câu thành ngữ phong phú của Ruby chủ yếu vì nó làm cho mã trở nên thanh lịch và dễ đọc hơn. Nếu chúng ta muốn hưởng lợi từ sự phong phú này, chúng ta cần dành thời gian để cấu trúc lại ngay cả những phần đơn giản nhất của mã của chúng ta — ví dụ, dọn dẹp một chuỗi — và cần một chút nỗ lực để mở rộng vốn từ vựng của chúng ta. Câu hỏi đặt ra là:nỗ lực thêm có xứng đáng không?
Bốn cách để xóa dấu cách
Đây là chuỗi đại diện cho số thẻ tín dụng:"055 444 285". Để làm việc với nó, chúng tôi muốn xóa các khoảng trắng. #gsub
có thể làm được điều này; với #gsub
bạn có thể thay thế bất cứ thứ gì bằng mọi thứ. Nhưng có những lựa chọn khác.
string = "055 444 285"
string.gsub(/ /, '')
string.gsub(' ', '')
string.tr(' ', '')
string.delete(' ')
# => "055444285"
Đó là sự thể hiện mà tôi thích nhất về các phương pháp tiện lợi. Cái cuối cùng là một ví dụ điển hình về điều này:nó không rõ ràng hơn là "xóa dấu cách". Suy nghĩ về sự cân bằng giữa các lựa chọn, tính dễ đọc là ưu tiên hàng đầu của tôi, trừ khi tất nhiên, nó gây ra các vấn đề về hiệu suất. Vì vậy, hãy xem giải pháp yêu thích của tôi, #delete
nguyên nhân thực sự.
Tôi đã chuẩn hóa các ví dụ trên. Bạn nghĩ phương pháp nào trong số những phương pháp này là nhanh nhất?
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 thứ tự từ người có hiệu suất cao nhất đến kém nhất. Mở nút chuyển để xem kết quả
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 ngạc nhiên về thứ tự, nhưng sự khác biệt về tốc độ vẫn khiến tôi ngạc nhiên. #gsub
không chỉ chậm hơn, mà nó còn đòi hỏi người đọc phải nỗ lực nhiều hơn để 'giải mã' các lập luận. Hãy xem so sánh này hoạt động như thế nào khi dọn dẹp nhiều hơn không gian.
Chọn số của bạn
Lấy số điện thoại sau:'(408) 974-2414'
. Giả sử chúng ta chỉ cần số => 4089742414
. Tôi đã thêm một #scan
cũng như vì tôi thích điều đó thể hiện rõ ràng hơn rằng chúng tôi hướng tới một số điều cụ thể, thay vì cố gắng loại bỏ tất cả những thứ chúng tôi không muốn.
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
Một lần nữa, hãy đoán thứ tự, sau đó mở nút chuyển đổi để xem câu trả lời
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
Sử dụng regex làm chậm mọi thứ, điều đó không có gì đáng ngạc nhiên. Và ý định tiết lộ tính biểu cảm của #scan
khiến chúng ta phải trả giá đắt. Nhưng khi nhìn vào cách các phương pháp chuyên biệt của Ruby xử lý việc dọn dẹp, tôi sẽ hiểu thêm.
Về tiền
Hãy thử một số cách xóa chuỗi con "€ "
từ chuỗi "€ 300"
. Một số giải pháp sau chỉ định chính xác chuỗi con "€ "
, một số sẽ chỉ xóa tất cả các ký hiệu tiền tệ hoặc tất cả các ký tự không phải số.
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 có thể mong đợi và đúng như vậy, người chiến thắng là một trong những #delete
S. Nhưng cái nào trong #delete
biến thể bạn mong đợi là nhanh nhất? Thêm vào đó:một trong những phương pháp khác nhanh hơn một số phương pháp trong số #delete
S. Cái nào?
Đoán và sau đó 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
Tôi ngạc nhiên rằng ngay cả việc cắt một mảng cũng nhanh hơn #gsub
và tôi luôn hài lòng khi thấy tốc độ của #split
Là. Và lưu ý rằng xóa tất cả các số không phải là số nhanh hơn xóa một chuỗi con cụ thể.
Theo dõi tiền bạc
Hãy xóa đơn vị tiền tệ sau số. (Tôi đã bỏ qua #gsub
chậm hơn các biến thể.)
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ó một trận hòa giữa những người chiến thắng. Bạn mong đợi 2 người nào sẽ cạnh tranh để trở thành người nhanh nhất?
Và:đoán _ chậm hơn bao nhiêu `# 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 phải lúc nào cũng có một phương pháp chuyên biệt phù hợp với nhu cầu của bạn. Bạn không thể sử dụng #to_i
nếu bạn cần giữ số "0" ở đầu. Và #delete_suffix
chủ yếu dựa vào giả định rằng đơn vị tiền tệ là Đô la Mỹ.
Các phương pháp chuyên biệt giống như các công cụ chính xác — phù hợp với một nhiệm vụ cụ thể trong bối cảnh cụ thể. Vì vậy, sẽ luôn có trường hợp #gsub
là chính xác những gì chúng tôi cần. Nó rất linh hoạt và luôn được ưu tiên hàng đầu. Nhưng nó có thể khó xử lý hơn một chút và thường chậm hơn, thậm chí chậm hơn tôi mong đợi. Đối với tôi, sự giàu có của Ruby cũng là một trong những lý do khiến nó rất thú vị khi làm việc cùng. Chiến thắng tốc độ là một phần thưởng tuyệt vời.