2009-03-04 12 views
5

Может кто-нибудь, пожалуйста, объясните этот кусок Руби код:Рубин Код объяснил

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

Я видел << оператор, используемый для конкатенации строк или как оператор побитового на других языках, но может кто-нибудь объяснить это в данном контексте. Это каким-то образом добавляет пустую ламду на арги, или я совершенно не прав?

Я также могу увидеть его использовать, как это:

before_parts(*args) << block 

ли Hash ключевое слово?

Я также не уверен, что говорит оператор ||=.

Я в равной степени в неведении относительно того, что такое caller(0)[2].

ответ

11

|| = - обычная идиома Ruby: она присваивает значение только в том случае, если оно еще не установлено. Эффект такой же, как код, как

if some_variable == nil 
    some_variable = some_value 
end 

или

some_variable= some_value unless some_variable 

===, когда он не переопределен, сравнивает два объекта идентичности. В случае Hash === args.last, Hash (который является объектом типа Class) проверяет, соответствует ли он классу последнего элемента массива args. В коде используется очевидный факт, что реализация класса # === заставляет проверять класс сравниваемого объекта.

Это не работает наоборот, например:

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

Аргументы хвостовые к способу, могут быть поставлены в качестве содержимого хэш без необходимости, чтобы обеспечить {}

Таким образом, вы можете сделать это:

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 

Он часто используется для предоставления переменной функциональных возможностей переменной длины.

Вы точно переписали исходный код, кстати? Чтобы получить массив аргументов, вы обычно добавляете * к аргументу, как объявлено, иначе аргументы должны вводиться как массив, который скорее победит объект.

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: Расширение далее на * ARGS вещь, попробуйте следующее:

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 

... с помощью * арг вызывает арг быть представлен метод как массив. Если я не использую *, как это, например:

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

... то арг должен быть массив, прежде чем я называю метод, или я получаю «ArgumentError: неверное число аргументы "за что-то, кроме одного. Так что это должно выглядеть так:

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

... с Хэшем, явно созданным с помощью {}. И это уродливо.

Все вышеперечисленное тестировалось с помощью МРТ 1.8.6, кстати.

+0

Нет, проходя в массиве имеет смысл, так как арг не возвращается, так что в противном случае, добавочный Hash будет потерян. – rampion

+0

Я не уверен, что понимаю: вы бы назвали метод следующим: some_method ([arg1, arg2, arg3], options_hash)? Это ужасно: см. Отредактированный ответ выше ... –

+0

Отличное объяснение, Майк. –

14

Я предполагаю, что args является Array.

Hash это имя класса - первая строка толкает пустой хэш {} на argsесли последний элемент args не является уже Hash (=== оператор для классов тестов, является ли объект определенного класса) ,

Оператор ||= подобен оператору +=: это более или менее эквивалентно:

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

Таким образом, он будет установлен args.last[:spec_path] тогда и только тогда, когда он в настоящее время не установлено.

Метод caller возвращает информацию о вызывающем методе.

1

В немного более коротком пути:

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 
Смежные вопросы