Ruby 2.4

Continuation

Continuation类

Parent:Object

继续对象由Kernel#callcc在+ require + d延续之后生成。 它们拥有一个返回地址和执行上下文,允许非本地从程序中的任何位置返回到callcc块的末尾。 持续有点类似于C的setjmp / longjmp的结构化版本(尽管它们包含更多的状态,所以你可能会认为它们更接近线程)。

例如:

require "continuation" arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ] callcc{|cc| $cc = cc} puts(message = arr.shift) $cc.call unless message =~ /Max/

产生结果:

Freddie Herbie Ron Max

你也可以用其他方法调用callcc:

require "continuation" def g arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ] cc = callcc { |cc| cc } puts arr.shift return cc, arr.size end def f c, size = g c.call(c) if size > 1 end f

这个(人为的)例子允许内部循环提前放弃处理:

require "continuation" callcc {|cont| for i in 0..4 print "\n#{i}: " for j in i*5...(i+1)*5 cont.call() if j == 17 printf "%3d", j end end } puts

产生结果:

0: 0 1 2 3 4 1: 5 6 7 8 9 2: 10 11 12 13 14 3: 15 16

公共实例方法

contargs, ...()

调用延续。该程序从该callcc块的末尾继续。如果没有给出参数,则原始callcc返回nil。如果给出一个参数,则callcc返回它。否则,返回一个包含args的数组。

callcc {|cont| cont.call } #=> nil callcc {|cont| cont.call 1 } #=> 1 callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3]

static VALUE rb_cont_call(int argc, VALUE *argv, VALUE contval) { rb_context_t *cont; rb_thread_t *th = GET_THREAD( GetContPtr(contval, cont if (cont->saved_thread.self != th->self) { rb_raise(rb_eRuntimeError, "continuation called across threads" } if (cont->saved_thread.protect_tag != th->protect_tag) { rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier" } if (cont->saved_thread.fiber) { if (th->fiber != cont->saved_thread.fiber) { rb_raise(rb_eRuntimeError, "continuation called across fiber" } } rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array cont->argc = argc; cont->value = make_passing_arg(argc, argv /* restore `tracing' context. see [Feature #4347] */ th->trace_arg = cont->saved_thread.trace_arg; cont_restore_0(cont, &contval return Qnil; /* unreachable */ }

call(args, ...) Show source

调用延续。 该程序从callcc块的末尾继续。 如果没有给出参数,则原始callcc返回nil。 如果给出一个参数,callcc会返回它。 否则,返回一个包含args的数组。

callcc {|cont| cont.call } #=> nil callcc {|cont| cont.call 1 } #=> 1 callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3]

static VALUE rb_cont_call(int argc, VALUE *argv, VALUE contval) { rb_context_t *cont; rb_thread_t *th = GET_THREAD( GetContPtr(contval, cont if (cont->saved_thread.self != th->self) { rb_raise(rb_eRuntimeError, "continuation called across threads" } if (cont->saved_thread.protect_tag != th->protect_tag) { rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier" } if (cont->saved_thread.fiber) { if (th->fiber != cont->saved_thread.fiber) { rb_raise(rb_eRuntimeError, "continuation called across fiber" } } rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array cont->argc = argc; cont->value = make_passing_arg(argc, argv /* restore `tracing' context. see [Feature #4347] */ th->trace_arg = cont->saved_thread.trace_arg; cont_restore_0(cont, &contval return Qnil; /* unreachable */ }