2016-01-29 11 views
5

Khi tôi Splat một mảng với * khi đi qua nó để một phương pháp, sau đó tái tạo lại một mảng từ nó với * trong phương pháp này, danh tính của mảng không được bảo quản:Đối tượng proc được bảo quản trong `&` -` & `roundtrip như thế nào?

a = [] 
a.object_id # => 69846339548760 
def bar *a; a.object_id end 
bar(*a) # => 69846339537540 

Tuy nhiên, khi tôi quay một proc thành một khối với & khi đi qua nó để một phương pháp, sau đó tái tạo lại một proc từ khối với & trong phương pháp, danh tính của proc dường như được bảo quản:

pr = ->{} 
pr.object_id # => 69846339666160 
def foo ≺ pr.object_id end 
foo(&pr) # => 69846339666160 

Làm thế nào là đối tượng proc bảo quản? Không phải là nó bị mất khi nó được chuyển đổi thành một khối? Đây có phải là hành vi được bảo đảm không?

+1

Thậm chí nhiều hơn: 'def baz; đặt Proc.new.object_id kết thúc; p = -> {}; p.object_id # ⇒ 9708320; baz &p ; # ⇒ 9708320' – mudasobwa

+0

@mudasobwa Đó là do một quy ước cú pháp đặc biệt. Khi bạn tạo một proc mà không có một khối trong một phương thức, nó đề cập đến proc được chuyển đến phương thức. – sawa

+0

Tôi biết, cảm ơn bạn. Tôi có nghĩa là 'Proc.new' là trong thực tế _not một constructor_. – mudasobwa

Trả lời

2

Ruby VM là một ngăn xếp. Khi gọi một hàm, nó đặt tất cả các đối số của nó (bao gồm self) vào ngăn xếp và sau đó gọi.

Làm thế nào mảng splat làm việc - phải mất nội dung mảng và đặt nó vào stack, sau đó gọi hàm:

> puts RubyVM::InstructionSequence.compile("a = []; func *a").disasm 
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: [email protected], kwrest: -1]) 
[ 2] a   
0000 trace   1            ( 1) 
0002 newarray   0 
0004 setlocal_OP__WC__0 2 
0006 putself   
0007 getlocal_OP__WC__0 2 
0009 splatarray  false 
0011 opt_send_without_block <callinfo!mid:func, argc:1, ARGS_SPLAT|FCALL> 
0013 leave 

Khi đi qua proc như một khối, điều tương tự sẽ xảy ra, nhưng ruby ​​không phải unwrap proc , nó đã là một proc.

Added: theo RubySpec của https://github.com/ruby/spec/blob/master/core/proc/block_pass_spec.rb

def revivify; Proc.new; end 

it "remains the same object if re-vivified by the target method" do 
    p = Proc.new {} 
    p2 = revivify(&p) 
    p.object_id.should == p2.object_id 
    p.should == p2 
end 

đây là hành vi có phần tiêu chuẩn hóa, vì vậy ít nhất nên được theo sau bởi Rubinius và JRuby

+1

Vì vậy, điều này có nghĩa là câu trả lời cho câu hỏi này phụ thuộc vào việc bạn đang sử dụng MRI, JRuby, Rubinius, vv? –

+1

@JoshRumbut rubyspec xác định hành vi này, vì vậy nó phải giống nhau – Vasfed

Các vấn đề liên quan