Как рассчитать максимальную частоту слова в Ruby - PullRequest
0 голосов
/ 07 марта 2020

Я работал над этим заданием для курса Coursera Intro to Rails. Нам было поручено написать программу, которая рассчитывает максимальную частоту слов в текстовом файле. Нам было поручено создать метод, который:

  1. Рассчитывает максимальное количество раз, когда одно слово появляется в данном контенте и сохраняется в highest_wf_count.
  2. . Идентифицируйте слова, которые использовались максимальное количество раз и сохраняют это в highest_wf_words.

Когда я запускаю предоставленные нам тесты rspe c, один тест не пройден. Я напечатал вывод, чтобы увидеть, в чем проблема, но не смог ее исправить.

Вот мой код, тест rspe c и что я получаю:

class LineAnalyzer

  attr_accessor :highest_wf_count
  attr_accessor :highest_wf_words
  attr_accessor :content
  attr_accessor :line_number

  def initialize(content, line_number)
    @content = content
    @line_number = line_number
    @highest_wf_count = 0
    @highest_wf_words = highest_wf_words
    calculate_word_frequency
  end
  def calculate_word_frequency()
    @highest_wf_words = Hash.new(0)
    @content.split.each do |word|
      @highest_wf_words[word.downcase!] += 1
      if @highest_wf_words.has_key?(word)
        @highest_wf_words[word] += 1 
      else
        @highest_wf_words[word] = 1
      end
      @highest_wf_words.sort_by{|word, count| count}
      @highest_wf_count = @highest_wf_words.max_by {|word, count| count}
    end
  end
  def highest_wf_count()
    p @highest_wf_count
  end
end

Это rspe c код:

require 'rspec'

describe LineAnalyzer do
  subject(:lineAnalyzer) { LineAnalyzer.new("test", 1) }

  it "has accessor for highest_wf_count" do
    is_expected.to respond_to(:highest_wf_count) 
  end 
  it "has accessor for highest_wf_words" do
    is_expected.to respond_to(:highest_wf_words) 
  end
  it "has accessor for content" do
    is_expected.to respond_to(:content) 
  end
  it "has accessor for line_number" do
    is_expected.to respond_to(:line_number) 
  end
  it "has method calculate_word_frequency" do
    is_expected.to respond_to(:calculate_word_frequency) 
  end
  context "attributes and values" do
  it "has attributes content and line_number" do
    is_expected.to have_attributes(content: "test", line_number: 1) 
  end
  it "content attribute should have value \"test\"" do
    expect(lineAnalyzer.content).to eq("test")
  end
  it "line_number attribute should have value 1" do
    expect(lineAnalyzer.line_number).to eq(1)
  end
end

  it "calls calculate_word_frequency when created" do
    expect_any_instance_of(LineAnalyzer).to receive(:calculate_word_frequency)
    LineAnalyzer.new("", 1) 
  end

  context "#calculate_word_frequency" do
    subject(:lineAnalyzer) { LineAnalyzer.new("This is a really really really cool cool you you you", 2) }

    it "highest_wf_count value is 3" do
      expect(lineAnalyzer.highest_wf_count).to eq(3)
    end
    it "highest_wf_words will include \"really\" and \"you\"" do
      expect(lineAnalyzer.highest_wf_words).to include 'really', 'you'
    end
    it "content attribute will have value \"This is a really really really cool cool you you you\"" do
      expect(lineAnalyzer.content).to eq("This is a really really really cool cool you you you")
    end
    it "line_number attribute will have value 2" do
      expect(lineAnalyzer.line_number).to eq(2)
    end
  end
end

Это rspe c вывод:

13 examples, 1 failure

Failed examples:

rspec ./course01/module02/assignment-Calc-Max-Word-Freq/spec/line_analyzer_spec.rb:42 # LineAnalyzer#calculate_word_frequency highest_wf_count value is 3

Мой вывод:

#<LineAnalyzer:0x00007fc7f9018858 @content="This is a really really really cool cool you you you", @line_number=2, @highest_wf_count=[nil, 10], @highest_wf_words={"this"=>2, nil=>10, "is"=>1, "a"=>1, "really"=>3, "cool"=>2, "you"=>3}>
  1. Исходя из тестовой строки, количество слов неверно.
  2. "ноль" включается в га sh.
  3. Ха sh не указывается отсортировано по значению (количеству), как и должно быть.

Я пробовал несколько вещей, чтобы исправить эти проблемы, и ничего не помогло. Я снова просмотрел материал лекции, но не могу найти ничего, что могло бы помочь, и на форумах не часто следят за вопросами от студентов.

Ответы [ 2 ]

1 голос
/ 07 марта 2020

В соответствии с Ruby документацией :

downcase! (* Args) publi c

Уменьшает содержание str, возвращая ноль, если никаких изменений не было сделано.

Из-за этого неожиданного поведения метода .downcase!, если слово уже все в нижнем регистре, вы увеличиваете вхождения nil в этой строке:

@highest_wf_words[word.downcase!] += 1

Тесты также не выполняются, поскольку @highest_wf_words.max_by {|word, count| count} возвращает массив, содержащий счетчик и слово, в то время как мы хотим получить только счетчик.

Упрощенный метод calculate_word_frequency, передающий тесты, будет выглядеть так это:

  def calculate_word_frequency()
    @highest_wf_words = Hash.new(0)

    @content.split.each do |word|
      # we don't have to check if the word existed before
      # because we set 0 as default value in @highest_wf_words hash

      # use .downcase instead of .downcase!
      @highest_wf_words[word.downcase] += 1

      # extract only the count, and then get the max
      @highest_wf_count = @highest_wf_words.map {|word, count| count}.max
    end
  end
1 голос
/ 07 марта 2020

Ноль :

Ноль из downcase!

Это модифицирует строку и возвращает nil, если ничего не изменилось. Если вы говорите «это странно», то вы правы (ИМХО).

# just use the non destructive variant
word.downcase

Сортировка :

sort_by возвращает новый объект (Ха sh, Array, ...) и не изменяет приемник метода. Вам нужно либо переназначить, либо использовать sort_by!

unsorted = [3, 1, 2]
sorted = unsorted.sort

p unsorted # => [3, 1, 2]
p sorted # => [1, 2, 3]

unsorted.sort!
p unsorted # => [1, 2, 3]

Количество неверных слов :

После того, как вы исправили эти две ошибки, он должен выглядеть лучше. Помните, что метод возвращает не одно целое число, а массив из двух элементов со словом и счетчиком, поэтому он должен выглядеть примерно так: ["really", 6]

Упрощение вещей:

Если вы можете использовать ruby 2.7, то есть удобный метод Enumerable#tally!

%w(foo foo bar foo baz foo).tally
=> {"foo"=>4, "bar"=>1, "baz"=>1}

Пример взят из https://medium.com/@baweaver / ruby -2- 7-перечислим-Талли-a706a5fb11ea

...