Ruby Code объяснил - PullRequest
       10

Ruby Code объяснил

5 голосов
/ 04 марта 2009

Может кто-нибудь объяснить этот кусок кода Ruby:

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].

Ответы [ 3 ]

14 голосов
/ 04 марта 2009

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

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

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

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

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

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

11 голосов
/ 04 марта 2009

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

if some_variable == nil
   some_variable = some_value
end

или

some_variable= some_value unless some_variable

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

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

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

РЕДАКТИРОВАТЬ: Более подробно о * 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

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

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

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

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

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

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

1 голос
/ 02 сентября 2011

Немного короче:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...