【问题标题】:Ruby - How do I pass an array and function within another function's arguments?Ruby - 如何在另一个函数的参数中传递数组和函数?
【发布时间】:2021-02-18 10:47:30
【问题描述】:

我想知道如何在另一个函数的参数中传递一个数组和函数。在下面的代码中,我得到了一个wrong number of arguments (given 0, expected 1)

它需要一个数组students 和一个名为callback 的回调。 is_female 是我要调用的函数。

students = [
    {name: 'John', grade: 8, gender: 'M'},
    {name: 'Sarah', grade: 12, gender: 'F'}
]
def is_female(students)
  students.find_all{ |item| item[:gender] == 'F' }
end
def filter_gender(students, &callback)
  callback(students)
end

p filter_gender(students, is_female)

【问题讨论】:

    标签: arrays ruby function callback


    【解决方案1】:

    我得到错误数量的参数(给定 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"}]
    

    这个对象可以赋值给变量,也可以作为positionalkeyword 参数传递给其他方法,就像任何其他对象一样。

    &amp;callback 然而是block argument,所以方法对象必须是converted to a block via &amp;

    m = method(:is_female)
    filter_gender(students, &m)
    
    # or in one line:
    
    filter_gender(students, &method(:is_female))
    

    实际上有两个步骤:&amp; 首先调用Method#to_proc,然后将结果作为块参数传递。所以在filter_gender 中,callback 变量不是指方法对象,而是指它的过程。 (另一层间接)

    幸运的是,MethodProc 有一个类似的调用接口:Proc#callProc#[]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,而不是方法,它可以通过&amp; 作为块参数传递:

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

    【讨论】:

    • OP,您缺少 callback.callcallback.() 以使您的代码正常工作。&amp;callback 将捕获 callback 变量中的块,但您必须调用 #call.() 语法来执行它。
    【解决方案2】:

    在 Ruby 中,将方法名称放在代码中相当于调用该方法(因为括号在上下文中是可选的),而您想要的是方法的引用。方法可以通过符号(方法名称以':'为前缀)或包含方法名称的字符串来引用。可以通过调用send 来调用该方法,并将方法引用作为第一个参数,然后是该方法所需的任何参数。

    尝试以下方法:

    students = [
        {name: 'John', grade: 8, gender: 'M'},
        {name: 'Sarah', grade: 12, gender: 'F'}
    ]
    
    def is_female(students)
      students.find_all{ |item| item[:gender] == 'F' }
    end
    
    def filter_gender(students, callback)
      send(callback, students)
    end
    
    p filter_gender(students, :is_female)
    

    我已经成功地使用它来实现离散事件模拟 Ruby gem,其中 gem 不需要任何修改即可运行具有任意数量的事件和您选择的事件名称的模型。

    【讨论】:

    • 这也返回wrong number of arguments (given 1, expected 2)
    • @StasysMeclazcke 你复制并粘贴了我的代码,还是抄写了它(可能有错误)?我发布的是在我的计算机上运行的代码的副本/粘贴,生成 [{:name=&gt;"Sarah", :grade=&gt;12, :gender=&gt;"F"}]
    猜你喜欢
    • 2019-06-08
    • 2019-10-09
    • 1970-01-01
    • 1970-01-01
    • 2012-05-18
    • 1970-01-01
    • 2014-05-05
    • 2015-12-04
    • 2019-10-09
    相关资源
    最近更新 更多