不懂回归类型

时间:2014-04-04 20:50:13

标签: ocaml

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

为什么月份是一种类型&a;当它明显使用&#39; +&#39;几个月的运营商。它也会在递归的基本情况下返回几个月,那么为什么它是一个&#39; 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 个答案:

答案 0 :(得分:3)

您未将+应用于数月,而是将其应用于calcTotalMonths的结果。函数应用程序的优先级高于中缀运算符。

表达式被解释为你写了这个:

(calcTotalMonths newBalance interestRate monthlyPayment months) + 1

你应该这样写:

calcTotalMonths newBalance interestRate monthlyPayment (months + 1)

<强>更新

看起来你希望months是一个可选参数,默认值为0.定义它的方法是这样的:

? (months = 0)

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

更新2

最后,如果要调用带有标记参数的函数,但不提供标签,则只能提供非可选参数。这不适合您,因为您需要在递归调用中提供月份。如果您在递归调用中提供所有标签,我认为事情会有效。

更新3

对于它的价值(适量),这里我将如何写这个功能。除非他们正在解决严重问题,否则我不会使用带标签或可选参数。另外,我认为辅助函数是处理迭代计数(几个月)的更好方法:

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

(我刚注意到gsg也展示了如何使用辅助函数。)

答案 1 :(得分: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

看一下这个,我感觉你只打算在months中使用calcTotalMonths参数的可能性,并且存在参数的可选性以将其隐藏在外部调用者之外。如果是这样,这不是好风格。更好的方法是处理循环的嵌套函数:

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

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