世界線航跡蔵

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

2006年04月29日

String#succ問題への回答

String#succで提起されている問題はかなり面白い。これをどう捉えるかが、数学屋と計算機屋で分かれそうだ。

Ruby的にスマートそうな回答はwalf443さんが提出済み。succに限らない一般化バージョンもある。

でも、私の目には、モノが"succ"だけにこれは関数合成の問題に見える。"str".succ.succ(succ ∘ succ)("str")に見えるのね。今『圏論の基礎』に再挑戦してるからsucc succ "str"と書こうか。

数学屋-計算機屋と書いたけれど、これは関数型言語系と手続き型言語系で分かれるといった方が正しいかもしれない。このあたり、関数型言語なら多変数/多価関数も含めてスマートに書けるんだろう。Haskellなんかsuccはまんまsucc (succ "str")だもんね。

そういう視点からこの問題を捉えると、実際この手のメソッドチェーンはレシーバーを出発点とした射の合成な訳だよね。ここで、ActiveSupportに入ったSymbol#to_procを思い出す(くまくま参照)。あんな感じで、シンボルを元に射の合成を表現できたらいいなぁ、と。

実装

で、書いてみました。

class Symbol
  unless instance_methods.include?("to_proc")
    def to_proc
      Proc.new{|obj, *args| obj.__send__(self, *args)}
    end
  end

  def * rhs
    MethodComposition.new([self.to_proc, rhs.to_proc])
  end

  def ** num
    MethodComposition.new(Array.new(num, self.to_proc))
  end
end

class MethodComposition
  def initialize(elements = [])
    @chain = elements
  end

  def * rhs
    MethodComposition.new(@chain.dup.push(rhs.to_proc))
  end

  def ** num
    MethodComposition.new(@chain * num)
  end

  def to_proc
    Proc.new{|receiver|
      @chain.inject(receiver) {|obj, proc| proc.call(obj) }
    }
  end
end

class Object
  def callcomp(proc = nil)
    unless proc
      self
    else
      proc.to_proc.call self
    end
  end
end

使いかた

例えば、

"b8".callcomp(:succ ** 5 * :succ * :succ ** 3) # => "c7"
1.callcomp(:succ ** 3 * :to_s)                 # => "4"

[64, 65, 66].map(&(:succ * :chr * :to_sym))    # => [:A, :B, :C]

感想

  • 効率は無視してProcオブジェクト化して格納
  • 関数合成だから、演算子は乗算と冪乗算を借りた
  • あと、Symbolに乗算を定義する意味は普通無さそうなので衝突しなそう
  • 名前がObject#callcompなのはcallccからの連想だけれど、もっとまともな名前はないものかと自分でも思ってる
  • というか、succを数回程度ならかえって複雑にしてるだけ
  • でも、mapとかに適用すると少しだけありがた味が出てくるかも
  • 1変数(というかself)で1価の関数しか扱えないのがちょっと難かなぁ。

トラックバック

http://yugui.jp/articles/418/ping
方向を間違えた件 (ratio - rational - irrational)
合成の方向を間違えて実装したので修正

コメント

blog comments powered by Disqus

ご案内

前の記事
次の記事

タグ一覧

過去ログ

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

フィード

フィードとは

その他

Powered by "rhianolethe" the blog system