2

我正在尝试在类上定义合取Proc#*和析取。当接收者和参数的数量不同时,它应该引发错误。否则,它应该分别返回一个联合/析取的 proc 对象。我实现它们如下:Proc#+Proc

class Proc
  def * other
    raise ArgumentError, "Arity does not match" unless arity == other.arity
    ->(*args){call(*args) and other.call(*args)}
  end
  def + other
    raise ArgumentError, "Arity does not match" unless arity == other.arity
    ->(*args){call(*args) or other.call(*args)}
  end
end

这适用于简单的过程:

p = ->x{x % 2 == 0} * ->x{x % 3 == 0}
p.call(2) # => false
p.call(6) # => true

但是当我进一步尝试构建这些方法的结果时,它会引发错误:

q = p * ->x{x % 5 == 0}
# => Error, Arity does not match

这是因为 is 的 arity ,而->x{x % 5 == 0}is的arity在我的实现中。1p-1call(*args)

有什么好的方法可以使方法Proc#*Proc#+递归工作吗?


如果我raise ...从定义中删除 ,那么它将起作用,但是,当具有不同 arity 的 proc 结合/分离时,它将返回误导性错误消息。例如,假设raise ...从上面的定义中删除了该部分,我这样做:

p = ->x{x % 2 == 0} * ->x, y, z{x % 3 == 0}

thenProc#*不会引发错误,但会返回一个有效的 proc 对象。但是,由于它的一部分需要一个参数,而另一部分需要三个参数,因此无法以p有效的方式将参数传递给。

p.call(2) # => error

会提出一个ArgumentError,说:

Wrong number of arguments

但是错误实际上是在创建p不能满足任何数量的参数时发生的,并且错误消息会产生误导。这就是我添加raise ...支票的原因。删除是raise ...使其工作的唯一方法吗?

4

2 回答 2

1

我认为您可能需要删除,raise因为可以有效调用带有可变数量参数的 proc,但具有不同的 arity 值。例如

p = ->(a) {} * (a, *args) {}
p.call(1)   # valid
p.call(1,2) # invalid

也许解决方案是改进返回的 lambda 中的错误消息?就像是

lambda(*args) do
  raise "wrong number of arguments for left lambda" if (arity < 0 && arity.abs - 1 <= args.size) || (arity > 0 && arity != args.size)
  # etc (not sure those cases are exactly right)
  call(*args) and other.call(*args)
end

使用parameters方法可能更容易,它将拼出必需的、可选的和 splat 参数。应该可以轻松检查在这种情况下是否永远无法调用生成的 lambda 并在创建时抛出错误。

于 2013-10-08T23:22:18.943 回答
1

一种可能的方法是扩展Proc

class MyProc < Proc
  attr_reader :my_arity

  def initialize(my_arity = nil, &block)
    @my_arity = my_arity || block.arity
    super(&block)
  end

  def *(other)
    other = MyProc.new(&other)
    raise "Arity error" unless my_arity == other.my_arity
    MyProc.new(my_arity, &->(*args){ call(*args) && other[*args] })
  end
end

p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x, y){ x == y })[1,1] #=> true
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x, y){ x != y })[1,1] #=> false
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x){ x })[1,1] #=> exception
于 2013-10-09T00:09:54.150 回答