класс Continuation
Continuation объекты генерируются Kernel#callcc, после того как был вызван +require+ продолжение. Они хранят адрес возврата и контекст выполнения, позволяя выполнить нелокальное возвращение в конец callcc блока из любой точки программы. Продолжения несколько аналогичны структурированной версии setjmp/longjmp C (хотя они содержат больше состояния, поэтому их можно считать более похожими на потоки).
Например:
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 "#{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
Публичные методы экземпляра
static VALUE
rb_cont_call(int argc, VALUE *argv, VALUE contval)
{
rb_context_t *cont = cont_ptr(contval);
rb_thread_t *th = GET_THREAD();
if (cont_thread_value(cont) != th->self) {
rb_raise(rb_eRuntimeError, "continuation called across threads");
}
if (cont->saved_ec.protect_tag != th->ec->protect_tag) {
rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
}
if (cont->saved_ec.fiber_ptr) {
if (th->ec->fiber_ptr != cont->saved_ec.fiber_ptr) {
rb_raise(rb_eRuntimeError, "continuation called across fiber");
}
}
rollback_ensure_stack(contval, th->ec->ensure_list, cont->ensure_array);
cont->argc = argc;
cont->value = make_passing_arg(argc, argv);
cont_restore_0(cont, &contval);
return Qnil; /* unreachable */
} Вызывает продолжение. Программа продолжает выполнение с конца 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 = cont_ptr(contval);
rb_thread_t *th = GET_THREAD();
if (cont_thread_value(cont) != th->self) {
rb_raise(rb_eRuntimeError, "continuation called across threads");
}
if (cont->saved_ec.protect_tag != th->ec->protect_tag) {
rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
}
if (cont->saved_ec.fiber_ptr) {
if (th->ec->fiber_ptr != cont->saved_ec.fiber_ptr) {
rb_raise(rb_eRuntimeError, "continuation called across fiber");
}
}
rollback_ensure_stack(contval, th->ec->ensure_list, cont->ensure_array);
cont->argc = argc;
cont->value = make_passing_arg(argc, argv);
cont_restore_0(cont, &contval);
return Qnil; /* unreachable */
} Вызывает продолжение. Программа продолжает выполнение с конца 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]
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.