F#是检查列表是否已排序的函数

时间:2010-09-04 11:59:20

标签: f# pattern-matching

我必须编写一个函数,如果给定列表按升序排序,则返回true。空元素和单元素列表已排序。此外,[5,12,12]应该返回true。

我写了一个似乎有效的功能:

let rec isSorted (l: int list) = 
    match l with
    | [] -> true
    | [x] -> true
    | [x;y] -> x <= y
    | x::y::xr -> if x > y then false else isSorted([y] @ xr);

但它似乎有点偏离......我在想有必要有一个更简单的方法吗?我讨厌我必须匹配4个案例,但我无法弄清楚如何让它变得更聪明。

有更好的解决方案吗?

4 个答案:

答案 0 :(得分:14)

您可以组合现有功能:

let isAscending l = l |> Seq.pairwise |> Seq.forall (fun (a, b) -> a <= b)

printfn "%b" (isAscending []) // true
printfn "%b" (isAscending [1]) // true
printfn "%b" (isAscending [5;12]) // true
printfn "%b" (isAscending [5;12;12]) // true
printfn "%b" (isAscending [5;12;12;11]) // false

答案 1 :(得分:7)

好吧,永远不要说

[y] @ xr

y :: xr

也会这样做。 (一般来说,@是一种代码气味。)

有点挑剔,但最后一行可能是

| x::((y::_)as t) -> if x > y then false else isSorted(t)

并避免进行任何分配。

现在,你需要第三种情况吗?如果删除它会发生什么?

答案 2 :(得分:5)

回到原始代码(而不是建议的库调用),我会说你可以做一些改进:

  • 第三个匹配案例并不是真的需要(已经提到过)。
  • 在第二种情况下,您不希望为该值指定名称,而您无法访问它。
  • 在第四种情况下,将y::xr再次与[y] @ xr(或y::xr)拼接在一起是不正确的。 as表达似乎更好。
  • 您只是将两个逻辑结果组合在一起,if..then看起来有点不合适。

我提出了以下修订版本:

let rec isSorted l =
    match l with
    | [] | [_] -> true
    | h1::(h2::_ as tail) -> h1 <= h2 && isSorted tail

我怀疑它比原版更有效,但它更容易看到。

答案 3 :(得分:2)

这在效率方面是一个特别糟糕的解决方案,所以我永远不会在现实世界中使用它,但这是一个非常实用的方式来查看我在博客示例中提出的问题:

让isSorted l = l =(l |&gt; List.sort)