6

Ruby で (字句ではなく) 動的なスコープ変数を使用することに興味があります。

letLispのように直接組み込みの方法はないようです。動的スコープ変数を実行する 1 つの可能な方法は、Christian Neukirchenによって提案されています。彼は自分のDynamicクラスで「スレッド ローカル ハッシュ」を作成します。私はそれについてあまり夢中ではありませんでした。

tapそこで、Ruby 1.9 にはメソッドがあることを思い出しました。tap一連のコマンドでデバッグ値を出力するために使用している人をたくさん見かけます。動的スコープ変数を非常にうまく模倣するために使用できると思います。

以下は、動的スコープ変数を使用したい状況の例と、 を使用した解決策tapです。

これを投稿するブログがあり、フィードバックがあれば、そこで投稿します。代わりに、私はこの考えに対する批判を求めて S/O に来ました。批評を投稿してください。賛成票が最も多いものに正しい答えを与えます。


状況

Account、各アカウントを表す ActiveRecord オブジェクトがありますhas_many Transaction。ATransactionには次の 2 つの属性があります。

  • description
  • amount

transactionsのすべての合計を見つけたいとaccount考えていますが、 はまたは のamountいずれかであることに注意してください (いいえ、それを批判することはできません)。nilFloat

最初のアイデアは次のとおりです。

def account_value
  transactions.inject(0){|acum, t| acum += t.amount}
end

これは、最初にゼロの量を持っているときに爆撃します:

TypeError: nil can't be coerced into Fixnum

クリーン ソリューション

tapを一時的に定義するために使用しますamount = 0transaction値を 0 のままにして保存するのを忘れた場合に備えて、これを一時的なものにしたいだけです。

def account_value
  transactions.inject(0){|acm, t| t.amount.tap{|amount| amount ||=0; acm+=amount}; acm}
end

の代入 to zero-if-nil はブロックamount内にあるためtap、 に戻すのを忘れる心配はありませんnil

あなたの考えは何ですか?

4

3 に答える 3

6

さて、あなたは何か他のものを目指していると思いますが、次のコードはあなたの例を修正し、実際には理解しやすいです:

transactions.inject(0) { |acum, t| acum += t.amount || 0 }

しかし、金額を合計するメソッドが金額のデフォルト値を知っているべきではないと思うnilので、(あなたの質問がそれについて議論できないと言っていても)amount代わりにデフォルトを返すようにメソッドを変更します:

def amount
  @amount || 0
end

それでも、あなたの例は解くのが簡単すぎると思います。実際には、より複雑な質問への回答を目指しています。他のすべての答えを楽しみにしています。

于 2011-03-22T00:55:23.777 に答える
4

ソリューションの動的スコープがどこにあるかわかりません。tap新しい字句スコープのブロックが導入され、値は字句スコープに従って復元されます。

ところで、CommonLispではlet動的スコープの変数をそれ自体で作成することもありません。declareそれを実現するには、変数を使用する必要がありますspecial(または、変数がすでに存在する場合は、変数の値を動的に再定義しますspecial)。

編集:完全を期すために、実際の動的変数の動作を実装するクラスをすばやく実装しました:http: //pastie.org/1700111

その出力は次のとおりです。

foo
bar
foo

編集2:ラッパークラスを必要とせずにインスタンス変数に対してこれを行う別の実装は次のとおりです:http://pastie.org/1706102

于 2011-03-22T11:37:03.647 に答える
2

記載されている問題は、演算子を使用して解決できます||(rubi で示されています)。

sumArrayでメソッドを呼び出すことにより、これをさらに簡素化できます。

account.transactions.all.sum {|t| t.amount|| 0 }

一方、グループ計算はRubyで行うべきではありません。DB はすべての面倒な作業を行う必要があります。

account.transactions.sum(:amount) # SELECT SUM(amount)
                                  # FROM   transactions
                                  # WHERE  account_id = account.id
于 2011-03-22T04:52:40.543 に答える