【问题标题】:Word parser script and implementing memoization单词解析器脚本和实现记忆
【发布时间】:2023-04-06 11:05:01
【问题描述】:

说明

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

  • 'sequences' 包含四个字母 (A-z) 的每个序列,它们恰好出现在字典的一个单词中,每行一个序列。
  • 'words' 将包含包含序列的相应单词,顺序相同,每行一个。

例如,给定的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

ma​​in.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 并且大部分都明白了。只是很难将这种技术应用到我已经写过的东西中。

【问题讨论】:

    标签: ruby dictionary memoization


    【解决方案1】:

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

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

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

    因此,记忆没有任何意义。

    假设您想多次调用 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 ||= ... 表达式,这是我以前从未见过的。

    【讨论】:

    • 我最初的想法是我会将这组单词存储在内存中,因为它是静态的并且不会改变。这样脚本可以根据需要引用它,但我没有意识到我在这个特定的脚本中只需要它一次。感谢学习!
    • 如果我想将 Rails 框架添加到其中,以便我可以为它处理 UI 层,那么最好的方法是什么?我只有使用rails new app_name 从头开始​​创建 Rails 应用程序的经验。
    • 这是一个大问题。 :-) 如果你想玩 react.rb(这对于一个简单的 UI 来说是完美的)请点击gitter.im/zetachang/react.rb,我们可以讨论......
    猜你喜欢
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-24
    相关资源
    最近更新 更多