如何使用活动模式而不是 when guard编写 startsWith list 函数



我需要检查一个列表是否以另一个较短的列表开头。当使用守卫,该功能微不足道:

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

活动模式的定义中有两个错误:

  1. 这是一个部分活动模式(因为它可能不匹配),因此语法(|HeadsMatch|_|)

  2. 您需要将两个列表成对,因为您想匹配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。通常,您几乎应该始终避免这两种方法

相关内容

  • 没有找到相关文章

最新更新