我在很多情况下都遇到了F#的困难。我相信我没有掌握一些基本概念。我希望有人可以追踪我的推理并弄清楚我可能遗失的事情(可能是很多)。
说我正在使用Xunit。我想要做的是,提供两个列表,成对应用Assert.Equal
方法。例如:
Open Xunit
let test1 = [1;2;3]
let test2 = [1;2;4]
List.map2 Assert.Equal test1 test2
编译器抱怨函数Equal
不带一个参数。据我所知,不应该map2
提供2个参数吗?
作为一个完整性检查,我在f#immediate中使用以下代码:
let doequal = fun x y -> printf "result: %b\n" (x = y)
let test1 = [1;2;3]
let test2 = [1;2;4]
List.map2 doequal test1 test2;;
这似乎完全相同。 doequal
是一个lambda,它使用两个通用参数并返回 unit 。 List.map2
将每个参数成对地交给lambda,我得到了我预期的输出:
result: true
result: true
result: false
那是什么给出的?来源展示Xunit.Equal
已签名public static void Equal<T>(T expected, T actual)
。为什么我的参数不会映射到方法签名上?
编辑一个 我认为两个变量x和y与一个元组(x,y)可以互换地构造和解构。所以我尝试了两个选项,得到了不同的结果。似乎第二个可能比第一个更进一步。
List.map2 Assert.Equal(test1, test2)
编译器现在抱怨“连续参数”应该是分隔空格或者是tupled&#39;
List.map2(Assert.Equal(test1, test2))
编译器现在抱怨“无法确定一个独特的重载方法......可能需要一个类型注释&#39;
答案 0 :(得分:7)
我认为问题的一部分来自混合方法(OO风格)和功能(FP风格)。
F#编译器尝试处理这两种方法,但偶尔需要一些帮助。
一种方法是使用FP函数“包装”OO方法。
// wrap method call with function
let assertEqual x y = Assert.Equal(x,y)
// all FP-style functions
List.map2 assertEqual test1 test2
如果不创建辅助函数,在使用lambda调用“inline”方法时,通常需要将多个函数参数转换为一个元组:
List.map2 (fun x y -> Assert.Equal(x,y)) test1 test2
当您在一行中混合方法和函数时,通常会得到“连续参数应该分开”错误。
printfn "%s" "hello".ToUpper()
// Error: Successive arguments should be separated
// by spaces or tupled
这告诉你编译器遇到了问题,需要一些帮助!
你可以通过方法调用附加额外的问题来解决这个问题:
printfn "%s" ("hello".ToUpper()) // ok
或者有时,使用反向管道:
printfn "%s" <| "hello".ToUpper() // ok
包装方法通常值得无论如何,以便您可以交换参数以使其更适合部分应用:
// wrap method call with function AND swap params
let contains searchFor (s:string) = s.Contains(searchFor)
// all FP-style functions
["a"; "b"; "c"]
|> List.filter (contains "a")
请注意,在最后一行中,我必须使用parens优先于contains "a"
而不是List.filter
答案 1 :(得分:5)
public static void Equal<T>(T expected, T actual)
不带两个参数 - 它需要一个参数,这是一个带有两个元素的元组:(T expected, T actual)
。
请改为尝试:
List.map2 Assert.Equal(test1, test2)
答案 2 :(得分:2)
所有类型签名都在那里。
Assert.Equals
的签名与'a * 'a -> unit
类似。 List.map2
需要'a -> 'b -> 'c
。
他们只是不适合。
List.map2 (fun x y -> Assert.Equal(x,y)) test1 test2
- 有效,因为lambda包裹Equals
具有预期的签名。
List.zip test1 test2 |> List.map Assert.Equal
- 因为您现在只有一个元组列表,因为List.map
需要一个'a -> 'b
函数(其中'a
现在是一个元组),Assert.Equal
1}}现在是公平的游戏。
两个值和一个元组是隐式可互换的,这是不正确的。至少不是F语言,或者是基础的IL表示。当您从C#调用F#代码时,您可以认为这是一种方式 - 'a -> 'b -> 'c
函数确实在句法上称为'a * 'b -> 'c
函数 - 但这是更多的例外而不是规则。
答案 3 :(得分:1)
根据其签名Xunit.Assert.Equal()
采用单个2值元组参数