【问题标题】:Taking two elements from an array and adding them, then adding them back into the array从数组中取出两个元素并添加它们,然后将它们添加回数组
【发布时间】:2016-08-18 03:46:11
【问题描述】:

我试图在 f# 中使用两个数组创建一个计算器,一个用于存储数字,另一个用于存储运算符符号。我需要符号数组与符号进行模式匹配,并根据运算符从数组中获取前两个元素并执行操作并将新数字添加到第二个数组的头部。

open System

[<EntryPoint>]
let main argv = 
printfn "%A" argv

let Add x y = x + y
let Sub x y = x - y
let Div x y = x * y
let Mul x y = x / y

printfn "1 > Calculator \n2 > Load from txt file"

let chosenIn = Console.ReadLine();

//This is where I need to do the operation and after call the sum function 
//again until there's only one element left in the number array
let rec sum num (numArray : int[]) sym (symArray : string[]) () =



let rec calc () = 
    printfn "Enter Sum"
    let input = Console.ReadLine()
    let intInput = input.Split()
    let numArray = [|for num in intInput do
                        let v , vp = System.Int32.TryParse(num)
                        if v then yield vp|]

    let symbolArray = [|for symbol in intInput do
                        match symbol with 
                        | "+" -> yield symbol
                        | "-" -> yield symbol
                        | "/" -> yield symbol
                        | "*" -> yield symbol
                        | _ -> ignore 0|]

    calc()

match chosenIn with
| "1" -> calc()
| "2" -> printfn "File"
| _ -> printfn "Invalid"

0 // return an integer exit code

【问题讨论】:

  • 这个问题定义不明确,少数代码片段对澄清问题可能是什么帮助不大。当前代码接受“1 2 3 4”作为输入,就像它接受“+ - /”或“1 2 / + 3 * *”一样,请说明在这些情况下的预期行为是什么。
  • 其中一个主要问题是对于+-*,签名是int -&gt; int -&gt; int,但对于/,它可以是int -&gt; int -&gt; intint -&gt; int -&gt; float这就是为什么如果您查看某些示例,您会看到答案省略了/。此外,输入应该与运算符和操作数组合到一个堆栈中,以便可以使用reverse polish notation
  • 我希望用户输入一个简单的和,例如“4 + 2”,然后应用程序将字符串解析为输入字符串数组,然后将输入字符串数组解析为两个数组,一个用于数字和符号中的一个,例如 numArray = [| 4; 2;|] 符号数组 = [| “+”|]。然后调用以两个数组作为参数的 sum 函数。该函数将查看符号数组中的第一个符号,并根据该符号确定对 num 数组中的前两个元素使用的运算符。并返回一个新的 num 数组,其中包含新计算的数字。
  • 为什么要为数字和运算符创建一个数组?例如,这会让每个人都认为您还需要处理具有 >1 个运算符的情况。您是否期望像“number op number”这样的输入,或者还有其他的输入吗?
  • 即使用户输入了多个运算符,例如“5 + 3 - 1”,sum 函数也是递归的,这意味着它会调用不止一次。例如 "5 + 3 - 1" -> [5 ,3, 1] 和 [ "+", "-"] 并且在第一次调用之后它将创建两个新数组 [8, 1] 和 ["-]。程序将看到仍然有两个数字,并将再次运行创建只剩下一个数字,这将是结果

标签: arrays recursion f# pattern-matching calculator


【解决方案1】:

为了回应@Liam Donnelly 对他自己的问题的回答:我将抛开诸如“这真的是解决问题的最佳方法”之类的问题,而只是评论如何更好地编写您当前拥有的代码。

你在这里做的数组切片和连接方式可以写成

let newNumArray = Array.append [| result |] numArray.[2..]

但是,对于您的任务,我会使用 F# 列表而不是数组。使用列表,您可以进行模式匹配以访问前 2 个元素。在我看来,模式匹配胜过直接索引,因为您可以直接编码极端情况,并让 F# 编译器提醒您极端情况。为操作员做同样的事情。您可以同时执行运算符和操作数。然后它看起来像这样:

let rec sum2 (numArray : int list) (symArray : string list)  =
    let newNum, newSym = 
        match numArray with
        | [] -> failwith "No numbers left to process"
        | arg1 :: [] -> failwith "There's only one number left to process"
        | arg1 :: arg2 :: args -> 
            match symArray with
            | op1 :: ops ->
                let result = 
                    match op1 with
                    | "+" -> Add arg1 arg2
                    | "-" -> Sub arg1 arg2
                    | "*" -> Mul arg1 arg2
                    | _ -> failwithf "Operator not recognized: '%s'" op1
                // Return the result, concatenate the non-processed 
                // numbers. Return the non-processed operators
                result :: args, ops
            | _ -> failwith "I've run out of operators?"
<snip>

此外,如果您不认识运算符,则返回“默认结果”是我认为非常冒险的事情(尽管这种做法相当普遍)

如果您使用列表(即 F# 列表),则可以通过 head: let newSymArray = symArray.Head 或使用 List.head 直接访问索引 1.. 处的元素

每次看到自己在 F# 中编写 for 循环时,请退后一步。它们编写起来很麻烦并且容易出错。大多数典型的循环用例都包含在 F# 库函数中,因此请仔细阅读这些用例。你的打印循环可以写得更短:

newNumArray
|> Seq.iter (printfn "%i")

【讨论】:

    【解决方案2】:

    我已经设法创建了执行我需要执行的任务的函数,并且我确信通过使用带有过滤器的 Array.copy 来获取数组的第一个元素,有一种更高效的代码方式,但我我是 f# 的新手,所以我只是按照我自信的方式去做

    let rec sum (numArray : int[]) (symArray : string[])  =
        let result = match symArray.[0] with
                     | "+" -> Add numArray.[0] numArray.[1]
                     | "-" -> Sub numArray.[0] numArray.[1]
                     | "*" -> Mul numArray.[0] numArray.[1]
                     | _ -> 0
    
        let newNumArray = [|
                            for i = 0 to numArray.Length - 1 do
                                if i = 0 then yield result
                                if i > 1 then yield numArray.[i]|]
    
        let newSymArray = [|
                            for i = 0 to symArray.Length - 1 do
                                if i > 0 then yield symArray.[i]|]
    
        if newNumArray.Length > 1 then
            sum newNumArray newSymArray
        else
            for i = 0 to newNumArray.Length - 1 do
                printfn "%i" (newNumArray.[i])
    

    【讨论】:

    • 您的解决方案仍然没有很好地利用 F#。我在下面添加了替代答案和 cmets。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多