6

Proc.newまたはを使用してインスタンス化された Proc の厳密なアリティ強制を「オン」にして、Kernel.procでインスタンス化された Proc のように動作する方法はありlambdaますか?

私のinitializeメソッドはブロック&actionを取り、それをインスタンス変数に割り当てます。アリティを厳密に強制したいactionので、後で引数を適用するとArgumentError、より意味のある例外をレスキューして発生させることができます。基本的:

class Command
  attr_reader :name, :action

  def initialize(name, &action)
    @name   = name
    @action = action
  end

  def perform(*args)
    begin
      action.call(*args)
    rescue ArgumentError
      raise(WrongArity.new(args.size))
    end
  end
end

class WrongArity < StandardError; end

残念ながら、actionデフォルトではアリティを強制しません:

c = Command.new('second_argument') { |_, y| y }
c.perform(1) # => nil

action.to_proc動作しませんlambda(&action)

他のアイデアはありますか?それとも問題へのより良いアプローチですか?

ありがとう!

4

3 に答える 3

8

あなた@actionProcインスタンスになり、Procs にはarityメソッドがあるため、ブロックに必要な引数の数を確認できます。

def perform(*args)
  if args.size != @action.arity
    raise WrongArity.new(args.size)
  end
  @action.call(*args)
end

{ |a| ... }これで、 やのようなスプラットのないブロックは処理されるはずですが、スプラットの{ |a,b| ... }場合は少し複雑になります。次のようなブロックがある場合、{ |*a| ... }-1@action.arityになり、-2 に{ |a,*b| ... }なります。arityアリティが -1 のブロックは任意の数の引数 (なしを含む) を取ることができ、アリティが -2 のブロックは少なくとも 1 つの引数を必要としますが、それ以上の引数を取ることができます。splatless テストの単純な変更は、splatful ブロッ​​クを処理する必要があります。

def perform(*args)
  if @action.arity >= 0 && args.size != @action.arity
    raise WrongArity.new(args.size)
  elsif @action.arity < 0 && args.size < -(@action.arity + 1)
    raise WrongArity.new(args.size)
  end
  @action.call(*args)
end
于 2012-11-29T07:34:01.847 に答える
1

この回答によると、proc をラムダに変換する唯一の方法はdefine_method、友人を使用することです。ドキュメントから:

define_method非ラムダ Proc オブジェクトが指定された場合でも、常にトリックなしでメソッド [つまり、ラムダ スタイルの Proc ] を定義します。これは、トリックが保持されない唯一の例外です。

具体的には、実際にメソッドを定義するだけでなくdefine_method(:method_name, &block)、ラムダを返します。不必要に貧弱なオブジェクトに一連のメソッドを定義せずにこれを使用するdefine_singleton_methodには、一時オブジェクトで使用できます。

したがって、次のようなことができます。

def initialize(name, &action)
  @name = name
  @action = to_lambda(&action)
end

def perform(*args)
  action.call(*args)
  # Could rescue ArgumentError and re-raise a WrongArity, but why bother?
  # The default is "ArgumentError: wrong number of arguments (0 for 1)",
  # doesn't that say it all?
end

private

def to_lambda(&proc)
  Object.new.define_singleton_method(:_, &proc)
end
于 2012-11-29T14:08:39.677 に答える
-1

あなたの解決策:

class Command
  attr_reader :name, :action

  def initialize(name) # The block argument is removed
    @name   = name
    @action = lambda # We replace `action` with just `lambda`
  end

  def perform(*args)
    begin
      action.call(*args)
    rescue ArgumentError
      raise(WrongArity.new(args.size))
    end
  end
end

class WrongArity < StandardError; end

いくつかの参考文献: 「Proc.new が独自の引数なしでメソッド内から呼び出されると、周囲のメソッドに指定されたブロックを含む新しい Proc が返されます。」-- http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html

ラムダも同じように機能することがわかります。

于 2012-11-29T08:29:57.050 に答える