我需要检查一个列表是否以另一个较短的列表开头。当使用守卫时,该功能微不足道:
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| x::xs, y::ys when x = y -> startsWith xs ys
| _ -> false
let lst1 = [ 1; 2; 1 ]
let lst2 = [ 1; 2; 1; 2; 3; ]
let lst3 = [ 1; 3; 1; 2; 3; ]
let c1 = startsWith lst1 lst2 // true
let c2 = startsWith lst1 lst3 // false
但是无论我按照活动模式的思路尝试什么:
let (|HeadsMatch|) (l1 : ('a) list) (l2 : ('a) list) =
if l1.Head = l2.Head then Some(l1.Tail, l2.Tail) else None
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| HeadsMatch /* need to capture the result */ -> startsWith t1 t2
| _ -> false
我无法编译。如何使用活动模式制作此函数的版本?如果这是不可能的,你能解释一下为什么吗?
附言还有其他不错的方法来编写上述函数吗?
编辑:我从丹尼尔的答案中获取了片段,以免分散对真正问题的注意力。
编辑:我的问题从一开始就开始了。我已将活动模式函数定义为
let (|HeadsMatch|_|) lst1 lst2 =
但它应该是
let (|HeadsMatch|_|) (lst1, lst2) =
在这种情况下,它将与接受的答案相匹配。
我想最好的方法可能是
let startsWith = Seq.forall2 (=)
如果要从头开始编写,则需要在两个列表中匹配:
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| x::xs, y::ys when x = y -> startsWith xs ys
| _ -> false
如果你想用一个活跃的模式来编写它来学习,使用Tarmil的定义,它将是
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| HeadsMatch(xs, ys) -> startsWith xs ys
| _ -> false
活动模式的定义中有两个错误:
-
这是一个部分活动模式(因为它可能不匹配),因此语法
(|HeadsMatch|_|)
。 -
您需要将两个列表成对,因为您想匹配
lst1, lst2
。
有了这些,代码将编译。但它会在运行时引发异常,因为当您不知道列表是否有任何元素时,您会在列表上使用 .Head
和 .Tail
;如果其中一个列表为空,则不会定义行为。
这是HeadsMatch
的惯用实现:
let (|HeadsMatch|_|) (lst1, lst2) =
match (lst1, lst2) with
| (x :: xs, y :: ys) when x = y -> Some (xs, ys)
| _ -> None
无防护:
let (|HeadsMatch|_|) (lst1, lst2) =
match (lst1, lst2) with
| (x :: xs, y :: ys) ->
if x = y then Some (xs, ys) else None
| _ -> None
作为旁注,您的第一个实现也有同样的问题。以下内容将引发运行时异常:
startsWith [1;2] [1]
因为在使用它之前,您不会检查lst2
是否为空.Head
并对其进行.Tail
。通常,您几乎应该始终避免这两种方法。