あなたの投稿の定義の鍵cost
は明らかにinject
方法です。このinject
メソッドは としても呼び出すことができreduce
ます。これは、リストを取り、それを 1 つの値に縮小するため、多くの英語話者にとってより適切な名前です。(さらに混乱させるために、関数型プログラミングの文献では、この関数はほとんどの場合「fold」と呼ばれます)。
多くの例があります。整数のリストの合計を見つけることを検討してください。
[1,2,3,4,5].inject(0) {|sum, num| return sum + num} #=> 15
それで、ここで何が起こっているのですか?ブロックの最初の引数は、実行結果 (この場合は部分和) です。に引数として渡すものとして開始されますinject
。上記の例では 0 です。
ブロックはリスト内の項目ごとに 1 回呼び出され、現在の項目が 2 番目の引数になります。ブロックによって返される値は、ブロックの次の反復に対する実行中の値 (最初の引数) になります。
したがって、上記のインジェクションをより明示的な命令コードに拡張すると、次のようになります。
def block(sum, num)
return sum + num
end
result = 0
for i in [1,2,3,4,5]
result = block(result, i)
end
この知識を武器に、次のことに取り組みましょうcost
。
def cost(*orders)
orders.inject(0) do |total_cost, order|
total_cost + order.keys.inject(0) {|cost, key| cost + @menu[key]*order[key] }
end
end
return
まず、 Ruby では省略できるという事実を利用しています。ブロック内の最後の式の値は、ブロックの戻り値です。
どちらinject
の呼び出しも、上記の例によく似ています。単純な合計ループです。外側inject
は個々の注文すべての総計を作成しますが、これらの注文は数値ではなくマップであるため、それらを合計する前に各注文のコストを取得するためにさらに多くの作業を行う必要があります。その「もっと仕事を」というのが内なるinject
呼びかけです。
order.keys.inject(0) {|cost, key| cost + @menu[key]*order[key] }
上記の拡張を使用すると、これがどのように機能するかがわかります。メニューに従って、注文の各値 (アイテムの数量) にそのアイテムの価格 (キー) を掛けた結果を加算するだけです。
ちなみに、値だけではなくキーと値のペアを削減することで、ブロック内の順序マップでキーを検索する必要がなくなります。inject
/に初期値を渡さない場合reduce
、デフォルトでゼロになるという事実を利用することもできます。
orders.inject { |grand_total, order|
grand_total + order.inject { |subtotal, line_item|
item, quantity = line_item
subtotal + quantity * @menu[item]
}
}