2011-03-11 57 views
1

我正在使用Ruby on Rails 3,並且希望改進以下代碼以更好地完成相同的操作。改進'each'語句

query = {} 
params.each { |k,v| query[k.singularize] = v } 

我怎麼能這樣做?

回答

1

如果你實際上發現

params.each { |k,v| query[k.singularize] = v } 

時間太長,singularize會佔用你的大部分時間。

如果大多數單詞是相同的,我會考慮memoization。

其實,如果你有一萬個參數,我會考慮一個代碼審查!

1
query = Hash[params.map{|k, v| [k.singularize, v]}] 
+0

它仍然循環數據一次並複製值。我也打算髮布這個確切的代碼,但我不認爲這是一個改進。如果有什麼更難閱讀。要在兩者之間運行一個基準,以查看性能是否有差異。待定... – nzifnab 2011-03-11 05:30:44

+0

您的代碼無效。 – 2011-03-11 05:30:44

+0

呃他錯過了最後一個關閉支架,沒有biggie – nzifnab 2011-03-11 05:31:22

0

嗯,我有一個想法(原理相同澤),並決定我想知道這是否是一個進步。這裏的基準測試結果:

params = {'puppies' => 'cute', 
    'dinosaurs' => 'angry', 
    'kittens' => 'kill them all', 
    'wat' => 4} 

Benchmark.bm do |x| 
    x.report(".each"){10000.times{query = {}; params.each{ |k,v| query[k.singularize] = v }}} 
    x.report("Hash"){10000.times{query = Hash[params.map{|k, v| [k.singularize, v]}]}} 
end 

而結果:

 user  system  total  real 
.each 3.850000 0.390000 4.240000 ( 4.260567) 
Hash 3.910000 0.400000 4.310000 ( 4.402304) 

所以差別非常小,雖然哈希是改善相反,遺憾的是 - 如果性能是你所關心。

我仍然傾向於使用Hash[]格式,只是因爲我喜歡.map的工作原理......但是.map也必須遍歷每一個項目,所以它沒有什麼不同。

編輯:

我去評論建議做一個真正的大的哈希,而不是一個小散10,000次。這裏的結果:

myhash = {} 
20000.times do |i| 
    myhash[i.to_s * 2 + 's'] = i 
end 

Benchmark.bm do |x| 
    x.report(".each"){query = {}; myhash.each{|k,v| query[k.singularize] = v}} 
    x.report("Hash"){query = Hash[myhash.map{|k,v| [k.singularize, v]}]} 
end 

結果:

 user  system  total  real 
.each 1.980000 0.110000 2.090000 ( 2.100811) 
Hash 2.040000 0.140000 2.180000 ( 2.176588) 

編輯2:幸得澤的第三種方法:

Benchmark.bm do |x| 
    x.report(".each"){query = {}; myhash.each{|k,v| query[k.singularize] = v}} 
    x.report("Hash"){query = Hash[myhash.map{|k,v| [k.singularize, v]}]} 
    x.report("with_object"){query = myhash.each_with_object({}){|(k, v), h| h[k.singularize] = v}} 
end 

     user  system  total  real 
.each 2.050000 0.110000 2.160000 ( 2.174315) 
Hash 2.070000 0.110000 2.180000 ( 2.187600) 
with_object 2.100000 0.110000 2.210000 ( 2.207763) 

如果您(或其他人)能找到一種方法,修改每個值就地我懷疑這將是最快的方式來做到這一點:

params.each{|arr| arr[0].singularize!} 

但你不能這樣做,因爲

  1. singularize!沒有定義,並
  2. 當您嘗試這樣做
params.each{|arr| arr[0].gsub!('s', '')} 

你得到一個錯誤:

TypeError: can't modify frozen string 

我只是堅持原來的版本:p

+0

而不是做一個小哈希一萬次,嘗試做一個大哈希一次? – 2011-03-11 05:51:29

+0

你是什麼意思,「它必須循環每個項目」?你不需要這樣做嗎? – 2011-03-11 06:10:16

+0

@Mladen它似乎是如此。我們試圖找到一種改進的方法,我找不到一個:)所以我對這個問題的答案是「堅持原始版本」 – nzifnab 2011-03-11 06:17:45

1

正如nzifnab寫道,我的其他代碼似乎是緩慢的。下面的一個可能比原始發佈稍快。

query = params.each_with_object({}){|(k, v), h| h[k.singularize] = v} 
+0

我從來不知道這種方法......事實證明,你可以在stackoverflow上學習東西;)哈哈。它在語法上很酷,但在邏輯上與他原來所做的相同......我將更新我的基準。 – nzifnab 2011-03-11 06:05:56

0

我在Ruby YARV 1.9中試過以下內容。1,無的ActiveSupport(因此reverse而不是singularize

require "benchmark" 

myhash = {} 
2000000.times do |i| 
    myhash[i.to_s * 2 + 's'] = i 
end 

Benchmark.bm do |x| 
    x.report(".each"){query = {}; myhash.each{|k,v| query[k.reverse] = v}} 
    x.report("Hash"){query = Hash[myhash.map{|k,v| [k.reverse, v]}]} 
    puts RUBY_VERSION 
    puts RUBY_ENGINE if defined?(RUBY_ENGINE) 
end 

給我

 user  system  total  real 
.each 6.350000 0.070000 6.420000 ( 6.415588) 
Hash 5.710000 0.100000 5.810000 ( 5.795611) 
1.9.1 
ruby 

所以在我的情況下,哈希也較快。

考慮到我的基準和nzifnab之間的速度差異,我想檢查大部分時間不是花在singularize上。

更新:

在1.8.7:

 user  system total  real 
.each 11.640000 0.380000 12.020000 (12.019372) 
Hash 15.010000 0.540000 15.550000 (15.552186) 
1.8.7 

因此,它是慢1.8下使用Hash?

+0

我正在使用Ruby 1.8.7 - 我的基準測試版具有大約相同速度的兩個版本(使用'.reverse') - 我想知道使用散列路由的更新版本還是Ruby更快。 – nzifnab 2011-03-11 06:36:39