while (token = lexer.read) != EOFをEnumeratorをつかってどうにかしたい

あたらしく研究室に入って(2度目)スクリプト言語の作り方って本が課題なのでやっていまして、そのときlexer作るんですが

while (token = lexer.read) != EOF
  ...
end

的な書き方してて嫌な気持ちになったので他にまともな書き方がないか調べました.

どうしたいか

  • while とかの中で代入はすきじゃないのでしたくない
  • eachみたいにつかえる
  • けど,任意の位置でreadできるようにしたい
  • しかしメモリ使用量を抑えたいので一気に配列に突っ込むとかはしたくない

つまり下みたいにはしたくない

# ファイルひらいて一気に配列に突っ込む
file = open(file_name).map { |f| f.strip }
file.each do |f|
  ...
end

どうしたか

Enumerator classを使いました.

例として以下の様なこと Enumeratorを使ってやってみました.

ファイルから1行ずつ読んでくる->読んできた1行になにかする(行番号をつける). javaLineNumberReaderみたいなやつ

class LineNumberReader
  def initialize(filename)
    @filename = filename
  end

  # nextがなかったらStopIterationをraiseする
  def read_line
    file.next
  end

  def peek_line
    file.peek
  end

  def has_next_line?
    !!file.peek
  rescue StopIteration
    false
  end

  # loopはStopIterationをrescueする
  def each
    loop { yield(read_line) }
  end

  private

  # each_line, with_indexは enumeratorを返すのでnextとかで逐次に取ってくるはず
  def file
    @file ||= open(@filename).each_line.with_index
  end
end

LineNumberReader#fileEnumeratorを返すようにしているので[メモリ使用量を抑えたいので一気に配列に突っ込むとかはしたくない]は解決出来てるはず

またLineNumberReader#read_lineを外から呼ぶようにすることで[任意の位置でreadできるようにしたい]も解決できる

reader = LineNumberReader.new('file.rb')
p reader3.read_line
...
p reader3.read_line if reader3.has_next_line?

またLineNumberReader#eachを作ってることで[eachみたいにつかえる]も解決

reader = LineNumberReader.new('file.rb')
reader.each |f|
  ...
end

while使いたいときはloopを使うと(実際eachで解消されてるけど)[while とかの中で代入はすきじゃないのでしたくない]も解決

reader = LineNumberReader.new('file.rb')
loop
  p reader.read_line
end

まとめ

ただのiteratorっぽいなにかだし,こんなことしてないで早く課題終わらせたほうが良さそう もっと賢い方法があればだれか教えてくれ!!

2週間でできる! スクリプト言語の作り方 (Software Design plus)

2週間でできる! スクリプト言語の作り方 (Software Design plus)