2014-04-04 72 views
-1
let rec calcTotalMonths ~moneyOwed:moneyOwed ~interestRate:interestRate ~monthlyPayment:monthlyPayment ~months:months=0 = ( 
    let newBalance = (moneyOwed -. monthlyPayment) *. interestRate in 
    match newBalance <= 0. with 
    true -> months 
    | false -> calcTotalMonths newBalance interestRate monthlyPayment months+1 
);; 

我想知道为什么我一直得到不明白返回类型

File "budget.ml", line 19, characters 12-73: 
Error: This expression has type 
     moneyOwed:float -> 
     interestRate:float -> monthlyPayment:float -> months:'a -> 'b 

为什么几个月类型“一当它清楚地使用‘+’操作符上几个月。它也会在递归的基本情况下返回几个月,所以它为什么是'b'作为返回类型。

编辑: 做杰夫的建议

File "budget.ml", line 19, characters 12-77: 
Error: This expression has type 
     moneyOwed:float -> 
     interestRate:float -> monthlyPayment:float -> months:int -> 'a 
     but an expression was expected of type int 

回答

2

移动可选参数在调用点前和使用标签:

let rec calcTotalMonths ?(months=0) ~moneyOwed ~interestRate ~monthlyPayment = 
    let newBalance = (moneyOwed -. monthlyPayment) *. interestRate in 
    if newBalance <= 0.0 then 
    months 
    else 
    calcTotalMonths 
     ~months:(months + 1) 
     ~moneyOwed:newBalance 
     ~interestRate 
     ~monthlyPayment 

注意,这里使用了一些简写标记参数:~argname:argname可以只用~argname所取代。将可选参数从最后一个位置移开是为了避免标记参数系统的不幸的一面:有关详细信息,请参见the manual's description of optional arguments

看着这个,我觉得你只打算使用calcTotalMonths内的months参数,并且该参数的可选性存在将它从外部调用者隐藏的可能性。如果是这样,这不是很好的风格。一个更好的做法是嵌套函数来处理循环:

let calcTotalMonths ~moneyOwed ~interestRate ~monthlyPayment = 
    let rec loop months balance = 
    let newBalance = (balance -. monthlyPayment) *. interestRate in 
    if newBalance <= 0.0 then months 
    else loop (months + 1) newBalance in 
    loop 0 moneyOwed 

(忽略,如果你真的需要的参数。)

3

你不适用+到几个月后,你把它应用到的calcTotalMonths结果。函数应用比中缀运算符具有更高的优先级。

(calcTotalMonths newBalance interestRate monthlyPayment months) + 1 

你应该这样写::

,如果你写这个表达式解释

calcTotalMonths newBalance interestRate monthlyPayment (months + 1) 

更新

它看起来像你想months是一个可选参数的默认值为0.定义方式如下所示:

? (months = 0) 

注意:拖尾可选参数不是一个好的形式,因为它会导致部分应用函数的问题。

更新2

最后,如果你想调用已标记的论点,但没有提供标签,你只能提供非可选参数的函数。这对您不起作用,因为您需要在递归调用中提供月份。如果你在递归调用中提供了所有的标签,我认为事情会起作用。

更新3

对于它的价值(适量),这里是我会怎么写这个功能。除非他们正在解决严重的问题,否则我不使用标记或可选参数。另外,我觉得一个辅助功能是一种更好的方式来处理迭代次数(月):(我只注意到GSG还展示了如何使用一个辅助功能)

let monthsUntilPaid owed interest payment = 
    let rec go balance months = 
     let balance' = (balance -. payment) *. interest in 
     if balance' <= 0.0 then months else go balance' (months + 1) 
    in 
    go owed 0 

+1

所以这次修复上个类型的问题,但对于整个返回类型的功能? –