稲葉です。

Inaba Hiroto wrote:

> えっと、でも今のrubyのcallccがscheme(他は調べてないですが、少なくとも
> GNUのscmとは)と違う振る舞いをする部分があるのを見つけてます。そのため
> 今の自分のcallccによる外部iteratorは使いづらい面があります。
> 詳細は明日(すみません)ruby-devにmailします。

えっと、callccの仕様の修正をお願いしようと思っていたのでruby-devに出す
つもりだったのですが、色々調べて仕様変更することはないと思い直したので
このままruby-listに出します。

まず、自分が書いて原さんに単純化してもらった

class Cursor
  def initialize(iter)
    @iter = iter
    @cont = nil
  end

  def next
    @cont.call if @cont
    @iter.call {|obj|
      callcc{|@cont| return obj }
    }
    nil
  end
end

ですが、色んな使い方をしようとすると破綻します。例えば

c = Cursor.new([1,2,3].method(:each))
p obj = c.next
p obj = c.next

とすると
1
2
3
nil
nil
nil
 :
nil
nil
cursor-by-callcc.rb:17: Interrupt

と無限ループになってしまいます。別の例では以下も無限ループです。

$acc = nil
def a
  callcc{|cc| $acc = cc; 1}
end

ret = a
p ret
$acc.call 2
p ret
exit

SCMでほぼ同じコード、

(define call/cc call-with-current-continuation)
(define acc #f)

(define (a)
  (call/cc (lambda (cc) (set! acc cc) 1)) )

(define ret (a))
(print ret)
(acc 2)
(print ret)
(exit)

を実行すると
1
2
とちゃんと止まります。
ところが最後の部分を

(begin
  (define ret (a))
  (print ret)
  (acc 2)
  (print ret)
  (exit))

とすると、これは無限ループです。
SCMはトップレベルを超えた継続をきるようです。
トップレベルが人間との会話系の場合と合わせているのかな
と思いますが、本当の所はわかりません。

rubyでもそうなればいいのかなあと最初考えたのですが、
トップレベルだけそうしてもクラスやモジュールにした時
他のメソッドから呼ぶときも結局同じになるので、あまり
意味ないな、と思い直しました。

で、色々考えてCursorのcallccによる実装を作り直しました。
先のやつよりちょっと面倒になって、さらに遅くなりました。

class Cursor
  def initialize(iter)
    @iter = iter
    @cont = nil
  end

  def next
    callcc{|@cc|
      @cont.call if @cont
      @iter.call {|obj|
        callcc{|@cont| @cc.call(obj) }
      }
      @cc.call(nil)
    }
  end
end
--
			稲葉 浩人  <inaba / st.rim.or.jp>