Объяснение анонимной функции сканирования Ruby - PullRequest
0 голосов
/ 21 февраля 2012

Я новичок в Ruby и пытаюсь понять, что происходит в этом фрагменте кода, который я написал.Почему я должен объявить две переменные |x,y|, чтобы получить ожидаемый результат?Я использую только x, а y всегда отображается как 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 ]

3 голосов
/ 21 февраля 2012

Из документации 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}
2 голосов
/ 21 февраля 2012

Другой ответ правильно объясняет, почему это не работает так, как ожидалось.Позвольте мне попытаться указать на еще некоторые проблемы с вашим кодом:

  • 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 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...