ZetCode

F# 排序

最后修改时间 2025 年 5 月 1 日

在本文中,我们将介绍如何在 F# 中对值进行排序。

排序

排序是将元素排列成有序序列。过去,人们开发了多种算法来对数据进行排序,包括归并排序、快速排序、选择排序或冒泡排序。

数据可以按字母顺序或数字顺序排序。排序键指定用于执行排序的标准。可以按多个键对对象进行排序。例如,在对用户进行排序时,用户的姓名可用作主排序键,职业可用作次排序键。

排序顺序

标准顺序称为升序:a 到 z,0 到 9。反向顺序称为降序:z 到 a,9 到 0。对于日期和时间,升序表示较早的值排在较晚的值之前,例如 2021/1/12 将排在 2022/1/12 之前。

稳定排序

稳定排序是指相等元素的初始顺序得以保留的排序。某些排序算法自然是稳定的,而某些则不稳定。例如,归并排序和冒泡排序是稳定的排序算法。另一方面,堆排序和快速排序是不稳定排序算法的示例。

考虑以下值:3715593。稳定排序的结果如下:1335579。3 和 5 的值顺序得以保留。不稳定排序可能产生如下结果:1335579

F# List.sort, List.sortDescending

List.sort 函数按升序对列表进行排序。List.sortDescending 按降序对列表进行排序。这些函数返回排序后的列表;原始列表不会被修改。这些函数实现稳定排序,即相等元素的原始顺序得以保留。

F# List.sortBy

List.sortBy 使用投影函数提供的键对给定列表进行排序。

F# 对整数列表进行排序

以下示例对整数列表进行排序。

main.fsx
let nums = [ 7; 9; 3; -2; 8; 1; 0 ]

List.sort nums |> printfn "%A"
List.sortDescending nums |> printfn "%A"

我们定义一个整数列表。该列表按升序和降序排序。

λ dotnet fsi main.fsx
[-2; 0; 1; 3; 7; 8; 9]
[9; 8; 7; 3; 1; 0; -2]

F# 对字符串列表进行排序

在第二个示例中,我们对字符串列表进行排序。

main.fsx
let words =
    [ "sky"
      "cloud"
      "atom"
      "brown"
      "den"
      "kite"
      "town" ]

List.sort words |> printfn "%A"
List.sortDescending words |> printfn "%A"

字符串按升序和降序排序。

λ dotnet fsi main.fsx
["atom"; "brown"; "cloud"; "den"; "kite"; "sky"; "town"]
["town"; "sky"; "kite"; "den"; "cloud"; "brown"; "atom"]

F# 按姓氏排序

在以下示例中,我们将按姓氏对姓名进行排序。

main.fsx
let names =
    [ "John Doe"
      "Lucy Smith"
      "Benjamin Young"
      "Robert Brown"
      "Thomas Moore"
      "Linda Black"
      "Adam Smith"
      "Jane Smith" ]

names
|> List.sortBy (fun e -> e.Split(" ")[1])
|> printfn "%A"

names
|> List.sortBy (fun e -> let a = e.Split(" ") in Array.get a 1)
|> printfn "%A"

我们有一个姓名列表。我们需要拆分姓名以获取姓氏。

names
|> List.sortBy (fun e -> e.Split(" ")[1])
|> printfn "%A"

在投影函数中,我们按空格拆分字符串并返回第二个值。

names
|> List.sortBy (fun e -> let a = e.Split(" ") in Array.get a 1)
|> printfn "%A"

这是一个替代解决方案,我们使用 let 表达式和 Array.get 函数。

λ dotnet fsi main.fsx
["Linda Black"; "Robert Brown"; "John Doe"; "Thomas Moore"; "Lucy Smith";
 "Adam Smith"; "Jane Smith"; "Benjamin Young"]
["Linda Black"; "Robert Brown"; "John Doe"; "Thomas Moore"; "Lucy Smith";
 "Adam Smith"; "Jane Smith"; "Benjamin Young"]

F# 不区分大小写列表排序

以下示例按不区分大小写的顺序对列表进行排序。

main.fsx
let words =
    [ "sky"
      "Sun"
      "Albert"
      "cloud"
      "by"
      "Earth"
      "else"
      "atom"
      "brown"
      "a"
      "den"
      "kite"
      "town" ]

words |> List.sortBy (fun e -> e.ToLower()) |> printfn "%A" 

List.sortBy 函数使用给定投影提供的键对给定列表进行排序。

words |> List.sortBy (fun e -> e.ToLower()) |> printfn "%A" 

我们将 lambda 函数传递给 List.sortBy。它将元素转换为小写。

λ dotnet fsi main.fsx
["a"; "Albert"; "atom"; "brown"; "by"; "cloud"; "den"; "Earth"; "else"; "kite";
 "sky"; "Sun"; "town"]

F# 对元组列表进行排序

在下一个示例中,我们对元组列表进行排序。

main.fsx
let vals =
    [ (1, 3)
      (4, 3)
      (3, 0)
      (4, 0)
      (0, 1)
      (0, 3)
      (2, 2)
      (0, 0)
      (1, 1)
      (3, 3) ]

vals |> List.sortBy (fun (e, _) -> e) |> printfn "%A" 
vals |> List.sortBy fst |> printfn "%A" 

printfn "------------------------" 

vals |> List.sortBy (fun (_, e) -> e) |> printfn "%A" 
vals |> List.sortBy snd |> printfn "%A" 

元组首先按第一个元素排序,然后按第二个元素排序。

vals |> List.sortBy (fun (e, _) -> e) |> printfn "%A" 

在投影中,我们选择第一个元素。

vals |> List.sortBy fst |> printfn "%A" 

我们也可以使用 fst 函数。

λ dotnet fsi main.fsx
[(0, 1); (0, 3); (0, 0); (1, 3); (1, 1); (2, 2); (3, 0); (3, 3); (4, 3); (4, 0)]
[(0, 1); (0, 3); (0, 0); (1, 3); (1, 1); (2, 2); (3, 0); (3, 3); (4, 3); (4, 0)]
------------------------
[(3, 0); (4, 0); (0, 0); (0, 1); (1, 1); (2, 2); (1, 3); (4, 3); (0, 3); (3, 3)]
[(3, 0); (4, 0); (0, 0); (0, 1); (1, 1); (2, 2); (1, 3); (4, 3); (0, 3); (3, 3)]

F# 对记录列表进行排序

在下一个示例中,我们对记录列表进行排序。

main.fsx
open System

type User =
    { FirstName: string
      LastName: string
      Salary: int }

let users =
    [ { FirstName = "John"
        LastName = "Doe"
        Salary = 1230 }
      { FirstName = "John"
        LastName = "Doe"
        Salary = 1230 }
      { FirstName = "Lucy"
        LastName = "Novak"
        Salary = 670 }
      { FirstName = "Ben"
        LastName = "Walter"
        Salary = 2050 }
      { FirstName = "Robin"
        LastName = "Brown"
        Salary = 2300 }
      { FirstName = "Joe"
        LastName = "Draker"
        Salary = 1190 }
      { FirstName = "Janet"
        LastName = "Doe"
        Salary = 980 } ]

users |> List.sortBy (fun u -> u.LastName) |> List.iter Console.WriteLine

Console.WriteLine "---------------------"

users |> List.sortBy (fun u -> u.Salary) |> List.iter Console.WriteLine

我们定义了一个用户列表。我们按姓氏和薪水对用户进行排序。

type User =
    { FirstName: string
      LastName: string
      Salary: int }

User 记录有三个标签:FirstNameLastNameSalary

users |> List.sortBy (fun u -> u.LastName) |> List.iter Console.WriteLine

这里我们按姓氏对用户列表进行排序。在投影函数中,我们选择 LastName 标签。

users |> List.sortBy (fun u -> u.Salary) |> List.iter Console.WriteLine

这里我们选择 Salary 标签。

F# 按多个字段对记录进行排序

接下来,我们展示如何按多个字段对记录列表进行排序。

main.fsx
type User =
    { FirstName: string
      LastName: string
      Salary: int }
    override this.ToString() =
        $"{this.FirstName} {this.LastName}, {this.Salary}"

let users =
    [ { FirstName = "John"
        LastName = "Doe"
        Salary = 1230 }
      { FirstName = "Lucy"
        LastName = "Novak"
        Salary = 670 }
      { FirstName = "Ben"
        LastName = "Walter"
        Salary = 2050 }
      { FirstName = "Robin"
        LastName = "Brown"
        Salary = 2300 }
      { FirstName = "Vivien"
        LastName = "Doe"
        Salary = 1010 }
      { FirstName = "Joe"
        LastName = "Draker"
        Salary = 1190 }
      { FirstName = "Albert"
        LastName = "Novak"
        Salary = 1930 }
      { FirstName = "Janet"
        LastName = "Doe"
        Salary = 980 }
      { FirstName = "Ken"
        LastName = "Novak"
        Salary = 2990 } ]

users
|> List.sortBy (fun e -> e.LastName, e.Salary)
|> List.iter (fun e -> printfn $"{e}")

用户列表首先按姓氏排序,然后按薪水排序。

|> List.sortBy (fun e -> e.LastName, e.Salary)

在投影函数中,我们只需传递用逗号分隔的两个标签。

λ dotnet fsi main.fsx
Robin Brown, 2300
Janet Doe, 980
Vivien Doe, 1010
John Doe, 1230
Joe Draker, 1190
Lucy Novak, 670
Albert Novak, 1930
Ken Novak, 2990
Ben Walter, 2050

在本文中,我们对 F# 中的值进行了排序。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。