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

F# 如何将两个函数调用合并为一个?,f#,F#,我想综合以下几点: 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 Sy

我想综合以下几点:

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 "3\n4"
    result |> should equal 7
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
测试:

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 "3\n4"
    result |> should equal 7
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
实施的反馈:

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 "3\n4"
    result |> should equal 7
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(数字:字符串)=
let分隔符:字符数组=[|',';'\n'|]
让inputArray:string数组=数字。拆分(分隔符)
let number:string list=Array.toList(inputArray)
让rec添加(数字:字符串列表)(总数:int):int=
将(数字:字符串列表)与
|“”:t->
合计
|h::t->
设number=System.Int32.h
让总数=总数+数字
合计
|[]->总计
添加数字0
让number=“1,2,3\n4,5,6\n\n”
让结果=add01个数字
当给出以下代码时,会发生以下错误,为什么

// 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#编译代码时,在进行类型检查之前,它使用类型推断将缺少的类型添加到函数和值中,而类型检查失败。因此,为了查看编译器对类型的看法,我们将在这里手动添加它们,并找出代码中没有引起问题的部分,从而为我们留下错误的原因,希望在某些方面可以得到明显的修复

唯一具有类型的是:

  • 结果
  • =
  • 数字
  • |>
  • “,”
  • “\n”
值的类型很简单:

  • 结果:int
  • 数字:字符串
  • “,”:字符串
  • “\n”:字符串
我不记得F#把相等(=)当作一个函数,但这里是如何看待它的。
=:“a->”a

管道运营商

let (|>) (x : 'a) f = f (x : 'a)  
要解决此问题,只需将管道操作员视为。
请参阅下面的示例以更好地理解

添加功能
添加:字符串->字符串->整数

因此,让我们将错误细化到其本质

//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函数要求第二个参数使用
字符串
,但得到了
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
这些是演示的一些辅助函数

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"  
请注意,输出与输入的顺序相同

将输出函数与管道操作符一起使用

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"  
//设(|>)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”)左侧的值,因为使用了管道运算符(|>)

//有关如何定义用户定义的运算符,请参见的第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)
如果执行此操作,则
结果
是一个
int列表
,然后需要决定如何处理该列表。在这种情况下,在列表上求和似乎是合适的,但考虑到检查
result1
result2
是否为正的当前条件,这似乎不合适


综上所述,考虑到当前提供的测试用例,没有理由让它变得更加复杂。此实现还通过了所有测试:

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

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

是否要求每个分隔符都单独调用add函数?你是想学习函数的组合,还是只想对字符串求和?如果您试图对字符串求和,我建议您执行一次调用,将所有分隔符作为数组传递给该方法。我们应该将您关于“类型'int'与类型'string'不匹配”的问题作为单独的问题。如果你能问,我可以把答案的那一部分移到它上面。这确实是两个问题,另一部分需要更容易找到。谢谢马克。你刚刚在我的逻辑中发现了一个关于大于零的错误。你回答了我问题的意图。我不确定你学习的重点是什么,但很高兴你今天学到了一些东西。花点时间阅读扩展的答案,你会定期回来的。