使用关键字参数来描述proc
假设我有一个通用的Proc
, Lambda
或带有可选第二个参数的方法:
pow = -> (base, exp: 2) { base**exp }
现在我想讨论这个函数,给它一个3
的exp
。
cube = pow.curry.call(exp: 3)
这里存在歧义,源于关键字参数和新的哈希语法,其中Ruby将exp: 3
解释为作为第一个参数base
传递的哈希。 这导致函数立即被调用,当#**
被发送到散列时呈现NoMethodError
。
为第一个参数设置默认值同样会导致在currying时立即调用该函数,并且如果我根据需要标记第一个参数,而不提供默认值:
pow = -> (base:, exp: 2) { base**exp }
当我试图讨论Proc
时,翻译会抱怨我缺少参数base
。
如何用第二个参数来讨论函数?
您可以构建自己的关键字风格的咖喱方法,该方法收集关键字参数,直到存在所需的参数。 就像是:
def kw_curry(method) -> (**kw_args) { required = method.parameters.select { |type, _| type == :keyreq } if required.all? { |_, name| kw_args.has_key?(name) } method.call(**kw_args) else -> (**other_kw_args) { kw_curry(method)[**kw_args, **other_kw_args] } end } end def foo(a:, b:, c: nil) { a: a, b: b, c: c } end proc = kw_curry(method(:foo)) proc[a: 1] #=> # proc[b: 1] #=> # proc[a: 1, b: 2] #=> {:a=>1, :b=>2, :c=>nil} proc[b: 2][a: 1] #=> {:a=>1, :b=>2, :c=>nil} proc[a: 1, c: 3][b: 2] #=> {:a=>1, :b=>2, :c=>3}
上面的示例仅限于关键字参数,但您当然可以扩展它以支持关键字参数和位置参数。
我不认为你可以用Proc.curry
做到这Proc.curry
,但总有一种简单的方法
cube = -> (base) {pow.(base, exp: 3)}
您还可以创建工厂function
pow_factory = -> (exp) {-> (base) {pow.(base, exp: exp)}} cube = pow_factory.(3)
-
curry
不适用于关键字参数。 curried函数一次获取一个参数,这在概念上与“任何顺序很好”关键字参数不兼容。 -
curry
必须知道确切的arity。 如果你只是调用没有参数的curry
,它将忽略任何选项(如果是pow = -> (base, exp=2) { base**exp }
,则与curry(1)
相同。 使用curry(2)
强制两个参数。 curried函数无法知道可选参数是否跟随,并且读取将来以确定它是应该执行还是返回curried continuation。
按照上一条评论扩展@Stefan上面的答案,并与他的片段一致:
def curry(method) -> (*args, **kargs) { required = method.parameters.select { |type, _| type == :req } krequired = method.parameters.select { |type, _| type == :keyreq } all_args = (required.length <= args.length) all_keys = krequired.all? { |_, name| kargs.has_key?(name) } if all_args && all_keys final_args = (args + kargs.map {|k,v| {k => v} }) method.call(*final_args) else -> (*args_, **kargs_) { curry(method)[*args, *args_, **kargs, **kargs_] } end } end def foo(a1, b1, c1 = 5, a2:, b2:, c2: 50) { a1: a1, b1: b1, c1: c1, a2: a2, b2: b2, c2: c2} end puts foz = curry(method(:foo)) #=> # puts bar = foz[6, a2: 60] #=> # puts bar[1, b2: 10] #=> {:a1=>6, :b1=>1, :c1=>5, :a2=>60, :b2=>10, :c2=>50} puts baz = bar[1] #=> # puts baz[10, b2: 30, c2: 40] #=> {:a1=>6, :b1=>1, :c1=>10, :a2=>60, :b2=>30, :c2=>40}