[譯] Ruby 2.6 String的split 方法支援程式碼塊執行

Madao-3發表於2018-08-01

本部落格系列翻譯自 Bigbinary 的 Ruby 2.6 系列, 已得到作者允許。Ruby 2.6.0-preview2 現已釋出

在Ruby 2.6之前, String#split 方法會返回一個被分割過的字串陣列。

在Ruby 2.6,我們可以傳遞程式碼塊來列舉 String#split 的返回結果並進行操作。這樣做就不用建立一個新的陣列,因此記憶體會用得更具有效率。

接下來我們將增加一個方法 is_fruit? 來了解新的split 用法。

def is_fruit?(value)
  %w(apple mango banana watermelon grapes guava lychee).include?(value)
end
複製程式碼

輸入是一組逗號連起來的蔬菜或者水果的名字,目標把它們用逗號切割並且把裡頭的水果儲存起來。

過去的 String#split 用法

input_str = "apple, mango, potato, banana, cabbage, watermelon, grapes"

splitted_values = input_str.split(", ")
=> ["apple", "mango", "potato", "banana", "cabbage", "watermelon", "grapes"]

fruits = splitted_values.select { |value| is_fruit?(value) }
=> ["apple", "mango", "banana", "watermelon", "grapes"]
複製程式碼

Using split an intermediate array is created which contains both fruits and vegetables names.

Ruby 2.6 的可以帶上程式碼塊的 String#split 用法

fruits = []

input_str = "apple, mango, potato, banana, cabbage, watermelon, grapes"

input_str.split(", ") { |value| fruits << value if is_fruit?(value) }
=> "apple, mango, potato, banana, cabbage, watermelon, grapes"

fruits
=> ["apple", "mango", "banana", "watermelon", "grapes"]
複製程式碼

當我們把程式碼塊傳遞給split 方法,它會返回被分割的字串並且不會建立一個陣列。String#split 的程式碼塊會出每一個被分割的字串 ,這個例子裡頭我們往fruits 陣列中推送水果名稱。

後續更新

效能比較

我們建立了一個巨大無比的隨機字串來評估split 和 split + 程式碼塊的效能差異。

require 'securerandom'

test_string = ''

100_000.times.each do
  test_string += SecureRandom.alphanumeric(10)
  test_string += ' '
end
複製程式碼
require 'benchmark'

Benchmark.bmbm do |bench|

  bench.report('split') do
    arr = test_string.split(' ')
    str_starts_with_a = arr.select { |str| str.start_with?('a') }
  end

  bench.report('split with block') do
    str_starts_with_a = []
    test_string.split(' ') { |str| str_starts_with_a << str if str.start_with?('a') }
  end

end
複製程式碼

結果

Rehearsal ----------------------------------------------------
split              0.023764   0.000911   0.024675 (  0.024686)
split with block   0.012892   0.000553   0.013445 (  0.013486)
------------------------------------------- total: 0.038120sec

                       user     system      total        real
split              0.024107   0.000487   0.024594 (  0.024622)
split with block   0.010613   0.000334   0.010947 (  0.010991)
複製程式碼

我們也用了benchmark/ips 來做了另一種效能測量。

require 'benchmark/ips'
Benchmark.ips do |bench|


  bench.report('split') do
    splitted_arr = test_string.split(' ')
    str_starts_with_a = splitted_arr.select { |str| str.start_with?('a') }
  end

  bench.report('split with block') do
    str_starts_with_a = []
    test_string.split(' ') { |str| str_starts_with_a << str if str.start_with?('a') }
  end

  bench.compare!
end
複製程式碼

結果是

Warming up --------------------------------------
               split     4.000  i/100ms
    split with block    10.000  i/100ms
Calculating -------------------------------------
               split     46.9062.1%) i/s -    236.000  in   5.033343s
    split with block    107.3011.9%) i/s -    540.000  in   5.033614s

Comparison:
    split with block:      107.3 i/s
               split:       46.9 i/s - 2.29x  slower
複製程式碼

結果看來,split 帶上block 的新用法在效能上是兩倍於過去的老玩法的。

這裡是相關的commit討論

原文地址

部落格地址

相關文章