缩短答案出错?

时间:2013-06-03 22:25:26

标签: ruby

我想写一个函数来计算str中所有字母的出现次数,听起来很容易,我想出了这个

def letter_count(str)

     hash = {}
     letters = str.gsub(' ','').split('')
     letters.each do |letter|
        if hash.include?(letter)
          hash[letter] += 1 
        else
          hash[letter] = 1
        end

     end
  hash

end

letter_count('moon') => {"m"=>1, "o"=>2, "n"=>1}

当我试图将其缩短为

def letter_count(str)

     hash = {}
     letters = str.gsub(' ','').split('')
     letters.each do |letter|
        hash[letter] += 1 if hash.include?(letter)
        hash[letter] = 1

     end
  hash

end

答案变成了:

letter_count('moon') => {"m"=>1, "o"=>1, "n"=>1}
你可以告诉我我错了吗???谢谢!!!!!!!

6 个答案:

答案 0 :(得分:4)

我的个人实施(只是为了好玩):

str = "moon"
counts = Hash[ str.scan(/\S/).group_by(&:chr).map{ |c,a| [c,a.length] } ]
#=> {"m"=>1, "o"=>2, "n"=>1} 

答案 1 :(得分:3)

始终执行您设置hash[letter] = 1的行。这就是为什么你的哈希值每个字母的值为1.你可以使用三元运算符将它变为一行,如下所示:

letters.each do |letter|
    hash.include?(letter) ? hash[letter] += 1 : hash[letter] = 1
end

这是一个if,else then语句。如果散列将字母作为键,则值递增,否则散列将字母存储为键,并将“1”指定为其值。

一个有用的链接:http://alvinalexander.com/blog/post/ruby/examples-ruby-ternary-operator-true-false-syntax

答案 2 :(得分:3)

这个怎么样?

def letter_count(str)
  str.gsub(' ','').split('').each_with_object(Hash.new(0)) do |letter, hash|
    hash[letter] += 1
  end
end

letter_count('moon') # => {"m"=>1, "o"=>2, "n"=>1}

稍作改进:使用scan代替gsub + split

def letter_count(str)
  str.scan(/[^\s]/).each_with_object(Hash.new(0)) do |letter, hash|
    hash[letter] += 1
  end
end

letter_count('sun and stars') # => {"s"=>3, "u"=>1, "n"=>2, "a"=>2, "d"=>1, "t"=>1, "r"=>1}

答案 3 :(得分:2)

这个特殊问题是选择正确算法的一个很好的例子,但更重要的是,正确的数据结构可以大规模简化解决方案。事实上,在这种特殊情况下,选择正确的数据结构会使算法变得微不足道,以至于它基本上完全消失了:数据结构已经 答案。

我所说的数据结构是MultisetMultiset就像Set,除了它不存储唯一的项目,而是可以添加多个项目时间和MultiSet将统计项目的添加频率 - 这正是您想要的。基本上,Set会告诉您特定项目是否在Set Multiset此外还会告诉您多久一次该特定项目位于Multiset

不幸的是,Ruby核心库或标准库中没有Multiset实现,但有is a gem

整个代码只是:

def letter_count(str)
  Multiset[*str.chars]
end

letter_count('moon')
# => #<Multiset:#1 "m", #2 "o", #1 "n">

是的,就是这样。这就是整个代码。如您所见,有一次出现'm'字母,两次出现字母'o',另一次出现字母'n'

答案 4 :(得分:1)

hash[letter] = 1将始终执行。您可以像这样重构代码:

letters.each do |letter|
    hash[letter] = 0 unless hash[letter]
    hash[letter] += 1
end

或没有后缀条件:

letters.each do |letter|
    if hash[letter]
      hash[letter] += 1
    else
      hash[letter] = 1 
    end
end

答案 5 :(得分:0)

如前所述,问题是因为hash[letter] = 1总是被执行。

为了使你的代码更短,你可以通过将默认值传递给你的哈希构造函数来完全摆脱if

hash = Hash.new(0)
letters.each { |letter| hash[letter] += 1 }