使用关键字参数来描述proc

时间:2016-01-19 06:10:53

标签: ruby functional-programming currying

说我有一个通用的ProcLambdamethod,它带有一个可选的第二个参数:

pow = -> (base, exp: 2) { base**exp }

现在我想讨论这个函数,给它exp 3

cube = pow.curry.call(exp: 3)

这里存在歧义,源于关键字参数和新的哈希语法,其中Ruby将exp: 3解释为作为第一个参数base传递的哈希。这会导致函数立即被调用,当NoMethodError被发送到散列时呈现#**

设置第一个参数的默认值同样会导致在curry时立即调用该函数,并且如果我根据需要标记第一个参数,而不提供默认值:

pow = -> (base:, exp: 2) { base**exp }
当我试图讨论base时,口译员会抱怨说我错过了论据Proc

如何用第二个参数来讨论函数?

4 个答案:

答案 0 :(得分:8)

您可以构建自己的关键字风格的咖喱方法,该方法收集关键字参数,直到存在所需的参数。类似的东西:

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:0x007f9a1c0891f8 (lambda)>
proc[b: 1]              #=> #<Proc:0x007f9a1c088f28 (lambda)>
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}

上面的示例仅限于关键字参数,但您当然可以扩展它以支持关键字参数和位置参数。

答案 1 :(得分:4)

我认为你不能用const _name = Symbol(); class Person { constructor(name) { this[_name] = name; } } const person = new Person('John'); alert(person.name); // => undefined做到这一点,但总有一种简单的方式

Proc.curry

您还可以创建工厂功能

cube = -> (base) {pow.(base, exp: 3)}

答案 2 :(得分:2)

  • curry不适用于关键字参数。一个curried函数一次得到一个参数,这在概念上与&#34不相容;任何顺序都很好&#34;关键字参数。
  • curry必须知道确切的arity。如果您只是在没有参数的情况下调用curry,它将忽略所有选项(如果是pow = -> (base, exp=2) { base**exp },则与curry(1)相同)。使用curry(2)强制执行这两个参数。一个curried函数不能知道一个可选参数正在跟随,并读取未来以确定它是应该执行还是返回一个curried continuation。

答案 3 :(得分:0)

根据最新评论并根据其摘要扩展@Stefan answer above

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)) #=> #<Proc:0x0000000003a255f0@./training.rb:56 (lambda)>
puts bar = foz[6,  a2: 60]     #=> #<Proc:0x0000000003a255f0@./training.rb:56 (lambda)>
puts bar[1, b2: 10]            #=> {:a1=>6, :b1=>1, :c1=>5, :a2=>60, :b2=>10, :c2=>50}
puts baz = bar[1]              #=> #<Proc:0x0000000003a17540@./training.rb:64 (lambda)>
puts baz[10, b2: 30, c2: 40]   #=> {:a1=>6, :b1=>1, :c1=>10, :a2=>60, :b2=>30, :c2=>40}