尝试比较每个子列表的长度(了解lambda和list模块)

时间:2018-10-04 07:05:07

标签: f#

我正试图创建一个检查列表子列表的函数,以查看它们的长度是否相等,并返回布尔值。

[ [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是不对的。

有人可以解释我在做什么错吗?

3 个答案:

答案 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

请注意两种样式之间的区别:

  • OO,调用方法: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