如何在Ruby中预填充函数的参数?



假设我有一个函数foo:

def foo(A, B, C)
A + B + C
end

我这样称呼它,只改变最后一个参数:

foo("foo", "bar", "123")
foo("foo", "bar", "456")
foo("foo", "bar", "789")

我怎样才能"烘焙"呢?或";pre-fill"不变的论点是什么?也许我会得到一个新的可调用的foo_baked这样foo_baked("123")就和foo("foo", "bar", "123")一样了?

并像这样使用:

foo_baked = ...?
foo_baked("123")
foo_baked("456")
foo_baked("789")

请注意,我不想使用def定义一个新函数,但希望能够在运行时动态地创建foo_baked,可能是一个数组。

Ruby在curry方法中内置了curry函数:

def foo(a, b, c)
a + b + c
end
foo_baked = method(:foo).curry.call('foo', 'bar')
# this returns "foobar123"
foo_baked.call('123')
# ArgumentError (wrong number of arguments (given 4, expected 3))
foo_baked.call('123', '234')

基本上,Method#curry返回一个curry进程:一个预加载了参数的函数,它只会在传递了足够的参数以满足方法签名时执行。

注意foo_baked.lambda?返回true,所以一个柯里化进程实际上是一个lambda。这一点很重要,因为这意味着如果超过了参数的最大数量,就会得到一个ArgumentError

通过向curry传递一个实参,可以允许在必需的三个实参之外添加其他实参。

def foo(*args)
args.join
end
# does not execute since only 2 arguments have been supplied
foo_baked = method(:foo).curry(3).call('foo', 'bar')
# executes on the third argument
# this returns "foobar123"
foo_baked.call('123')
# this returns "foobar123234"
foo_baked.call('123', '234')

您可以定义一个新方法foo_baked,它包装foo并将不可更改的参数作为硬编码值传递:

def foo_baked(c)
foo("foo", "bar", c)
end

则可以这样调用:

foo_baked("123")

或者,您可以使用Ruby的内置#curry方法,如TonyArra的回答所述。这种方法更加灵活,在提供足够的参数之前返回一个可调用的进程。

您有几个选择。第一个是在Zoran的回答中概述的curry方法。

第二种是使用默认的位置参数。但为了做到这一点,可选参数需要在之后的必需的参数:

def foo_baked(a, b="foo", c="bar")
[a, b, c]
end
foo_baked(123) # => [123, "foo", "bar"]
foo_baked(123, "asd") 

第三个选项是使用关键字参数。这种方法的好处是参数可以是任意顺序的。您可以挑选您为其提供值的对象。

def foo_baked(b: "foo", c: "bar", a:)
[a, b, c]
end
foo_baked(a: 123) # => [123, "foo", "bar"]
foo_baked(a: 123, b: "asd") # => [123, "asd", "bar"]

最后一个选项是组合位置和关键字参数:

def foo_baked(a, b: "foo", c: "bar")
[a, b, c]
end
foo_baked(123) # => [123, "foo", "bar"]
foo_baked(123, b: "asd") # => [123, "asd", "bar"]

这样做的好处是仍然可以使用位置参数,但可选参数的顺序无关紧要,它们永远不会干扰位置参数

您可以创建一个lambda包装方法:

foo_baked = ->(value) { foo('foo', 'bar', value) }
foo_baked.(123) # => [123, "foo", "bar"]

请注意执行过程中附加的.。这是ruby的特殊语法,相当于:

foo_baked.call(123)

最新更新