2012-03-04 19 views
73

Hầu hết nhận thức được _’s special meaning in IRB as a holder for last return value, nhưng đó là không phải những gì tôi hỏi ở đây.Biến _ (gạch dưới) ở đâu và như thế nào được chỉ định?

Thay vào đó, tôi hỏi về _ khi được sử dụng làm tên biến trong mã Ruby cũ. Ở đây nó dường như có hành vi đặc biệt, giống như một "biến không quan tâm" (à la Prolog). Dưới đây là một số ví dụ minh họa hữu ích hành vi độc đáo của nó:

lambda { |x, x| 42 }   # SyntaxError: duplicated argument name 
lambda { |_, _| 42 }.call(4, 2) # => 42 
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_' 
lambda { |_| _ + 1 }.call(42) # => 43 
lambda { |_, _| _ }.call(4, 2) # 1.8.7: => 2 
           # 1.9.3: => 4 
_ = 42 
_ * 100   # => 4200 
_, _ = 4, 2; _ # => 2 

Đây là tất cả chạy trong Ruby trực tiếp (với puts s thêm vào) -không IRB-để tránh xung đột với chức năng bổ sung của nó.

Đây là kết quả của thử nghiệm của riêng tôi mặc dù, vì tôi không thể tìm thấy bất kỳ tài liệu nào về hành vi này ở bất kỳ đâu (thừa nhận đây không phải là điều dễ dàng nhất để tìm kiếm). Cuối cùng, tôi tò mò làm thế nào tất cả điều này hoạt động nội bộ vì vậy tôi có thể hiểu rõ hơn những gì là đặc biệt về _. Vì vậy, tôi yêu cầu tài liệu tham khảo tài liệu, và tốt nhất là mã nguồn Ruby (và có lẽ là RubySpec) cho biết cách hoạt động của Ruby trong _.

Lưu ý: hầu hết những điều này phát sinh trong this discussion với @Niklas B.

Trả lời

48

Có một số xử lý đặc biệt trong nguồn để chặn lỗi "tên đối số trùng lặp". Các thông báo lỗi chỉ xuất hiện trong shadowing_lvar_gen bên parse.y, the 1.9.3 version looks like this:

static ID 
shadowing_lvar_gen(struct parser_params *parser, ID name) 
{ 
    if (idUScore == name) return name; 
    /* ... */ 

idUScoredefined in id.c như thế này:

REGISTER_SYMID(idUScore, "_"); 

Bạn sẽ thấy xử lý đặc biệt tương tự trong warn_unused_var:

static void 
warn_unused_var(struct parser_params *parser, struct local_vars *local) 
{ 
    /* ... */ 
    for (i = 0; i < cnt; ++i) { 
     if (!v[i] || (u[i] & LVAR_USED)) continue; 
     if (idUScore == v[i]) continue; 
     rb_compile_warn(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i])); 
    } 
} 

Bạn sẽ nhận thấy rằng cảnh báo bị chặn trên dòng thứ hai của t anh ta for vòng lặp.

Xử lý đặc biệt duy nhất của _ mà tôi có thể tìm thấy trong nguồn 1.9.3 ở trên: lỗi tên trùng lặp bị chặn và cảnh báo biến không sử dụng bị chặn.Ngoài hai thứ đó, _ chỉ là một biến cũ đơn giản giống như bất kỳ biến nào khác. Tôi không biết bất kỳ tài liệu nào về tính đặc biệt (nhỏ) của _.

Trong Ruby 2.0, các thử nghiệm idUScore == v[i] trong warn_unused_var được thay thế bằng một cuộc gọi đến is_private_local_id:

if (is_private_local_id(v[i])) continue; 
rb_warn4S(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i])); 

is_private_local_id ngăn chặn cảnh báo cho các biến bắt đầu với _:

if (name == idUScore) return 1; 
/* ... */ 
return RSTRING_PTR(s)[0] == '_'; 

thay vì chỉ _ chinh no. Vì vậy, 2,0 nới lỏng mọi thứ lên một chút.

+1

Tôi tự hỏi liệu sự khác biệt về hành vi của 'lambda {| _, _ | _} .call (4, 2) 'giữa 1.8 & 1.9 chỉ là một tác dụng phụ không chủ ý sau đó? Trong các trường hợp "bình thường", trong đó tên biến không thể được nhân đôi thứ tự mà chúng được chỉ định là không quan trọng. –

+0

Wow, bạn đánh tôi với nó. +1 –

+3

@AndrewMarshall: Vâng, tôi nghĩ rằng vấn đề "4 vs 2" chỉ là một tạo phẩm về cách 1,8 và 1,9 xử lý ngăn xếp. Thời gian duy nhất nó sẽ là đáng chú ý là '| _, _, ... |' bởi vì lỗi trùng lặp đã bị loại bỏ. –

22

_ là một định danh hợp lệ. Số nhận dạng không được chứa dấu gạch dưới, chúng cũng có thể dấu gạch dưới.

_ = o = Object.new 
_.object_id == o.object_id 
# => true 

Bạn cũng có thể sử dụng nó như tên phương pháp:

def o._; :_ end 
o._ 
# => :_ 

Tất nhiên, nó không phải là chính xác một tên dễ đọc, cũng không vượt qua bất kỳ thông tin đến người đọc về những gì biến đề cập đến hoặc những gì phương pháp nào.

IRB, đặc biệt là bộ _ với giá trị của biểu thức cuối cùng:

$ irb 
> 'asd' 
# => "asd" 
> _ 
# => "asd" 

Vì nó là in the source code, nó chỉ đơn giản đặt _ với giá trị cuối cùng:

@workspace.evaluate self, "_ = IRB.CurrentContext.last_value" 

Đã làm một số kho lưu trữ khám phá.Đây là những gì tôi thấy:

Trên dòng cuối cùng của tập tin id.c, có cuộc gọi:

REGISTER_SYMID(idUScore, "_"); 

grep ing nguồn cho idUScore đã cho tôi hai kết quả dường như có liên quan:

shadowing_lvar_gen có vẻ là cơ chế mà thông số chính thức của một khối thay thế một biến cùng tên tồn tại trong một phạm vi khác. Đó là chức năng dường như tăng "tên đối số trùng lặp" SyntaxError và cảnh báo "biến bên ngoài biến địa phương".

Sau grep ing nguồn cho shadowing_lvar_gen, tôi thấy sau on the changelog for Ruby 1.9.3:

Tue 11 tháng 12 01:21:21 2007 Yukihiro Matsumoto

  • parse.y (shadowing_lvar_gen): không trùng lặp lỗi cho "_".

nào có khả năng là nguồn gốc của this line:

if (idUScore == name) return name; 

Từ đó, tôi suy ra rằng trong một tình huống như proc { |_, _| :x }.call :a, :b, một _ biến đơn giản là bóng tối kia.


Đây là the commit in question. Về cơ bản nó giới thiệu hai dòng sau:

if (!uscore) uscore = rb_intern("_"); 
if (uscore == name) return; 

Từ thời điểm mà idUScore thậm chí không tồn tại, rõ ràng.

+5

Điều này không giải thích * chút nào * tại sao 'lambda {| _, _ | 42} 'hoạt động trong khi' lambda {| x, x | 42} 'không. –

+0

@AndrewMarshall, có vẻ như bạn đã đúng. '| _, _ |' không hoạt động, nhưng '| __, __ |' thì không. '_' dường như có một số ý nghĩa đặc biệt, tôi sẽ xem liệu tôi có thể khai thác bất kỳ thông tin nào từ nguồn Ruby. –

+1

Nhân tiện, cập nhật của bạn, trong khi thông tin, không liên quan vì nó chỉ áp dụng cho IRB, mà tôi đã nêu cụ thể trong câu hỏi của tôi mà tôi không hỏi. –

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