世界線航跡蔵

Mad web programmerのYuguiが技術ネタや日々のあれこれをお送りします。

2008年05月16日

sortコマンドと無名関数の合成

sort コマンドの実装(その2を読んで、単なる「引数に対する操作を表すオブジェクト」のためにクラスを定義する意味が分かんね、と思っていろいろ書いてみたらこうなった。

require 'optparse'

module Ordering
  def cartesian(rhs)
    proc {|(a0,*ar), (b0,*br)|
      result = self.call(a0, b0)
      result == 0 ? rhs.call(ar, br) : result
    }.extend(Ordering)
  end
  def composite(rhs)
    proc {|a,b| self.call(rhs.call(a), rhs.call(b)) }.extend(Ordering)
  end
  def scalar_prod(rhs)
    case rhs
    when -1 then -self
    when  0 then NULL
    when  1 then self
    else raise ArgumentError, "must be -1, 0, or 1"
    end
  end
  def -@
    proc {|a,b| -self.call(a,b)}.extend(Ordering)
  end
  def *(rhs)
    case 
    when rhs.respond_to?(:call)
      case rhs.arity
      when 1 then self.composite(rhs)
      when 2 then self.cartesian(rhs)
      end
    when -1, 0, 1 
      scalar_prod(rhs)
    else
      raise TypeError
    end
  end
  def coerce(lhs)
    case lhs
    when -1, 0, 1 then [self, lhs]
    else super
    end
  end

  DEFAULT = proc{|a,b| a <=> b}.extend(Ordering).freeze
  ennumeric = proc{|x| /^(\d*|\s+\d+)(.*)$/ =~ x; [$1.to_i, $2]}
  NUMERIC = (DEFAULT*DEFAULT*ennumeric).freeze
  NULL = proc{|a,b| 0}.extend(Ordering).freeze
end

sort = Ordering::DEFAULT
reverse = 1

opt = OptionParser.new
opt.on("-n", "--numeric-sort") {|v| sort = Ordering::NUMERIC }
opt.on("-r", "--reverse") {|v| reverse = -1}
opt.parse!(ARGV)

ARGF.sort(&reverse*sort).each{|v| print v}

結局、sort(1)だのyes(1)だの、uniq(1)だの、この手のUnixのUnixたるところを示す典型的フィルタコマンドは関数型言語のほうが綺麗に書けるんだろうな。Rubyだといまいちぱっとしない。むやみにProcにメソッド追加するのもアレだし、かといってlambda相当物はProc以外にないので苦労している。それでも、こんな風にソート処理を分解しておけば、あとから-f (--ignore-case)オプションを追加しようと思ったとしても、proc{|x| x.downcase}を掛けるだけだから拡張が簡単である。

一応、上のコードは、ソート関数を「要は全順序だろ」と思って係数体が{-1, 0, 1}のベクトル空間と見なすやりかただな。えっと比較対象の行に対する逆元をうまく定義できるとすれば、線形性は満たすよな。ただ、辞書式順序をcartesianと名付けたのはちょっと不正確か? 線形写像のカルテシアン積の、その{-1, 0, 1}への線形な写像だよな。

久々にcoerce規約を実装して楽しかった。でも、これHaskellならずっと綺麗に書けるだろう。むー。

トラックバック

http://yugui.jp/articles/774/ping

現在のところトラックバックはありません

コメント

blog comments powered by Disqus

ご案内

前の記事
次の記事

タグ一覧

過去ログ

  1. 2016年07月
  2. 2016年01月
  3. 2015年09月
  4. 2015年08月
  5. 過去ログ一覧

フィード

フィードとは

その他

Powered by "rhianolethe" the blog system