Ruby扫描匿名函数说明

时间:2012-02-21 16:00:28

标签: ruby syntax anonymous-function

我是Ruby的新手,并且正在努力理解我编写的这段代码中发生了什么。为什么我必须声明两个变量|x,y|来获得我期望的输出?我只使用xy似乎总是nil。但是当我更改为|x|时,我的字数始终为0(请参阅下面的代码和输出)。感谢您提供的任何见解。

def count_words(string)
  string.downcase!

  wordhash = Hash.new

  # what is going on here?  
  # Why do I have to have two 
  # variables in the scan block?
  string.scan(/(\b\w+\b)/){|x,y|
    wordhash.store(x,string.scan(/\b#{x}\b/).length)}

  return wordhash
end

puts count_words("Hello there.  This is bob bob bob")

# Correct Output with |x,y|:
# {"hello"=>1, "there"=>1, "this"=>1, "is"=>1, "bob"=>3}

# Incorrect Output with |x|:
# {["hello"]=>0, ["there"]=>0, ["this"]=>0, ["is"]=>0, ["bob"]=>0}

2 个答案:

答案 0 :(得分:3)

来自String#scan文档:

  

如果模式包含组,则每个结果本身都是一个   每个组包含一个条目的数组。

由于您的模式包含一个组,因此第一个块参数是一个数组。如果您使用|x, y|,则会对数组进行解构并将其第一个元素指定给x

顺便说一句,要获得字数统计,你可以这样做:

s = "this is a test string it is"
Hash[s.split.group_by{ |e| e }.map { |k,v| [k, v.size] }] 
#=> {"this"=>1, "is"=>2, "a"=>1, "test"=>1, "string"=>1, "it"=>1}

答案 1 :(得分:2)

另一个答案正确地解释了为什么这不能按预期工作。让我试着指出代码中的一些问题:

  • string.downcase!修改赋予函数的参数,这是非常糟糕的样式
  • /(\b\w+\b)/您在此处不需要其他匹配组,只需使用/\b\w+\b/即可。这样您就可以使用scan(...) do |x|,其中x将是匹配的字词
  • wordhash.store(x,y)可以简单地写成wordhash[x] = y
  • string.scan(/\b#{x}\b/).length你第二次扫描字符串,虽然没有必要。相反,您可以为给定单词的每个匹配增加一个计数器。

示例:

def count_words(string)
  # set up a hash that accumulates the number of occurrences per word
  wordcount = Hash.new(0)
  string.downcase.scan(/\b\w+\b/) { |word| wordcount[word] += 1 }
  # no need to use return here, the function already evaluates to the last
  # value
  wordcount
end

p count_words("Hello there.  This is bob bob bob")
# => {"hello"=>1, "there"=>1, "this"=>1, "is"=>1, "bob"=>3}

这只是为了演示你的方法是如何工作的,在Ruby中你可能会以更实用的方式解决这个问题,最好使用group_by,正如迈克尔已经证明或使用inject

string.downcase.split.inject(Hash.new(0)) { |h,word| h[word] += 1; h }