我有这两个值:
decimal totalAmountDue = 1332.29m;
short installmentCount = 3;
我想使用此类创建3个分期付款,这些分期付款基于totalAmountDue(额外的便士适用从最低分期付款编号开始到最高分期付款编号):
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,Amount = 444.10m}
{InstallmentNumber = 2,金额= 444.10m}
{InstallmentNumber = 3,Amount = 444.09m}
我正在寻找一种有趣的方式来创建我的3期。使用简单的LINQ to objects方法会很好。我最近一直试图更多地了解函数式编程,这似乎是一个相当不错的递归练习。我能想到的唯一正确的方法是使用传统的while或for循环...
答案 0 :(得分:4)
这里没有很多“功能性”。我会像这样解决问题:
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 :(得分: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);