トップ «前の日記(2008-07-25) 最新 次の日記(2008-07-30)» 編集

Route 477



2008-07-29

[misc] 寝不足でだるい日でも

夕方になるとだんだん元気になってくるな。

どういうメカニズムなんだろう。

[scheme][ruby] call/ccの7つの典型的な使い方

わだばさん(でいいのかな?)のところで紹介されていたCall with Current Continuation Patternsをちょっと見てみた。

それによると、callccの典型的な使い方には以下のようなものがあるらしい。

  1. (無限)ループからの脱出
  2. 再帰からの脱出
  3. Cでいうcontinue
  4. 脱出+再入
  5. コルーチン
  6. non-blindな(??)バックトラッキング
  7. マルチタスク

で、だ。

1, 2, 3は、breakとかcontinueがある言語なら普通にできる。 5や4の一部は、PythonのジェネレータとかRuby1.9のFiberでできる。 7はコルーチンやスレッドを使えばできる。

となるとcallccでしか出来ないことって(この中では)再入とバックトラックしか残らんのだよなぁ。 しかもバックトラックはHaskellだとリストモナドで綺麗に書けたりするし、見た目はカッコいいけど普通にループ回すのより効率が良いわけでもないし。

なんとなく「マクロの機能の8割は他の言語でも可能になりつつある」という話(あ、しまった、まだ書いてないや)とオーバーラップした。

それでも「できるだけシンプル、かつ強力」を志すSchemeとしてはcall/ccの代わりにbreakを導入するとかはまずあり得ないし(それこそcall/ccとマクロでできるよ)、 最後に残った「call/ccでしかできない仕事」はSchemeの力として残りつづける。

ただそれ以外の言語(というか、Ruby)にこれからもcallccが必要かと言われると、説得力のある答えを提示できないのが正直なところだ。 まぁContinuationクラスを無くしてほしくない人は、「再入をうまく使うパターンを考えろ」というのがヒントになるかもね。

[ruby] Re: Pythonのデコレータが面白いのでRubyで実装してみた

んー、まぁ一応動いてるんだけど、アノテートみたいにメソッド定義の前に付けられないのがなぁ・・・。

[いい天気 - ずっと君のターンより引用]

「private」みたいにメソッドの直前に付けるのはどうだろ?ということで実装してみた。

Point = Struct.new(:x, :y)

class Point
  extend DeclareArgsDecorator

  declare_args(Point)
  def distance_to(other)
    Math.sqrt( (x - other.x)**2 + (y - other.y)**2 )
  end
end

center = Point.new(0.0, 0.0)
puts center.distance_to(Point.new(3.14, 1.592))
puts center.distance_to([2.0, 4])

実行結果。

/Users/yhara/proj/misc/ruby_decorator % ruby foo.ugoku.rb 
3.52052041607487
foo.ugoku.rb:10:in `check_args': 引数の型がちがうお (PointじゃなくてArrayになってる) (ArgumentError)
        from foo.ugoku.rb:8:in `each'
        from foo.ugoku.rb:8:in `check_args'
        from foo.ugoku.rb:29:in `distance_to'
        from foo.ugoku.rb:50

実装はまあ、ふつうですね。yharaはalias_methodを覚えた。

module DeclareArgsDecorator
  def self.check_args(args, types)
    if args.size != types.size      raise ArgumentError, "引数の数がちがうお"
    elsif 
      args.zip(types).each do |arg, type|
        unless arg.is_a?(type)
          raise ArgumentError, "引数の型がちがうお (#{type}じゃなくて#{arg.class}になってる)"
        end
      end
    end
  end

  @@declared_type = nil
  def declare_args(*klasses)
    @@declared_type = klasses
  end

  def method_added(name)
    if @@declared_type
      declared_type = @@declared_type
      @@declared_type = nil  #avoid infinite loop

      class_eval {
        alias_method("__original_#{name}__", name)
        define_method(name) do |*args|
          DeclareArgsDecorator.check_args(args, declared_type)
          self.__send__("__original_#{name}__", *args)
        end
      }
    end
  end
end

あとは一般化してmodule Decoratorとか作ればいいんだけど、飽きた。あとあんまりデコレータっぽくない(Pythonのデコレータは関数から関数を作る感じですよね)。

本日のツッコミ(全3件) [ツッコミを入れる]
shiro (2008-07-30 11:24)

call/ccは普通の人が使うものじゃないのです。<br>新しい例外メカニズムとか、新しいスレッドシステムとか、新しいへんてこりんな構文要素とかを試してみたい人が、そういうものを「処理系自体に手を加えずに」「かつ複数の実装間でポータブルに」作ってみる時につかうものなのです。<br>RubyやPythonや... で実験したい人は、処理系そのものに手を入れればよろしいでしょう。1言語1処理系を前提とするならそれで構わないわけで。(どちらも1言語1処理系の前提は崩れつつありますが)

ohai (2008-07-30 20:20)

なんか以前似たような物を作ったことがあります。<br>http://www.kmc.gr.jp/~ohai/diary/?date=20060110#p01<br>このときは Haskell の型宣言ぽく作ろうと考えていた気がします。<br><br>あとこの手のメソッドに細工をする類のメソッドを定義するのは<br>UnboundMethodを使ったほうがきれいに書けるかも。

yhara (2008-08-06 12:25)

shiroさん:<br>なるほど…、call/ccは「処理系の内臓が外にはみ出してるようなもの」と思っていいんですかね。<br>マクロがそうであるようにcall/ccも「Scheme使いなら普通に使うもの」かと思ってたんで、Scheme使いの方の意見が聞けて嬉しいです。