我得到错误数量的参数(给定 0,预期 1)
在许多其他语言中,is_female() 将调用该方法,而is_female 将返回对该方法的引用。在 Ruby 中,括号是可选的,两种变体是等价的——它们调用方法。
在 Ruby 中获取对方法的引用有点复杂:您必须调用 Object#method 并传递方法的名称。结果是Method 的一个实例——一个围绕你的方法的对象包装器:
m = method(:is_female)
#=> #<Method: main.is_female>
m.paramters
#=> [[:req, :students]]
m.call(students) # <- equivalent to is_female(students)
#=> [{:name=>"Sarah", :grade=>12, :gender=>"F"}]
这个对象可以赋值给变量,也可以作为positional 或keyword 参数传递给其他方法,就像任何其他对象一样。
&callback 然而是block argument,所以方法对象必须是converted to a block via &:
m = method(:is_female)
filter_gender(students, &m)
# or in one line:
filter_gender(students, &method(:is_female))
实际上有两个步骤:& 首先调用Method#to_proc,然后将结果作为块参数传递。所以在filter_gender 中,callback 变量不是指方法对象,而是指它的过程。 (另一层间接)
幸运的是,Method 和 Proc 有一个类似的调用接口:Proc#call、Proc#[] 或 prc.():(call 的语法糖)
callback.call(students)
callback[students]
callback.(students)
# ^
# note the dot
应用于您的代码:
def filter_gender(students, &callback)
callback.call(students)
end
filter_gender(students, &method(:is_female))
#=> [{:name=>"Sarah", :grade=>12, :gender=>"F"}]
一些建议:
我会将学生性别的过滤和检索从方法中移出并移至filter_gender:
def is_female(gender)
gender == 'F'
end
def filter_gender(students, &callback)
students.select { |item| callback.call(item[:gender]) }
end
filter_gender(students, &method(:is_female))
#=> [{:name=>"Sarah", :grade=>12, :gender=>"F"}]
而不是显式调用callback,我只需要yield 块的性别并使用常规块调用它:(您可以以任何一种方式组合上面和下面的代码)
def filter_gender(students)
students.select { |item| yield item[:gender] }
end
filter_gender(students) { |gender| is_female(gender) }
#=> [{:name=>"Sarah", :grade=>12, :gender=>"F"}]
您可以显式创建Proc,而不是方法,它可以通过& 作为块参数传递:
is_female = -> (gender) { gender == 'F' }
filter_gender(students, &is_female)
#=> [{:name=>"Sarah", :grade=>12, :gender=>"F"}]
最后同样重要的是,您可以创建一个Student 类并实现一个female? 方法:
class Student
attr_accessor :name, :grade, :gender
def initialize(name:, grade:, gender:)
@name = name
@grade = grade
@gender = gender
end
def female?
gender == 'F'
end
end
students = [
Student.new(name: 'John', grade: 8, gender: 'M'),
Student.new(name: 'Sarah', grade: 12, gender: 'F')
]
students.select(&:female?)
#=> [#<Student:0x00007fa8d186e350 @name="Sarah", @grade=12, @gender="F">]