2013-10-31 101 views
1

我正在理解Rubymonk編碼練習解決方案,並且無法理解cost方法中發生了什麼。Ruby編碼練習解決方案(rubymonk)

僅供參考,menu{:rice => 3, :noodles => 2},其目的是計算從menu訂單的總成本。

的命令的例子是:

{:rice => 1, :noodles => 1}) 

我想出瞭解決的辦法是簡單的,至少在我的頭上,但返回「無法轉換符號爲Integer」這我無法糾正錯誤通過to_i

class Restaurant 
    def initialize(menu) 
    @menu = menu 
    end 

    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 
end 

有人可以簡單地解釋一下cost方法中的每一步嗎?

+0

查找施帕爾特運營商,並期待在使用注射功能的一些示例。 – hirolau

+0

@hirolau,'s/spalt/splat /'。 –

+0

如果您對任何答案感到滿意,您應該檢查對您最有幫助的答案。 –

回答

4

考慮到total cost正在計算,看起來@menu包含單位價格(通常會發現,除了可能在最好的餐館),每個訂單包含每個訂購的菜單項的數量。假設:

@menu = {rice: 0.69, noodles: 0.89} 

其中的值是單位價格和orders元素看起來是這樣的:

{rice: 3, noodles: 2} 

其中的值是訂購數量。供應此訂單給出的數量的成本爲:

(3)(0.69) + (2)(0.89) = 3.95 

您將對所有訂單彙總此成本。

首先,讓我們寫這樣的方法,

def cost(*orders) 
    orders.inject(0) do |total_cost, order| 
    total_cost + order.keys.inject(0) do |cost, key| 
     cost + order[key] * @menu[key] 
    end 
    end 
end 

澄清其結構。 inject(又名reduce)正在迭代orders並在變量total_cost中累計值。您可以通過將參數傳遞給inject來分配total_cost初始值(如您所做的那樣)。如果您不給inject參數初始值,則將total_cost設置爲等於跟在inject後面的塊中的第一個評估值。在這種情況下,如果您將參數刪除到inject,則會得到相同的結果。

對於orders第一值(塊變量order),下面的號碼添加到累加器total_cost

order.keys.inject(0) do |cost, key| 
    cost + @menu[key] * order[key] 
end 

要獲得此值,紅寶石必須執行側計算。假設@menuorder具有上面給出的值。

inject(0)通過order.keys(它是[:rice, :noodles])迭代,使用cost作爲其累加器。該塊被用於:rice,然後執行用於noodles

cost + order[:rice] * @menu[:rice] => 0 + 3 * 0.69 # => 2.07 => cost 
    cost + order[:noodles] * @menu[:noodles] => 2.07 + 2 * 0.89 # => 3.95 => cost 

這樣就完成了計算側,所以3.95被添加到外累加器total_cost(其先前等於零)。然後orders的下一個元素由外部inject處理,依此類推。

0

首先,瞭解紅寶石的Enumerable inject是如何工作的。 「ruby inject and the Mandelbrot set」是一篇介紹性文章。

基於這一認識,我們看到這樣的代碼:

order.keys.inject(0) {|cost, key| cost + @menu[key]*order[key] } 

被簡單地返回@menu[key]*order[key]key迭代order.keys所有值的總和給每個訂單的總成本。

最後,外循環orders.inject(0) do |total_cost, order| ...循環遍歷orders列表中每個訂單的成本,以返回所有訂單的總成本。

+0

馬特,感謝您的信息。這篇文章是一個很好的閱讀,儘管它在閱讀之後並沒有爲我立即聚集在一起,但我現在能夠掌握和理解在這種情況下注入的內容。我認爲作爲初學者的問題的一部分無意中集中在這些通用練習中的變量名稱上,而不是這些方法實際上如何工作,導致與正在發生的事情混淆......如果這是有道理的! – John

0

在您的文章中定義cost的關鍵明顯是inject方法。 inject方法也可以調用爲reduce,這對於許多英語用戶來說是一個更明智的名稱,因爲它需要一個列表並將其減少爲單個值。 (只是在功能編程的文獻中進一步混淆了事物,這個函數幾乎總是被稱爲「摺疊」)。

有很多例子;考慮找到整數列表的總和:

[1,2,3,4,5].inject(0) {|sum, num| return sum + num} #=> 15 

那麼這裏發生了什麼?塊的第一個參數是運行結果 - 在這種情況下的部分總和。它以任何你通過的參數inject(在上面的例子中爲0)開始。

該塊在列表中每個項目調用一次,當前項目成爲第二個參數。塊返回的值成爲塊的下一次迭代的運行值(第一個參數)。

因此,如果我們擴大上述注入更加明確必要的代碼,我們得到這樣的:

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 

首先,它採取您可以在Ruby中取消return的優勢;塊中最後一個表達式的值是該塊的返回值。

這兩個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] 
    } 
}