128

tap私はちょうどブログ記事を読んでいて、著者がスニペットで次のようなものを使用していることに気付きました:

user = User.new.tap do |u|
  u.username = "foobar"
  u.save!
end

私の質問は、を使用する利点または利点は正確には何tapですか? 私はただできませんでした:

user = User.new
user.username = "foobar"
user.save!

またはさらに良い:

user = User.create! username: "foobar"
4

19 に答える 19

117

読者が遭遇したとき:

user = User.new
user.username = "foobar"
user.save!

3 つの行すべてをたどる必要があり、それが という名前のインスタンスを作成しているだけであることを認識する必要がありますuser

もしそうなら:

user = User.new.tap do |u|
  u.username = "foobar"
  u.save!
end

そうすれば、それはすぐに明らかになります。userインスタンスが作成されたことを知るために、リーダーはブロックの内部を読む必要はありません。

于 2013-07-05T16:50:41.107 に答える
43

タップを使用する別のケースは、オブジェクトを返す前に操作することです。

したがって、これの代わりに:

def some_method
  ...
  some_object.serialize
  some_object
end

余分な行を保存できます:

def some_method
  ...
  some_object.tap{ |o| o.serialize }
end

状況によっては、この手法により 1 行以上を節約し、コードをよりコンパクトにすることができます。

于 2013-07-05T17:12:56.477 に答える
38

これは、一連のチェーンされたスコープをデバッグする場合に役立ちます。ActiveRecord

User
  .active                      .tap { |users| puts "Users so far: #{users.size}" } 
  .non_admin                   .tap { |users| puts "Users so far: #{users.size}" }
  .at_least_years_old(25)      .tap { |users| puts "Users so far: #{users.size}" }
  .residing_in('USA')

これにより、ローカル変数に何かを格納したり、元のコードを大幅に変更したりする必要なく、チェーンの任意の時点で非常に簡単にデバッグできます。


そして最後に、通常のコード実行を中断することなくデバッグするための迅速かつ目立たない方法として使用します。

def rockwell_retro_encabulate
  provide_inverse_reactive_current
  synchronize_cardinal_graham_meters
  @result.tap(&method(:puts))
  # Will debug `@result` just before returning it.
end
于 2016-01-15T15:32:12.240 に答える
10

@sawaの答えのバリエーション:

既に述べたように、 を使用tapすると、コードの意図を理解するのに役立ちます (コードが必ずしもコンパクトになるわけではありません)。

次の 2 つの関数は同じ長さですが、最初の関数では、最初に空のハッシュを初期化した理由を理解するために最後まで読む必要があります。

def tapping1
  # setting up a hash
  h = {}
  # working on it
  h[:one] = 1
  h[:two] = 2
  # returning the hash
  h
end

一方、ここでは、初期化されるハッシュがブロックの出力 (この場合は関数の戻り値) になることが最初からわかっています。

def tapping2
  # a hash will be returned at the end of this block;
  # all work will occur inside
  Hash.new.tap do |h|
    h[:one] = 1
    h[:two] = 2
  end
end
于 2015-05-13T17:38:40.960 に答える
8

を使うメリットはないと言えtapます。@sawa が指摘しているように、唯一の潜在的な利点は次のとおりです。ただし、その時点で、単純化されていないレコード作成ロジックを実行している場合は、そのロジックを独自のメソッドに抽出することで、意図がより適切に伝達されるという議論を行うことができます。

私は、コードの可読性に対する不必要な負担であり、 Extract Methodtapのようなより良い手法を使用せずに行うか、代わりに使用できるという意見を保持しています。

tap便利な方法ですが、個人的な好みでもあります。試してみてくださいtap。それから、tap を使わずにコードを書いてください。

于 2013-07-05T17:33:26.847 に答える
4

使用できる可能性のある用途と場所がいくつかある可能性がありますtap。これまでのところ、次の 2 つの使用方法しか見つかりませんでしtapた。

1) このメソッドの主な目的は、チェーン内の中間結果に対して操作を実行するために、メソッド チェーンを利用することです。すなわち

(1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
    tap    { |x| puts "array: #{x.inspect}" }.
    select { |x| x%2 == 0 }.
    tap    { |x| puts "evens: #{x.inspect}" }.
    map    { |x| x*x }.
    tap    { |x| puts "squares: #{x.inspect}" }

2) 何かのオブジェクトに対してメソッドを呼び出したときに、戻り値が望んだものではないことに気付いたことはありませんか? ハッシュに格納された一連のパラメーターに任意の値を追加したいと思うかもしれません。Hash.[]で更新しますが、params ハッシュの代わりにbarが返されるため、明示的に返す必要があります。すなわち

def update_params(params)
  params[:foo] = 'bar'
  params
end

ここでこの状況を打破するために、tapメソッドが登場します。オブジェクトで呼び出して、実行したいコードを含むブロックをタップするだけです。オブジェクトはブロックに渡され、返されます。すなわち

def update_params(params)
  params.tap {|p| p[:foo] = 'bar' }
end

他にもたくさんのユースケースがありますので、自分で見つけてみてください :)

出典:
1) API Dock オブジェクト タップ
2)使用すべき 5 つのルビー メソッド

于 2017-01-25T05:55:33.173 に答える
3

あなたの言うとおりです。tapあなたの例での の使用は無意味であり、おそらくあなたの代替案よりもきれいではありません。

Rebitzele が指摘しているように、tapは単なる便利なメソッドであり、現在のオブジェクトへの短い参照を作成するためによく使用されます。

の良い使用例の 1 つtapはデバッグです。オブジェクトを変更し、現在の状態を出力してから、同じブロック内でオブジェクトの変更を続けることができます。例については、http: //moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressionsを参照してください。

私はときどきtap内部メソッドを使用して条件付きで早期に戻り、それ以外の場合は現在のオブジェクトを返すことを好みます。

于 2013-07-05T16:55:56.053 に答える