Word 解析器脚本和实现记忆

Word parser script and implementing memoization

描述

给定一个字典,我的程序生成两个输出文件,'sequences.txt' 和 'words.txt'。

例如,给定的 spec/fixtures/sample_words.txt 字典仅包含

arrows
carrots
give
me

输出应该是:

'sequences'             'words'

carr                    carrots
give                    give
rots                    carrots
rows                    arrows
rrot                    carrots
rrow                    arrows

当然,'arro' 不会出现在输出中,因为它是 发现不止一个字。

到目前为止我想出了什么

项目结构:

├── Gemfile
├── Gemfile.lock
├── examples
│   └── dictionary.txt
├── lib
│   └── word_sequence_parser.rb
├── main.rb
├── output
├── readme.md
└── spec
    ├── fixtures
    │   └── sample_words.txt
    └── word_sequence_parser_spec.rb

到运行脚本: ruby main.rb examples/dictionary.txt

main.rb

require_relative 'lib/word_sequence_parser.rb'

dict_path = ARGV.shift

if dict_path.nil?
  dict_path = 'spec/fixtures/sample_words.txt'
end

parser = WordSequenceParser.new(dict_path)

# step 1 - Opens dictionary file and generates a new set of words
parser.set

# step 2 - Parses word sequences
parser.sequence

# step 3 - Prints to files in ./output
parser.dump_text

有效的脚本

word_sequence_parser.rb

require 'set'

class WordSequenceParser

  def initialize(path)
    @path = path
  end

  def set
    set = Set.new

    File.open(@path) do |f|
      f.each_line do |line|
        set.add(line.chomp.downcase)
      end
    end
    set
  end

  def sequence
    sequences = Set.new
    words = Set.new
    to_remove = Set.new

    set.each do |w|
      letters = w.split(//)
      letters.each_cons(4) do |seq|
        s = seq.join
        if !words.add?(s)
          to_remove.add(s)
        end
        sequences.add( {seq: s, word: w} )
      end
    end
    sequences.delete_if { |hash| to_remove.include?(hash[:seq]) }
  end

  def dump_text
    output_s = File.open( 'output/sequences.txt', 'w' )
    output_w = File.open( 'output/words.txt', 'w' )

    sequence.each do |hash|
      output_s.puts("#{hash[:seq]}")
      output_w.puts("#{hash[:word]}")
    end

    output_s.close
    output_w.close
  end
end

我对剧本的记忆不起作用

require 'set'

class WordSequenceParser

  def initialize(path)
    @path = path
  end

  def set
    set = Set.new

    File.open(@path) do |f|
      f.each_line do |line|
        set.add(line.chomp.downcase)
      end
    end
    set
  end

  def memoize
    @set = set
  end

  def sequence
    sequences = Set.new
    words = Set.new
    to_remove = Set.new

    @set.each do |w|
      letters = w.split(//)
      letters.each_cons(4) do |seq|
        s = seq.join
        if !words.add?(s)
          to_remove.add(s)
        end
        sequences.add( {seq: s, word: w} )
      end
    end
    sequences.delete_if { |hash| to_remove.include?(hash[:seq]) }
  end

  def dump_text
    output_s = File.open( 'output/sequences.txt', 'w' )
    output_w = File.open( 'output/words.txt', 'w' )

    sequence.each do |hash|
      output_s.puts("#{hash[:seq]}")
      output_w.puts("#{hash[:word]}")
    end

    output_s.close
    output_w.close
  end
end

我在尝试 运行 脚本时收到此错误消息。

../word_sequence_parser.rb:29:in `sequence': undefined method `each'     for nil:NilClass (NoMethodError)
    from main.rb:15:in `<main>'

我已经阅读了 Justin Weiss' article on memoization 并且大部分都明白了。只是很难将这种技术应用到我已经写过的东西中。

它不起作用,因为您从不调用 memoize,所以 @set 永远不会被初始化。

然而这里真正的问题是,没有什么可以记忆的。

您的原始代码看起来很不错,如果您考虑它是如何工作的,没有任何代码的冗余执行。每行执行一次,或者如果多次执行,returns 不同的值。

因此记忆没有意义。

假设您想多次调用 dump_text(或只是序列),那么您肯定希望按如下方式记忆序列:

def sequence
  @sequence ||= begin
    sequences = Set.new
    words = Set.new
    to_remove = Set.new

    set.each do |w|
      letters = w.split(//)
      letters.each_cons(4) do |seq|
        s = seq.join
        if !words.add?(s)
          to_remove.add(s)
        end
        sequences.add( {seq: s, word: w} )
      end
    end
    sequences.delete_if { |hash| to_remove.include?(hash[:seq]) }
  end
end

这只会执行一次你原来的序列计算代码,然后分配@sequence。对@sequence 的每一次调用都会重用已经计算出的@sequence 的值(因为它现在不是零。)

我喜欢这个问题,因为这是 第一件事 我记得我的公司开始使用 ruby。我们有一位顾问重做了很多旧的 asp.net 代码,他在方法中有这些 @foo ||= ... 表达式,这是我以前从未见过的。