2009-03-04 36 views
5

thể ai đó xin vui lòng giải thích đoạn mã này Ruby:của Ruby Mã giải thích

def add_spec_path_to(args) # :nodoc: 
    args << {} unless Hash === args.last 
    args.last[:spec_path] ||= caller(0)[2] 
end 

Tôi đã thấy các << điều hành sử dụng cho các chuỗi nối hoặc như một nhà điều hành Bitwise bằng các ngôn ngữ khác, nhưng ai đó có thể giải thích nó trong bối cảnh này. Có phải nó bằng cách nào đó gắn thêm một lamda trống vào args hay tôi hoàn toàn sai?

tôi cũng có thể thấy nó được sử dụng như thế này:

before_parts(*args) << block 

Hash một từ khóa?

Tôi cũng không chắc chắn về những gì nhà điều hành ||= đang nói.

Tôi bình đẳng trong bóng tối như những gì caller(0)[2] là.

Trả lời

11

|| = là thành ngữ Ruby phổ biến: nó chỉ gán giá trị nếu nó chưa được đặt. Hiệu quả là giống như mã như

if some_variable == nil 
    some_variable = some_value 
end 

hoặc

some_variable= some_value unless some_variable 

===, khi không ghi đè, so sánh hai đối tượng cho sắc. Trong trường hợp của Hash === args.last, Hash (là đối tượng của loại Class) đang kiểm tra xem nó có khớp với lớp của mục cuối cùng trong mảng args hay không. Mã đang sử dụng thực tế rõ ràng là việc triển khai Class # === buộc kiểm tra trên lớp của đối tượng được so sánh.

Nó không làm việc theo cách khác xung quanh, ví dụ:

a = [{}] 
Hash === a.last #=> true 
a.last === Hash #=> false 

Những lập luận trailing đến một phương pháp có thể được cung cấp như nội dung của một băm mà không cần phải cung cấp {}

Vì vậy, bạn có thể thực hiện điều này:

def hello(arg1, arg2, arg3) 
    puts [arg1.class, arg2.class, arg3.class].join(',') 
end 

hello 1,2,3 #=> Fixnum,Fixnum,Fixnum 
hello :a, "b", :c => 1, :d => 99 #=> Symbol,String,Hash 

Nó thường được sử dụng để cung cấp danh sách các thông số tùy chọn có độ dài thay đổi cho hàm.

Bạn có chắc là bạn đã phiên mã chính xác mã btw không? Để có được một mảng các đối số, bạn thường sẽ thêm một * vào đối số như được khai báo, nếu không args sẽ phải là đầu vào như là một mảng, thay vì sẽ đánh bại đối tượng.

def add_spec_path_to(*args)    # now args is an array 
    args << {} unless Hash === args.last # if trailing arguments cannot be 
             # interpreted as a Hash, add an empty 
             # Hash here so that following code will 
             # not fail 
    args.last[:spec_path] ||= caller(0)[2] # Set the spec_path option if it's not 
             # already set 
end 

EDIT: Mở rộng hơn nữa về điều * args, hãy thử này:

def x(*args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

x 1,2,:a=>5,:b=>6 
1,2,a5b6 
Fixnum,Fixnum,Hash 

... sử dụng * args gây args sẽ được trình lên các phương pháp như một mảng. Nếu tôi không sử dụng *, như thế này, ví dụ:

def y(args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

... sau đó args phải là một mảng trước khi tôi gọi phương thức, hoặc tôi sẽ nhận được một "ArgumentError: sai số đối số "cho bất cứ điều gì nhưng một điều đã trôi qua. Vì vậy, nó phải giống như thế này:

y [1,2,{:c=>3,:d=>4}] 

... với Hash được tạo một cách rõ ràng với {}. Và nó xấu xí.

Tất cả các thử nghiệm trên đã được thử với MRI 1.8.6, btw.

+0

Không, đi qua trong một mảng có ý nghĩa, vì args không được trả lại, vì vậy nếu không một Hash nối sẽ bị mất. – rampion

+0

Tôi không chắc chắn tôi hiểu: bạn sẽ gọi phương thức như thế này: some_method ([arg1, arg2, arg3], options_hash)? Thật kinh khủng: hãy xem câu trả lời đã chỉnh sửa ở trên ... –

+0

Giải thích tuyệt vời, Mike. –

14

Tôi giả sử rằng argsArray.

Hash là tên của một lớp học - dòng đầu tiên đẩy một băm rỗng {} vào argstrừ yếu tố cuối cùng của args đã là một Hash (các === điều hành cho kiểm tra các lớp học xem một đối tượng là một lớp nhất định) .

Nhà điều hành ||= cũng tương tự như các nhà điều hành +=: đó là nhiều hay ít tương đương với:

args.last[:spec_path] = args.last[:spec_path] || caller(0)[2] 

Vì vậy, nó sẽ thiết lập args.last[:spec_path] khi và chỉ khi nó là hiện unset.

Phương thức caller trả về thông tin về phương thức gọi.

1

Trong một cách ngắn hơn chút:

def add_spec_path_to(args) # :nodoc: 

... 

# Append an empty hash to args UNLESS the last arg is a hash.. in which case do nothing 
args << {} unless Hash === args.last # so we need a hash. If it is not there, make an empty one and put it there. 

... 

#if args.last[:spec_path] equals nil or false, set it to caller(0)[2]... 

#so inside that hash from the first part, if :spec_path is not there, create it by using caller(0)[2]. 

args.last[:spec_path] ||= caller(0)[2] 

... 

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