我正在尝试编写一些代码,该代码将循环遍历字符串数组,清理条目,然后将清理后的条目添加到跟踪每个单词出现频率的哈希中。这是我的第一个解决方案:
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
根据我对each_with_index的理解,这不应该起作用,但不知何故它确实起作用,并且哈希接收每个字符串的干净版本:https://repl.it/B9Gw 。这里发生了什么?有没有不同的方法来解决这个问题而不循环两次?
编辑:经过一番阅读后,我能够通过以下方式仅使用一个循环来解决问题:
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
但是,这更多的是 JS 或 C++ 解决方案,看起来不像惯用的 Ruby。还有其他选择吗?另外,为什么 each_with_index
方法甚至有效?
最佳答案
您正在使用 String#tr!
方法,该方法会破坏性地修改字符串,而不是返回新字符串。事实上,您再次在哈希上查找它(使用 words[index]
)不会改变任何内容,因为字符串对象仍然相同 - 所以 word
> 您用来修改频率
的哈希值也会被修改。
And is there a different way to solve this problem without looping twice?
一个明显的方法是使用与您使用的相同的逻辑,但没有 with_index
(无论如何,这在这里没有任何区别)。我建议使用非破坏性的 String#tr
而不是 String#tr!
,以便更清楚哪些字符串已被清理,哪些尚未清理。
frequencies = Hash.new(0)
words.each do |word|
cleaned = word.tr("\",.", "")
frequencies[cleaned] += 1
end
如果您想明确进程的 map
阶段,并且仍然只循环一次,您可以利用 ruby 的惰性枚举器:
frequencies = Hash.new(0)
cleaned_words = words.lazy.map { |word| word.tr("\",.", "") }
cleaned_words.each do |cleaned|
frequencies[cleaned] += 1
end
在这里,即使我们先执行一个map
,然后执行一个each
,该集合也只被遍历一次,并且 ruby 不会创建任何中间数组。
关于arrays - 使用 #each_with_index 更改数组时出现奇怪的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34325542/