使用#each_with_index更改数组时的结果很奇怪

时间:2015-12-17 02:15:31

标签: arrays ruby

我正在尝试编写一些循环遍历字符串数组的代码,清理条目,然后将清理后的条目添加到跟踪每个单词出现频率的哈希值。这是我的第一个解决方案:

Dockerfile

它工作正常,但是在数组中循环两次感觉非常低效,所以我一直试图找到一种方法去做,并偶然发现以下内容:

puts("Give me your text.")
text = gets.chomp

words = text.split
frequencies = Hash.new(0)
words.map! do |word|
  word.tr("\",.", "")
end
words.each do |word|
  frequencies[word] += 1
end

根据我对puts("Give me your text.") text = gets.chomp words = text.split frequencies = Hash.new(0) words.each_with_index do |word, index| words[index].tr!("\",.", "") frequencies[word] += 1 end 的理解,这应该不起作用,但不管怎么说,哈希接收每个字符串的干净版本:https://repl.it/B9Gw。这里发生了什么?如果没有循环两次,有没有不同的方法来解决这个问题?

编辑:经过一些阅读,我能够通过以下方式只使用一个循环来解决问题:

each_with_index

然而,这更像是一个JS或C ++解决方案,并不像惯用的Ruby。还有其他选择吗?另外,为什么puts("Give me your text.") text = gets.chomp words = text.split frequencies = Hash.new(0) for i in 0..words.length-1 words[i].tr!("\",.", "") frequencies[words[i]] += 1 end 方法甚至有效?

1 个答案:

答案 0 :(得分:3)

您正在使用String#tr!方法,该方法会破坏性地修改字符串,而不是返回新字符串。你再次查看哈希值(使用words[index])的事实不会改变任何东西,因为字符串对象仍然是相同的 - 所以word用来修改{{1}哈希也被修改了。

  

有没有不同的方法来解决这个问题而不循环两次?

一种显而易见的方法是使用您使用的相同逻辑,但没有frequencies(这里没有任何区别)。我建议使用非破坏性with_index代替String#tr,以便更清楚地清除哪些字符串以及哪些字符串没有清除。

String#tr!

如果你想明确流程的frequencies = Hash.new(0) words.each do |word| cleaned = word.tr("\",.", "") frequencies[cleaned] += 1 end 阶段并且仍然只循环一次,你可以利用ruby的惰性枚举器:

map

在这里,即使我们执行frequencies = Hash.new(0) cleaned_words = words.lazy.map { |word| word.tr("\",.", "") } cleaned_words.each do |cleaned| frequencies[cleaned] += 1 end 然后执行map,集合也只会遍历一次,而ruby不会创建任何中间数组。