31
Лямбды, блоки, замыкания А давайте посмотрим, что у них внутри

Блоки, лямбды, замыкания

Embed Size (px)

DESCRIPTION

Как в Руби внутри устроены блоки, лямбды и замыкания.

Citation preview

Page 1: Блоки, лямбды, замыкания

Лямбды, блоки, замыкания

А давайте посмотрим, что у них внутри

Page 2: Блоки, лямбды, замыкания

Дмитрий Кириенко

Twitter: @DimKiriyenko

Github: dmitriy-kiriyenko

Page 3: Блоки, лямбды, замыкания
Page 4: Блоки, лямбды, замыкания

rb_block_t

???

Page 5: Блоки, лямбды, замыкания

10.times do str = "Hello world." puts strend

putstring "Hello world."setlocal str, 0putselfgetlocal str, 0send :puts, 1leave

rb_block_t

iseq

Page 6: Блоки, лямбды, замыкания

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

Page 7: Блоки, лямбды, замыкания

"Чтобы решить эту проблему мы вводим понятие замыкания как структуры, содержащей лямбда-выражение и окружение, которое будет использовано, когда это выражение будет применено к своим аргументам."Scheme: An Interpreter for Extended Lambda Calculus

Page 8: Блоки, лямбды, замыкания

Внутренний стек YARV

locals: str_ext

rb_control_frame_t

EP

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

Кадр стека верхнего уровня

Page 9: Блоки, лямбды, замыкания

Внутренний стек YARV

locals: str_ext

rb_control_frame_t

EP

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

Кадр стека верхнего уровня

rb_block_t

iseq

EP

Page 10: Блоки, лямбды, замыкания

Внутренний стек YARV

locals: str_ext

rb_control_frame_t

EP

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

Кадр стека Fixnum#times

Page 11: Блоки, лямбды, замыкания

Внутренний стек YARV

locals: str_ext

rb_control_frame_t

EP

Кадр стека Block#yield

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

locals: str

EP

rb_block_t

iseq

EP

Page 12: Блоки, лямбды, замыкания

DEFINE_INSNgetlocal(lindex_t idx, rb_num_t level)()(VALUE val){ int i, lev = (int)level; VALUE *ep = GET_EP();

for (i = 0; i < lev; i++) { ep = GET_PREV_EP(ep); } val = *(ep - idx);}

https://github.com/ruby/ruby/blob/trunk/insns.def#L43L67

Page 13: Блоки, лямбды, замыкания

"Чтобы решить эту проблему мы вводим понятие замыкания как структуры, содержащей лямбда-выражение и окружение, которое будет использовано, когда это выражение будет применено к своим аргументам."Scheme: An Interpreter for Extended Lambda Calculus

Page 14: Блоки, лямбды, замыкания

typedef struct rb_control_frame_struct { VALUE *pc; VALUE *sp; rb_iseq_t *iseq; VALUE flag; VALUE self; VALUE klass; VALUE *ep; rb_iseq_t *block_iseq; VALUE proc; const rb_method_entry_t *me;

#if VM_DEBUG_BP_CHECK VALUE *bp_check;#endif} rb_control_frame_t;

Control frame Block

typedef struct rb_block_struct { VALUE self; VALUE klass VALUE *ep; rb_iseq_t *iseq; VALUE proc;} rb_block_t;

Page 15: Блоки, лямбды, замыкания

require 'benchmark'require 'benchmark/ips'

Benchmark.ips do |b| b.report 'while' do sum = 0; i = 0 while i <= 10 sum += i; i += 1 end end

b.report 'block' do sum = 0 (1..10).each do |i| sum += 1 end end

b.report 'reduce' do (1..10).reduce {|acc, i| acc+i} endend

while 6763524 in 5.036406sblock 4267105 in 5.016071sreduce 3606540 in 5.054937s

Page 16: Блоки, лямбды, замыкания

def get_helloer str_ext = "Hello" lambda do |who| puts "#{str_ext} #{who}!" endend

helloer = get_helloerhelloer.call('there')

Page 17: Блоки, лямбды, замыкания

В 1930-х годах Алонзо Чёрч ввёл лямбда-нотацию в своём исследовании "Лямбда-исчисление"

Page 18: Блоки, лямбды, замыкания

def get_helloer str_ext = "Hello" lambda do |who| puts "#{str_ext} #{who}!" endend

helloer = get_helloerhelloer.call('there')

Page 19: Блоки, лямбды, замыкания

rb_lambda_t

???

Page 20: Блоки, лямбды, замыкания

Стек

Куча

locals: str_ext rb_control_frame_t

Кадр стека get_helloer

"Hello"

RString

Page 21: Блоки, лямбды, замыкания

Стек

Куча

locals: str_ext rb_control_frame_t

Кадр стека get_helloer

str_extrb_block_t

iseq

EP

rb_proc_t

envval

is_lambda

rb_env_t

env

Page 22: Блоки, лямбды, замыкания

Стек

Куча

locals: str_ext rb_control_frame_t

Кадр стека внешнего кода

helloer

rb_proc_t

rb_env_t str_ext

Page 23: Блоки, лямбды, замыкания

Стек

Куча

locals: str_ext rb_control_frame_t

Кадр стека helloer.call

str_extrb_block_t

iseq

EP

rb_proc_t

envval

is_lambda

rb_env_t

env

argument: whoEP

Page 24: Блоки, лямбды, замыкания

def get_helloer str_ext = "Hello" res = lambda do |who| puts "#{str_ext} #{who}!" end str_ext = "Goodbye" resend

helloer = get_helloerhelloer.call('there')

Page 25: Блоки, лямбды, замыкания

def get_helloer str_ext = "Hello" res = lambda do |who| puts "#{str_ext} #{who}!" end str_ext = "Goodbye" resend

helloer = get_helloerhelloer.call('there')

Goodbye there!

Page 26: Блоки, лямбды, замыкания

Стек

Куча

locals: str_ext rb_control_frame_t

Кадр стека get_helloer

str_ext

rb_env_t

env

EP

Page 27: Блоки, лямбды, замыкания

501491

def create_counter(start) value = start { inc: ->{value+=1}, dec: ->{value-=1}, get: ->{value} }end

counter = create_counter(500)counter[:inc].callputs counter[:get].call #=> 50110.times { counter[:dec].call }puts counter[:get].call #=> 491

Page 28: Блоки, лямбды, замыкания

block 19659752 in 5.000587slambda 4574745 in 5.033561s

require 'benchmark/ips'

def do_with_block yieldend

def do_with_lambda(&block) block.callend

Benchmark.ips do |b| b.report 'block' do do_with_block { 1+1 } end b.report 'lambda' do do_with_lambda {1+1} endend

Page 29: Блоки, лямбды, замыкания

JRuby

_file_

RubyFixnum.times

block_0$RUBY$__file__DynamicScope

DynamicScopestr_ext

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

родитель

Page 30: Блоки, лямбды, замыкания

Rubinius

Integer.times

код блокаVariableScope

VariableScopestr_ext

str_ext = "Hello"10.times do str = "world." puts "#{str_ext} #{str}"end

код верхнего уровня

родитель

Page 31: Блоки, лямбды, замыкания