這裏沒有什麼是「功能性」的。我會處理這個問題是這樣的:
var pennies = (totalAmountDue * 100) % installmentCount;
var monthlyPayment = totalAmountDue/installmentCount;
var installments = from installment in Enumerable.Range(1, installmentCount)
let amount = monthlyPayment + (Math.Max(pennies--, 0m)/100)
select new Installment(installment, amount);
您可能能夠些辦法,你不斷地減去總量先前支付和做除法舍入到最接近的一分錢。在F#(C#是這個太羅嗦了),它可能是這樣的:
let calculatePayments totalAmountDue installmentCount =
let rec getPayments l (amountLeft:decimal) = function
| 0 -> l
| count -> let paymentAmount =
(truncate (amountLeft/(decimal)count * 100m))/100m
getPayments (new Installment(count, paymentAmount)::l)
(amountLeft - paymentAmount)
(count - 1)
getPayments [] totalAmountDue installmentCount
對於那些不熟悉F#,這是什麼代碼正在做的是建立一個遞歸函數(getPayments
),並與一些自舉其初始值(空列表,起始值)。使用match expressions它建立一個終止符(如果installmentCount
爲0),返回列表到目前爲止。否則,它會計算付款金額,並調用遞歸方法,將新的批次添加到列表的前面,從剩餘金額中減去付款金額,然後減去計數。
這實際上是建立反向列表(每次加入前面),所以我們扔掉額外的便士(truncate
),最終它趕上了我們,所以一分錢四捨五入按預期工作。這顯然比上面的加/減代碼更密集,因爲我們在每次迭代中進行分割和相乘。但它是完全遞歸的,並利用尾遞歸,所以我們永遠不會用完堆棧。
這裏C#的麻煩在於,你需要一系列的分期付款和遞歸,並且沒有用C#做這種做法的慣用的內置結構。在這裏,我使用了F#的不可變的列表和O(1)操作來預先定義。
您可以使用Reactive Extensions中的Scan()
方法來構建一些東西,以將狀態從一次實例傳遞到另一個實例。
在功能的世界,這是「展開」 – Ankur 2011-05-04 05:38:42