简体   繁体   English

传递一个在Ruby中将参数作为参数的方法

[英]Passing a method that takes arguments as an argument in Ruby

I'd like to calculate the difference for various values inside 2 hashes with the same structure, as concisely as possible. 我想尽可能简洁地计算具有相同结构的2个哈希值内的各种值的差异。 Here's a simplified example of the data I'd like to compare: 这是我想要比较的数据的简化示例:

hash1 = {"x" => { "y" => 20 } }
hash2 = {"x" => { "y" => 12 } }

I have a very simple method to get the value I want to compare. 我有一个非常简单的方法来获取我想要比较的值。 In reality, the hash can be nested a lot deeper than these examples, so this is mostly to keep the code readable: 实际上,散列可以嵌套比这些示例更深,所以这主要是为了保持代码可读:

def get_y(data)
  data["x"]["y"]
end

I want to create a method that will calculate the difference between the 2 values, and can take a method like get_y as an argument, allowing me to re-use the code for any value in the hash. 我想创建一个方法来计算2个值之间的差异,并且可以将get_y这样的方法作为参数,允许我重复使用哈希中任何值的代码。 I'd like to be able to call something like this, and I'm not sure how to write the method get_delta : 我希望能够调用这样的东西,我不知道如何编写方法get_delta

get_delta(hash1, hash2, get_y)    # => 8

The "Ruby way" would be to pass a block: “Ruby方式”将传递一个块:

def get_delta_by(obj1, obj2)
  yield(obj1) - yield(obj2)
end

hash1 = {"x" => { "y" => 20 } }
hash2 = {"x" => { "y" => 12 } }

get_delta_by(hash1, hash2) { |h| h["x"]["y"] }
#=> 8

A method could be passed (indirectly) via: 方法可以通过以下方式(间接)传递:

def get_y(data)
  data["x"]["y"]
end

get_delta_by(hash1, hash2, &method(:get_y))
#=> 8

Building on Stefan's response, if you want a more flexible get method you can actually return a lambda from the function and pass arguments for what you want to get. 基于Stefan的响应,如果你想要一个更灵活的get方法,你实际上可以从函数返回一个lambda并传递你想要得到的参数。 This will let you do error handling nicely: 这样可以很好地处理错误:

Starting with the basics from above... 从上面的基础知识开始......

def get_delta_by(obj1, obj2)
  yield(obj1) - yield(obj2)
end

hash1 = {"x" => { "y" => 20 } }
hash2 = {"x" => { "y" => 12 } }

get_delta_by(hash1, hash2) { |h| h["x"]["y"] }

Then we can define a get_something function which takes a list of arguments for the path of the element to get: 然后我们可以定义一个get_something函数,该函数获取元素路径的参数列表:

def get_something(*args)
  lambda do |data|
    args.each do |arg|
      begin
        data = data.fetch(arg)
      rescue KeyError
        raise RuntimeError, "KeyError for #{arg} on path #{args.join(',')}"
      end
    end
    return data
  end
end

Finally we call the function using the ampersand to pass the lambda as a block: 最后,我们使用&符号调用函数将lambda作为块传递:

lambda_getter = get_something("x","y")
get_delta_by(hash1, hash2, &lambda_getter)

That last bit can be a one liner... but wrote it as two for clarity here. 最后一点可以是一个班轮......但为了清楚起见,将其写成两个。

In Ruby 2.3, you can use Hash#dig method, if it meets your needs. 在Ruby 2.3中,如果满足您的需求,您可以使用Hash#dig方法。

hash1.dig("x", "y") - hash2.dig("x", "y")
#=> 8

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM