Ruby中能否按非声明顺序实现函数柯里化?
你说得没错,Ruby标准库自带的curry方法确实是严格按照参数声明的顺序来做柯里化的,没法直接跳过前面的参数去绑定后面的。但这并不意味着咱们必须重写Proc或者写代理接口——其实有不少优雅的方式可以实现非顺序的参数绑定,完美复用现有的Proc/方法。
针对你的场景的具体解决方案
先回到你的示例代码,原Proc是:
at = ->(arr, i) { arr[i] } list = [ [11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44] ]
你想要固定第二个参数i=1,然后把每个数组作为第一个参数传入,得到每个数组的第二个元素。这里有几种不用重写原Proc的方法:
方法1:手动封装部分应用的Proc
这是最简单直接的方式,写个小lambda把原Proc包起来,固定我们需要的参数:
# 固定i=1,返回一个只接收arr的Proc at_with_i1 = ->(arr) { at.call(arr, 1) } p list.map(&at_with_i1) # => [12, 22, 32, 42]
这种方式可读性拉满,不需要依赖任何额外工具,适合单次使用的场景。
方法2:写个通用工具函数实现任意位置的参数绑定
如果需要频繁对不同Proc做非顺序参数绑定,可以写一个通用的高阶函数,支持按参数名绑定任意位置的值:
def partial_apply(proc, **kwargs) ->(*args) { # 按照原Proc的参数顺序,把固定参数和传入参数组合起来 full_args = proc.parameters.map do |_type, name| kwargs.fetch(name) { args.shift } end proc.call(*full_args) } end # 使用时直接指定要固定的参数名和值 at_with_i1 = partial_apply(at, i: 1) p list.map(&at_with_i1) # => [12, 22, 32, 42]
这个函数利用Proc#parameters获取原Proc的参数列表,然后根据我们传入的关键字参数填充固定值,剩下的参数按顺序补位。不管原Proc的参数顺序如何,都能灵活绑定任意位置的参数,复用性极强。
方法3:反转参数顺序后用curry(适合双参数场景)
如果你的场景只是需要交换两个参数的顺序,Ruby 2.7+给Proc加了reverse方法,可以直接反转参数处理顺序,然后再用curry绑定:
# 反转原Proc的参数顺序,变成(i, arr)后再curry绑定i=1 at_with_i1 = at.reverse.curry[1] p list.map(&at_with_i1) # => [12, 22, 32, 42]
这种方式非常简洁,适合参数数量少、只需要调整顺序的场景。
为什么标准库的curry不支持非顺序绑定?
Ruby的设计哲学偏向简洁和明确,标准库的curry遵循函数式编程里经典的柯里化逻辑——按参数声明顺序逐步绑定。如果需要更灵活的参数绑定,Ruby更鼓励开发者通过高阶函数来实现,而不是在标准库里添加复杂的配置选项,这样既保持了语言的简洁性,又给了开发者足够的扩展空间。
内容的提问来源于stack exchange,提问作者Nossidge

