这不是一个确切的答案,因为我不确定你的意思,但它应该给你一些想法。
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"
由于这是一个错误,表明两种类型不同意,因此需要理解类型推断 https://en.wikipedia.org/wiki/Type_inference以及如何解决此类问题。
我不会在这里解释类型推断,因为这本身就是一个很大的主题,但是我将给出一个在大多数情况下成功解决此类错误的模式示例。
当 F# 编译代码时,它会在执行类型检查之前使用类型推断将缺少的类型添加到函数和值中,而失败的正是类型检查。因此,为了查看编译器对类型的看法,我们将在此处手动添加它们,并剔除代码中不会导致问题的部分,从而让我们知道错误的原因,然后希望在某些事情中变得明显可以修复。
唯一具有类型的东西是:
- result
- =
- 数字
- |>
- add
- ","
- "\n"
值的类型很简单:
- 结果:整数
- 数字:字符串
- “,“ : 细绳
- “\n”:字符串
我不记得 F# 将 equals (=) 视为函数,但这里是如何看待它的。
= : 'a -> 'a
管道运营商
let (|>) (x : 'a) f = f (x : 'a)
为了解决这个问题,只需将管道运算符视为句法糖 https://en.wikipedia.org/wiki/Syntactic_sugar.
请参阅下面的示例以更好地理解。
添加功能
添加:字符串 -> 字符串 -> 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”) 左侧的值。
// 参见第 3.7 节F#规范 http://fsharp.org/specs/language-spec/4.0/FSharpSpec-4.0-latest.pdf关于如何定义用户定义的运算符。
将输出函数与用户定义的管道运算符一起使用。
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"