更改退货计划

Cas*_*roy 0 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
Run Code Online (Sandbox Code Playgroud)

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

Car*_*ten 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)
Run Code Online (Sandbox Code Playgroud)

这是明显的翻译

简单化

当然,双ifs有点过时了:

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)
Run Code Online (Sandbox Code Playgroud)

当然,许多人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 -> ()
Run Code Online (Sandbox Code Playgroud)

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

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

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)
Run Code Online (Sandbox Code Playgroud)

要考虑的事情

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

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

而不是做"1.20 - >给季度 - >给季度 - >给季度 - >给季度 - > ......"很容易看出你在1.20中使用mod/div以聪明的方式有4个季度:D