如何将两个函数调用合并为一个



我想合并以下行:

let result1 = add (numbers, ",")
let result2 = add (numbers, "n")

变成这样的东西:

let resultX = add (numbers, ",") |> add (numbers, "n")

我可以编写这样的函数吗?

注意:

正在学习 F#,如果这个问题看起来很愚蠢,我深表歉意。

代码如下:

module Calculator
open FsUnit
open NUnit.Framework
open System
let add (numbers:string) =
    let add (numbers:string) (delimiter:string) =
        if (numbers.Contains(delimiter)) then
            numbers.Split(delimiter.Chars(0)) |> Array.map Int32.Parse
                                              |> Array.sum
        else 0
    let result1 = add numbers ","
    let result2 = add numbers "n"
    if (result1 > 0 || result2 > 0) then 
        result1 + result2
    else let _ , result = numbers |> Int32.TryParse
         result

测试:

[<Test>]
let ``adding empty string returns zero`` () =
    let result = add ""
    result |> should equal 0
[<Test>]
let ``adding one number returns number`` () =
    let result = add "3"
    result |> should equal 3
[<Test>]
let ``add two numbers`` () =
    let result = add "3,4"
    result |> should equal 7
[<Test>]
let ``add three numbers`` () =
    let result = add "3,4,5"
    result |> should equal 12
[<Test>]
let ``line feeds embedded`` () =
    let result = add "3n4"
    result |> should equal 7

更新

我收到以下错误:

类型">

int"与类型"字符串"不匹配

let add (numbers:string) =
    let add (numbers:string) (delimiter:string) =
        if (numbers.Contains(delimiter)) then
            numbers.Split(delimiter.Chars(0)) |> Array.map Int32.Parse
                                              |> Array.sum
        else 0
let resultX = numbers |> add ","
                      |> add "n"

已实施的反馈:

let add (numbers:string) =
    let add (numbers:string) (delimiters:char array) =
        if numbers.Length = 0 then 0
        else numbers.Split(delimiters) |> Array.map Int32.Parse
                                       |> Array.sum
    let delimiters = [|',';'n'|]
    add numbers delimiters

这不是一个确切的答案,因为我不确定你的意思,但它应该给你一些想法。

let add01 (numbers:string) =
    let delimiters : char array = [|',';'n'|]
    let inputArray : string array = numbers.Split(delimiters)
    let numbers : string list = Array.toList(inputArray)
    let rec add (numbers : string list) (total : int) : int =
        match (numbers : string list) with
        | ""::t ->
            add t total
        | h::t -> 
            let number =  System.Int32.Parse h
            let total = total + number
            add t total
        | [] -> total        
    add numbers 0
let numbers = "1,2,3n4,5,6nn"
let result = add01 numbers

当给定以下代码时,会发生以下错误,为什么?

// Type mismatch. Expecting a
//     int -> 'a    
// but given a
//     string -> int    
// The type 'int' does not match the type 'string'
let result = numbers |> add ","
                     |> add "n"

由于这是一个错误,指出两个类型不一致,因此需要了解类型推断以及如何解决此类问题。我不会在这里解释类型推断,因为这本身就是一个很大的话题,但是我将举一个模式的例子,该模式在大多数时间都成功地解决了此类错误。

当 F# 编译代码时,它使用类型推断在执行类型检查之前将缺少的类型添加到函数和值,并且类型检查失败。因此,要查看编译器看到的类型,我们将在此处手动添加它们,并分解出不会导致问题的代码部分,使我们希望在某些事情中找出错误的原因,然后变得显而易见。

唯一具有类型的内容是:

  • 结果
  • =
  • 数字
  • |>
  • ","
  • ""

值的类型很简单:

  • 结果 : 整数
  • 数字:字符串
  • ",":字符串
  • "" : 字符串

我不记得 F# 将等于 (=( 视为函数,但这里是如何思考它。
= : 'a -> 'a

管道操作员

let (|>) (x : 'a) f = f (x : 'a)  

为了解决这个问题,只需将管道运算符视为语法糖。
请参阅以下示例以更好地理解。

添加函数
添加 : 字符串 -> 字符串 -> int

因此,让我们将错误细化为本质。

//Type mismatch. Expecting a
//    int -> 'a    
//but given a
//    string -> int    
//The type 'int' does not match the type 'string'
let result = numbers |> add ","
                     |> add "n"

将类型签名添加到值,并验证我们收到相同的错误。这就是类型推断的作用,我们手动完成了。

//Type mismatch. Expecting a
//    int -> int    
//but given a
//    string -> int    
//The type 'int' does not match the type 'string'
let (result : int) = (numbers : string) |> add ("," : string) 
                     |> add ("n" : string)

现在将代码视为可以分解的数学表达式。

分解出第一个管道运算符并验证我们得到相同的错误。请注意,错误现在只是 r2 的一部分

//Expecting a
//    int -> 'a    
//but given a
//    string -> int    
//The type 'int' does not match the type 'string'
let (result : int) = 
    let r1 = (numbers : string) |> add ("," : string) 
    let r2 = r1 |> add ("n" : string)
    r2

撤消第二个管道运算符的语法糖,并验证我们得到相同的错误。请注意,错误现在只是 r2 的一部分;特别是 R1 参数

//This expression was expected to have type
//    string    
//but here has type
//    int
let (result : int) = 
    let r1 = (numbers : string) |> add ("," : string) 
    let r2 = add ("n" : string) r1
    r2

将类型添加到 r1 并验证我们收到相同的错误。

//This expression was expected to have type
//    string    
//but here has type
//    int   
let (result : int) = 
    let (r1 : int) = (numbers : string) |> add ("," : string) 
    let r2 = add ("n" : string) r1
    r2

此时,错误应该是显而易见的。第一个管道运算符的结果是一个int,并作为第二个参数传递给 add 函数。add 函数需要第二个参数的string,但被赋予了int


为了更好地理解管道运算符的工作原理,我为此演示创建了一个等效的用户定义运算符。

这些是演示的一些帮助程序函数。

let output1 w =
    printfn "1: %A" w
let output2 w x =
    printfn "1: %A 2: %A" w x
let output3 w x y =
    printfn "1: %A 2: %A 3: %A" w x y
let output4 w x y z =
    printfn "1: %A 2: %A 3: %A 4: %A" w x y z

在没有管道运算符的情况下使用输出函数。

output1 "a"  
1: "a"  
output2 "a" "b"  
1: "a" 2: "b"  
output3 "a" "b" "c"  
1: "a" 2: "b" 3: "c"  
output4 "a" "b" "c" "d"  
1: "a" 2: "b" 3: "c" 4: "d"  

请注意,输出与输入的顺序相同。

将输出函数与管道运算符一起使用。

设 (|>( x f = fx

"a" |> output1  
1: "a"  
"a" |> output2 "b"  
1: "b" 2: "a"  
"a" |> output3 "b" "c"  
1: "b" 2: "c" 3: "a"  
"a" |> output4 "b" "c" "d"  
1: "b" 2: "c" 3: "d" 4: "a"  

请注意,输出函数的最后一个参数是管道运算符 ("a"( 左侧的值,因为使用了管道运算符 (|>(。

有关如何定义用户定义的运算符,请参阅 F# 规范的第 3.7 节。

将输出函数与用户定义的管道运算符一起使用。

let (@.) x f = f x  
"a" @. output1  
1: "a"  
"a" @. output2 "b"  
1: "b" 2: "a"  
"a" @. output3 "b" "c"  
1: "b" 2: "c" 3: "a"  
"a" @. output4 "b" "c" "d"  
1: "b" 2: "c" 3: "d" 4: "a"  

我不知道有什么通用的方式来组合函数,就像你问的那样,但是如果你只需要改变一个参数,一种选择是创建一个参数列表,然后映射这些参数:

let results = [","; "n"] |> List.map (add numbers)

如果这样做,则results是一个int list,然后您需要决定如何处理该列表。在这种情况下,对列表求和似乎是合适的,但考虑到当前检查result1result2是否为正的条件,这似乎不合适。


综上所述,鉴于当前提供的测试用例,没有理由使其比必须的更复杂。此实现还通过了所有测试:

let add =
    let split (x : string) =
        x.Split([| ','; 'n' |], StringSplitOptions.RemoveEmptyEntries)
    split >> Array.map Int32.Parse >> Array.sum

这不是一个特别健壮的实现,因为如果字符串包含无法解析为整数的字符,它将失败,但 OP 实现也会失败。

最新更新