【问题标题】:Pass arbitrary number of lambdas (or procs) in Ruby在 Ruby 中传递任意数量的 lambda(或 procs)
【发布时间】:2021-08-14 00:47:44
【问题描述】:

我正在研究 Ruby 中的函数模型,但遇到了一个问题。我能够成功地将任意给定数量的参数传递给任意函数,如下所示:

add = ->(x, y) { return x + y }
mul = ->(x, y) { return x * y }

def call_binop(a, b, &func)
  return func.call(a, b)
end

res = call_binop(2, 3, &add)
print("#{res}\n")   #5

res = call_binop(3, 4, &mul)
print("#{res}\n")   #12

但是,我无法传递任意数量的函数:

dbl = ->(x) { return 2 * x }
sqr = ->(x) { return x * x }

def call_funccomp(a, &func1, &func2)
  return func2.call(func1.call(a))
end

res = call_funccomp(3, &dbl, &sqr)
print("#{res}\n")   #Expect 36 but compiler error

编译器错误为syntax error, unexpected ',', expecting ')'

我已经将 lambdas 和 procs 添加到一个数组中,然后执行了数组的元素,所以我知道我可以通过传递这样一个数组作为参数来解决这个问题,但对于简单的情况,这似乎是一个扭曲某些东西(我希望)在该语言中是合法的。 Ruby 是否真的限制了参数列表中可以传递的数量或 lambda?它似乎有一个相当现代、灵活的功能模型(符号有点奇怪),其中事情可以通过call 方法执行。

【问题讨论】:

  • @alf 我不同意关闭,链接的答案是硬连线使用两个 procs/lambdas。有一个更通用的解决方案(当它关闭时我正在发布)。 def call_funccomp(a, *funcs); funcs.each { |f| a = f.call(a) }; a; end; 例如,call_funccomp(3, dbl, sqr) 为 36,call_funccomp(3, dbl, sqr, dbl) 为 72。
  • 我的问题与链接的问题不同,但我可以使用那里发布的解决方案。另外(我会尝试一下),我可以使用您在此处发布的那个。谢谢。 Ruby 的功能方面对我来说似乎并没有遵循“原则或最不意外”。例如,我所看到的所有内容都使用了 &func 和 func.call() 语法,这在本示例中失败了。我还没有在任何地方看到 func 和 func.() 记录,更糟糕的是,它可以工作,这意味着它的行为不同。您可能知道我在哪里可以获得明确的 Ruby 文档吗?不是那种“嘿,看看编程有多简单”吗?
  • @PoissonAerohead &block 语法在编程语言中是相当反常的。该语言确实是硬连线的,每个方法只接受一个块。即使方法不期望它,您也可以传递一个块(该方法不会对它做任何事情,但这不是语法错误)。如果您需要将多个可执行代码块传递给一个方法,请使用常规 procs/lambdas,如 pjs 所述。
  • @PoissonAerohead 在搜索时,我找到了this blog。看起来功能组合已明确添加到 Ruby 2.6。基于此,您可能更喜欢的另一个解决方案是:dbl = ->(x) { 2 * x }; sqr = ->(x) { x * x }; composition = dbl >> sqr; puts composition.call(3)>> 可以被链接起来,所以 composition = dbl >> sqr >> dbl 在以 3 作为参数调用时将产生 72。
  • @PoissonAerohead 您可以将yield 转为&block,顺便说一句。 “这让你认为你正在像一个普通的论点一样传递它,但你不是” - 如果你用&传递它,你不是。你没有用这个印记装饰其他论点,所以它显然不仅仅是一个普通的论点。

标签: ruby functional-programming


【解决方案1】:

Ruby 是否真的限制了参数列表中可以传递的数量或 lambda?

不,您可以传递任意数量的 procs / lambdas。您只是不能将它们作为块参数传递。

在 proc 前加上 & 会触发 Ruby 的 proc to block conversion,即你的 proc 成为一个块参数。而 Ruby 最多只允许一个 block argument

尝试调用call_funccomp(3, &dbl, &sqr)相当于传递了两个块:

call_funccomp(3) { 2 * x } { x * x }

Ruby 不允许的东西。

解决方法是省略 &,即将 procs / lambdas 作为 positional arguments 传递:

dbl = ->(x) { 2 * x }
sqr = ->(x) { x * x }

def call_funccomp(a, func1, func2)
  func2.call(func1.call(a))
end

res = call_funccomp(3, dbl, sqr)
print("#{res}\n") 

还有Proc#>>,它结合了两个过程:

def call_funccomp(a, func1, func2)
  (func1 >> func2).call(a)
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-10
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多