kiyokaのブログアーカイブ

Archive of old blog posts

Ruby標準ライブラリを使って]のライブラリ実装をサボりまくりな話

私がRubyで書いているLisp方言、 [Nendo]の開発状況続き。

RubyでLisp処理系を作ると楽できる

例えば、sort関数。 仕様は『引数にリストを取って、ソート済みの新しいリストを返す』というもの。

nendoが動いている様子。

$ ./nendo 
nendo> (sort '(1 5 6 3 4))
(1 3 4 5 6 )

Rubyでは

$ irb
>> *1, 5, 6, 3, 4*.sort
=> *1, 3, 4, 5, 6*

さて、nendoの実装はというと

  def getIFunc( name )
    case name
      .
      
      .
    when 'sort'
      lambda {|arg|     arg.to_arr.sort.to_list }
      .
      
      .
    end
  end

となっている。 引数argはconsセル用の ‘Cellクラス’ の連結リストで、一旦RubyのArrayに変換することでRuby標準のsort関数を使っている。

上の様に短く書けるのはCellクラスにイテレータを実装したことと、Arrayクラスにto_listメソッドを追加したため。 Cellクラスのキモの部分

class Cell
  include Enumerable

  def initialize( car = Nil.new, cdr = Nil.new )
    @car = car
    @cdr = cdr
  end
  attr_accessor :car, :cdr
    .
    
    .
  def each                    # Supporting iterator
    it = self
    while Nil != it.class
      yield it
      it = it.cdr
    end
  end

  def to_arr
    self.map {|x| x.car}
  end
end

こちらは、動的にArrayクラスにto_listを追加するコード

class Array
  def to_list
    cells = self.map { |x|
      Cell.new( x )
    }
    ptr = cells.pop
    cells.reverse.each { |x|
      x.cdr = ptr
      ptr = x
    }
    return ptr
  end
end

sortだけでなくreverseとuniqも同じ

一旦Arrayに変換するだけで良い関数はsortと同様に書ける

  def getIFunc( name )
    case name
      .
      .
    when 'sort'
      lambda {|arg|     arg.to_arr.sort.to_list }
    when 'reverse'
      lambda {|arg|     arg.to_arr.reverse.to_list }
    when 'uniq'
      lambda {|arg|     arg.to_arr.uniq.to_list }
      .
      .
    end
  end

sortに評価関数を指定したい場合は?

ウーン… どうしようかな… うまくこの仕組みにのっかって行けるかどうか微妙。 その時はsort関数をnendoで実装しなおさないといけないなぁ。 reverseはそのままでいいかも。