假设我有一个函数 foo:
def foo(A, B, C)
A + B + C
end
我这样称呼它,只改变最后一个参数:
foo("foo", "bar", "123")
foo("foo", "bar", "456")
foo("foo", "bar", "789")
如何“烘焙”或“预填充”不改变的参数?所以也许我会得到一个新的可调用
foo_baked
,这样 foo_baked("123")
与 foo("foo", "bar", "123")
相同?
并像这样使用它:
foo_baked = ...?
foo_baked("123")
foo_baked("456")
foo_baked("789")
请注意,我不想使用
def
定义新函数,但希望能够在运行时动态创建 foo_baked,也许是为了数组。
Ruby 内置了 function-currying 和 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
创建一个柯里化过程:一个预加载参数的函数,只有在传递足够的参数来满足方法签名时才会执行。
请注意,
foo_baked.lambda?
返回true
,因此柯里化过程实际上是一个lambda。这很重要,因为这意味着如果超过参数的最大数量,您将得到 ArgumentError
。
您可以通过将 arity 参数传递给
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 的答案中概述的“柯里化”方法。 第二种是使用默认位置参数。但为了做到这一点,可选参数需要位于必需参数之后:
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 包装原始方法:
请注意执行中的额外
.
。这是Ruby特殊语法,相当于:
foo_baked.call(123)