使用<<附加字符串不能按预期工作,但使用+

时间:2013-08-09 15:32:35

标签: 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]

一切都按预期工作。我的第一个版本导致意外行为的原因是什么?

2 个答案:

答案 0 :(得分:1)

  

key = word&lt;&lt; “”&lt;&lt; arr [index + 1]

问题是String#<<执行就地操作,因此下次使用时会修改字符串。另一方面,String#+返回一个新副本。

你被一种必要的副作用所困扰(这并不罕见,因为副作用是一个巨大的错误来源。除非有非常令人信服的性能原因,functional方法会产生更好的代码)。例如,可以使用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

答案 1 :(得分:1)

String#<<方法修改调用它的原始对象。 这就是你的word变量引用的对象 另一个对arr数组中一个字符串的引用。你可以看到这个 效果与代码:

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

因此,当您在一次通过迭代器时使用该方法时,它会影响 以下传球。

另一方面,String#+方法创建一个新的String对象来保存 组合的字符串。有了这个方法,通过迭代器就没有了 对其他通行证的影响。