2011-05-04 39 views
0

我有這2個值平均分配資金數使用這個類到最高批數):在C#中使用功能的做法

public class Installment 
{ 
    public Installment(short installmentNumber, decimal amount) 
    { 
    InstallmentNumber = installmentNumber; 
    Amount = amount; 
    } 

    public short InstallmentNumber { get; private set; } 
    public decimal Amount { get; private set; } 
} 

分期付款應該如下:

{InstallmentNumber = 1,金額= 444.10米}
{InstallmentNumber = 2,金額=444.10米}
{InstallmentNumber = 3,金額=444.09米}

我正在尋找一個有趣的方式來創建我的3個分期付款。使用簡單的LINQ to objects方法會很好。最近我一直試圖更多地瞭解函數式編程,這似乎是遞歸中的一個相當不錯的練習。我能想到的唯一體面的方式是用傳統的方式或者爲了目前的循環...

+0

在功能的世界,這是「展開」 – Ankur 2011-05-04 05:38:42

回答

3

這裏沒有什麼是「功能性」的。我會處理這個問題是這樣的:

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()方法來構建一些東西,以將狀態從一次實例傳遞到另一個實例。

1

Talljoe, 我認爲你是在向正確的方向推動我。下面的代碼似乎工作。我不得不切換出一分錢的數學是如何工作的,但這個看起來還不錯(我認爲)

decimal totalAmountDue = 1332.29m; 
short installmentCount = 8; 

var pennies = (totalAmountDue * 100) % installmentCount; 
var monthlyPayment = Math.Floor(totalAmountDue/installmentCount * 100); 

var installments = from installmentNumber in Enumerable.Range(1, installmentCount) 
        let extraPenny = pennies-- > 0 ? 1 : 0 
        let amount = (monthlyPayment + extraPenny)/100 
        select new Installment(installmentNumber, amount); 
+0

呀,就一分錢數學良好的通話。 – Talljoe 2011-05-04 04:39:58