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

时间:2016-01-07 13:07:08

标签: f#

我想巩固以下几行:

user

这样的事情:

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

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

注意:

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

代码如下:

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

试验:

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

已更新

我收到以下错误:

  

'int'类型与'string'类型

不匹配
[<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) (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"

2 个答案:

答案 0 :(得分:3)

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

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,3\n4,5,6\n\n"
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"

由于这是一个错误,表明两种类型不同意,需要了解type inferencing以及如何解决此类问题。 我不会在这里解释类型推理,因为这本身就是一个很大的主题,但是我将举例说明一种模式,它可以在大部分时间成功地解决这些错误。

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

唯一有类型的东西是:

  • 结果
  • =
  • 数字
  • |&GT;
  • 添加
  • “,”
  • “\ n”

值的类型很简单:

  • 结果:int
  • 数字:字符串
  • “,”:string
  • “\ n”:string

我不记得F#将equals(=)视为一种功能,但这里是如何思考它   =:'a - &gt; “一

管道运营商

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

要解决问题,只需将管道运算符视为syntactic sugar   请参阅下面的示例以便更好地理解。

添加功能
    add:string - &gt; string - &gt; 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"  

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

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

// let(|&gt;)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”),因为使用了管道运算符(|&gt;)。

//参见F# specification的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"  

答案 1 :(得分:3)

我不知道编写函数的任何通用方法,就像你似乎在问,但如果你只需要改变一个参数,一个选项是创建一个参数列表,然后映射那些:

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

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

所有这一切都说,鉴于目前提供的测试用例,没有理由让它变得更加复杂。此实现还通过了所有测试:

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

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