我正试图创建一个检查列表子列表的函数,以查看它们的长度是否相等,并返回布尔值。
[ [1;2;3]; [4;5;6] ] (return true)
[ [1;2;3]; [4;5] ] (return false)
我正在尝试学习lambda和list模块。
到目前为止,我有:
let isTable (lst : 'a list list) : bool =
List.forall (fun x -> x.Length = 2) ([ [1;2;3]; [4;5;6] ])
它说x.Length是不对的。
有人可以解释我在做什么错吗?
答案 0 :(得分:2)
您的代码的问题在于,在检查lambda函数时,F#类型推断不知道x
是什么类型,因此它无法检查对象是否具有成员Length
。类型推断从左到右检查您的程序,因此仅会发现x
将在列表中稍后到达参数[ [1;2;3]; [4;5;6] ]
时成为列表。
有两种方法可以解决此问题。您可以使用List.length
,它是一个函数,而不是实例成员,因此推论可以检查:
let isTable (lst : 'a list list) : bool =
List.forall (fun x -> List.length x = 2) [ [1;2;3]; [4;5;6] ]
一个更好的选择是使用|>
运算符,该运算符将左侧的内容传递给右侧的函数,因此编写x |> f
与调用f x
相同。这会将输入置于左侧,因此推断将起作用:
let isTable (lst : 'a list list) : bool =
[ [1;2;3]; [4;5;6] ] |> List.forall (fun x -> x.Length x = 2)
最后,您还可以添加类型注释:
let isTable (lst : 'a list list) : bool =
List.forall (fun (x:_ list) -> x.Length = 2) [ [1;2;3]; [4;5;6] ]
在这三种方法中,我认为最惯用的解决方案是使用|>
,但是List.length
也很常见。
答案 1 :(得分:1)
尝试以下代码:
let isTable (lst: 'a list list) =
match lst with
| [] | [[]] -> false
| []::t -> false
| [_] -> true
| h::t -> t |> List.forall(fun l -> l.Length = h.Length)
答案 2 :(得分:0)
您遇到的问题是F#的类型推断系统不能肯定地将x
识别为您要进行x.Length
时的列表。这似乎很奇怪,因为如果您使用Intellisense(例如,将鼠标悬停在x
上),它将告诉您这是一个列表,但是编译器会抱怨。
这样做的原因是,在使用面向对象(OO)点.
表示法时,F#的类型推断不能很好地工作,而在使用功能点.
表示法时,F#的类型推断要好得多。为了区别两者,F#(和.Net)中的约定是类成员(方法和属性)以大写字母(也称为Pascal Case)开头,因此为x.Length
。另一方面,功能样式代码(例如模块和/或记录成员中的功能)以小写字母(称为Camel Case)开头,例如List.length
。
请注意两种样式之间的区别:
x.Length
List.length x
如果要使用OO样式,通常的解决方案是添加类型注释,您可以通过以下几种方法进行操作:
fun (x:_ list) -> x.Length = 2
fun x -> (x:_ list).Length = 2
通常,最好使用功能样式。但是,您并不总是有选择的余地。例如,有许多String方法不具有等效的功能:
fun (s:string) -> s.StartsWith "Hello"
我还想指出,您所说的代码并没有真正做到您想要的。仅当所有列表的长度均为2时,它才返回true。
kagetoki的解决方案有效,并且演示了列表的模式匹配的使用。
以下是简化版本:
let isTable lst =
match lst with
| h::t -> t |> List.forall(fun (l:_ list) -> l.Length = h.Length)
| _ -> true
请注意,通过声明l
是列表,它已经知道h
也是列表。
最后,只是为了好玩,一个超级紧凑(但晦涩)的版本:
let isTable =
function
| h::t -> t |> List.forall (List.length >> (=) h.Length)
| _ -> true