更改退货计划

时间:2014-09-18 03:05:36

标签: f# functional-programming

我的方法是使用命令式代码尝试问题,然后使用惯用功能代码再次尝试相同的问题。

这是我目前正在解决的问题:

  

更改退货计划 - 用户输入费用,然后输入金额。该计划将找出变化所需的季度,硬币,镍币,硬币的数量变化。

这是我天真(迫切)的解决方案:

let cost = 1.10m
let amountGiven = 2.00m
let mutable change = amountGiven - cost

while change <> 0m do
    if change >= 0.25m then
        Console.WriteLine "quater"
        change <- change - 0.25m
    else if change >= 0.10m then
        Console.WriteLine "dime"
        change <- change - 0.10m
    else if change >= 0.05m then
        Console.WriteLine "nickel"
        change <- change - 0.05m
    else if change >= 0.01m then
        Console.WriteLine "penny"
        change <- change - 0.01m

如何使用功能结构编写此代码(即没有mutable)?

1 个答案:

答案 0 :(得分:5)

在这种情况下,最简单的方法是将可变变量移动到函数参数中并使用递归(while只是对递归的暂停检查):

let cost = 1.10m
let amountGiven = 2.00m

let rec giveChange change =
    if change > 0 then
        if change >= 0.25m then
            Console.WriteLine "quater"
            giveChange (change - 0.25m)
        else if change >= 0.10m then
            Console.WriteLine "dime"
            giveChange (change - 0.10m)
        else if change >= 0.05m then
            Console.WriteLine "nickel"
            giveChange (change - 0.05m)
        else if change >= 0.01m then
            Console.WriteLine "penny"
            giveChange (change - 0.01m)
giveChange (amountGiven-cost)

这是明显的翻译

简化

当然,双if有点过时了:

let rec giveChange change =
    if change >= 0.25m then
        Console.WriteLine "quater"
        giveChange (change - 0.25m)
    else if change >= 0.10m then
        Console.WriteLine "dime"
        giveChange (change - 0.10m)
    else if change >= 0.05m then
        Console.WriteLine "nickel"
        giveChange (change - 0.05m)
    else if change >= 0.01m then
        Console.WriteLine "penny"
        giveChange (change - 0.01m)

当然,许多if看起来很糟糕 - 让我们用一个列表替换它们:

let coins = ["quarter", 0.25m; "dime", 0.10m; "nickel", 0.05m; "penny", 0.01m]
let rec giveChange change =
    match coins |> List.tryFind (fun (_,c) -> change >= c) with
    | Some (name,coin) ->
        Console.WriteLine name
        giveChange (change-coin)
    | None -> ()

我认为现在这是一个合理的解决方案。

没有一种情况永远不会发生,因为我们已经在那里获得了一分钱(或者我们希望如此,因为如果某人支付不够而且价值变为负值,我们就会遇到麻烦) - 也许我们应该给一些人更好的暗示:

let coins = ["quarter", 0.25m; "dime", 0.10m; "nickel", 0.05m; "penny", 0.01m]
let rec giveChange change =
    if change < 0.0m then failwith "cannot change negative amounts"
    match coins |> List.tryFind (fun (_,c) -> change >= c) with
    | Some (name,coin) ->
        Console.WriteLine name
        giveChange (change-coin)
    | None -> failwith ("cannot find a coin smaller than " + string change)

要考虑的事情

  • 你真的不想在那里做输出吗?
  • 这是某种贪心算法 - 但为什么必须通过重复减法进行除法

这些都会显着改变你的算法,所以我不会向你展示更多的代码,但你应该考虑第二个:

而不是做&#34; 1.20 - &gt;给四分之一 - &gt;给四分之一 - &gt;给四分之一 - &gt;给四分之一 - &gt; ...&#34;很容易看出你在1.20中使用mod / div以聪明的方式有4个季度:D