使用<<无法按预期工作,但使用+可以

  • 本文关键字:可以 工作 使用 ruby
  • 更新时间 :
  • 英文 :


我有下面的代码,这给我标记的行带来了问题。

arr = 'I wish I may I wish I might'.split
dictionary = Hash.new
arr.each_with_index do |word, index|
  break if arr[index + 2] == nil
  key = word << " " << arr[index + 1] #This is the problem line 
  value = arr[index + 2]
  dictionary.merge!( { key => value } ) { |key, v1, v2| [v1] << v2 }
end
puts dictionary

运行此代码时,我希望得到以下输出:

{"I wish"=>["I", "I"], "wish I"=>["may", "might"], "I may"=>"I", "may I"=>"wish"}

然而,我得到的却是

{"I wish"=>["I may", "I"], "wish I"=>["may I", "might"], "I may"=>"I wish", "may I"=>"wish I"}

我发现如果我用替换问题线路

key = word + " " + arr[index + 1]

一切如预期。我的第一个版本的台词是什么导致了这种意想不到的行为?

字符串#<lt;方法修改对其进行调用的原始对象。这是word变量引用的对象对CCD_ 2数组中的字符串之一的另一个引用。你可以看到这个代码效果:

 a = 'Hello'
 b = a << ' ' << 'World'
 puts a.__id__
 puts b.__id__

因此,当您在迭代器的一次遍历中使用该方法时,它会影响接下来的传球也是如此。

另一方面,String#+方法创建一个新的String对象来保存组合字符串。使用此方法,遍历迭代器时没有对其他传球的影响。

key=单词<lt;"<lt;arr[index+1]

问题是String#<lt;执行就地操作,以便下次使用字符串时对其进行修改。另一方面,String#+返回一个新副本。

你已经被一个强制性的副作用所折磨(这并不罕见,因为副作用是bug的巨大来源。除非有非常令人信服的性能原因,否则函数方法会产生更好的代码)。例如,这就是使用Facets:中的each_consmap_by编写它的方式

words = 'I wish I may I wish I might'.split
dictionary = words.each_cons(3).map_by do |word1, word2, word3|
  ["#{word1} #{word2}", word3]
end

相关内容

最新更新