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

问题描述 投票:0回答:4

假设我有一个函数 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-on-rails ruby function functional-programming
4个回答
6
投票

Ruby 内置了 function-curryingcurry 方法:

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')

1
投票

您可以定义一个新方法

foo_baked
来包装
foo
并将不可更改的参数作为硬编码值传递:

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

然后可以这样调用:

foo_baked("123")

或者,您可以使用 Ruby 的内置

#curry
方法,如 TonyArra 的回答 中所述。这种方法更灵活,返回一个可调用的过程,直到提供足够的参数。


0
投票

您有几个选择。第一种是 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 包装原始方法:


0
投票

请注意执行中的额外

.
。这是Ruby特殊语法,相当于:

foo_baked.call(123)

    

© www.soinside.com 2019 - 2024. All rights reserved.