字符串中字母的频率

时间:2014-05-03 01:40:48

标签: ruby hash iterator each

当试图找到“奇妙”字母的频率时,我无法理解给定的解决方案:

def letter_count(str)
  counts = {}

  str.each_char do |char|
    next if char == " "
    counts[char] = 0 unless counts.include?(char)
    counts[char] += 1
  end

  counts
end

我尝试解构它,当我创建下面的代码时,我希望它能完成同样的事情。然而,它给了我不同的结果。

blah = {}
x = 'fantastic'
    x.each_char do |char|
        next if char == " "
        blah[char] = 0 
            unless 
            blah.include?(char)
            blah[char] += 1
    end
blah
end

第一段代码给出了以下内容

puts letter_count('fantastic')
>
{"f"=>1, "a"=>2, "n"=>1, "t"=>2, "s"=>1, "i"=>1, "c"=>1}

为什么第二段代码会给我

puts blah
>
{"f"=>0, "a"=>0, "n"=>0, "t"=>0, "s"=>0, "i"=>0, "c"=>0}

有人可以分解代码并告诉我底层的区别是什么。我想一旦我理解了这一点,我就能真正理解第一段代码。另外,如果你想解释一下第一段代码,以帮助我,那也很棒。

2 个答案:

答案 0 :(得分:2)

你不能拆分这条线......

counts[char] = 0 unless counts.include?(char)

...按照你做的方式多行。尾随条件仅适用于一行。

如果您想将其拆分为多行,则必须转换为传统的if / end(在本例中为unless / end)格式。

unless counts.include?(char)
  counts[char] = 0
end

以下是代码的解释......

# we define a method letter_count that accepts one argument str
def letter_count(str)

  # we create an empty hash 
  counts = {}

  # we loop through all the characters in the string... we will refer to each character as char
  str.each_char do |char|

    # we skip blank characters (we go and process the next character)
    next if char == " "

    # if there is no hash entry for the current character we initialis the 
    # count for that character to zero
    counts[char] = 0 unless counts.include?(char)

    # we increase the count for the current character by 1
    counts[char] += 1

  # we end the each_char loop
  end

  # we make sure the hash of counts is returned at the end of this method 
  counts

# end of the method
end

答案 1 :(得分:2)

既然@Steve已经回答了你的问题并且你接受了他的答案,也许我可以建议另一种方法来计算这些字母。这只是可以采取的众多方法之一。

<强>代码

def letter_count(str)
  str.downcase.each_char.with_object({}) { |c,h|
    (h[c] = h.fetch(c,0) + 1) if c =~ /[a-z]/ }
end

示例

letter_count('Fantastic')
  #=> {"f"=>1, "a"=>2, "n"=>1, "t"=>2, "s"=>1, "i"=>1, "c"=>1}

<强>解释

以下是发生的事情。

str = 'Fantastic'

我们使用String#downcase,例如,为了计算,'f''F'被视为相同的字符。 (如果您不想这样,只需删除.downcase。)让

s = str.downcase #=> "fantastic"

s.each_char.with_object({}) { |c,h| (h[c] = h.fetch(c,0) + 1) c =~ /[a-z]/ }

枚举器String#each_char被链接到Enumerator#with_index。这将创建一个复合枚举器:

enum = s.each_char.with_object({})
  #=> #<Enumerator: #<Enumerator: "fantastic":each_char>:with_object({})>

我们可以通过将枚举器转换为数组来查看枚举器将传递给块的内容:

enum.to_a
  #=> [["f", {}], ["a", {}], ["n", {}], ["t", {}], ["a", {}],
  #    ["s", {}], ["t", {}], ["i", {}], ["c", {}]]

(实际上,它只传递带有'f'的空哈希;此后它传递更新的哈希值。)枚举器with_object创建一个由块变量{{1}表示的空哈希}。

传递给块的第一个元素h是字符串enum。块变量'f'被赋值为该值,因此块中的表达式为:

c

评估为:

(h[c] = h.fetch(c,0) + 1) if c =~ /[a-z]/

现在

(h['f'] = h.fetch('f',0) + 1) if 'f' =~ /[a-z]/ 
当且仅当c =~ /[a-z]/ 是小写字母时,

才是true。这里

c

所以我们评估表达式

'f' =~ /[a-z]/ #=> true
如果h[c] = h.fetch(c,0) + 1 有密钥h.fetch(c,0),则

h[c]会返回h;否则它返回Hash#fetch的第二个参数的值,此处为零。 (c也可以阻止。)

由于fetch现在为空,它变为

h

然后,枚举器h['f'] = 0 + 1 #=> 1 会将each_char'a''n'传递给该块,从而导致哈希变为

't'

传入的下一个字符是第二个h = {'f'=>1, 'a'=>1, 'n'=>1, 't'=>1 } 。由于'a'已有密钥h

'a'

评估为

h[c] = h.fetch(c,0) + 1

字符串的其余部分以相同的方式处理。