有人可以解释一下这段Ruby代码:
def add_spec_path_to(args) # :nodoc:
args << {} unless Hash === args.last
args.last[:spec_path] ||= caller(0)[2]
end
我已经看到<<
运算符用于连接字符串或者作为其他语言的按位运算符,但有人可以在此上下文中解释它。它是以某种方式将空白的lamda附加到args上还是我完全错了?
我也可以看到它像这样使用:
before_parts(*args) << block
Hash
是否为关键字?
我也不确定||=
运营商在说什么。
对于caller(0)[2]
是什么,我同样处于黑暗中。
答案 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 :(得分:11)
|| =是一种常见的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
它通常用于为函数提供可变参数的可变参数列表。
你确定你准确地转录了原始代码吗,顺便问一下?为了获得一个参数数组,你通常会在声明的参数中添加一个*,否则args必须作为一个数组输入,而不是打败对象。
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导致args作为数组呈现给方法。如果我不使用*,就像这样,例如:
def y(args)
puts args.join(',')
puts args.map{|a| a.class }.join(',')
end
...然后args在我调用方法之前必须是一个数组,否则我会得到一个“ArgumentError:错误的参数数量”,但除了一件事之外。所以它必须看起来像这样:
y [1,2,{:c=>3,:d=>4}]
...使用{}显式创建的Hash。这太丑了。
所有上述测试均采用MRI 1.8.6,btw。
答案 2 :(得分: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