按照 Elm 中元组的第一个索引对列表进行排序

4
假设我有一个元组列表,类似于以下示例:
[(5, "a"), (1, "c"), (7, "d")] 

在 Elm 中,如何根据其第一个元素按升序对列表进行排序,以便我们获得以下结果?
[(1, "c"), (5, "a"), (7, "d")]

使用Elm List文档,看起来sortBysortWith函数对于这种情况会很有用。我的实现尝试如下:

maxTuples : Tuple(a, b) -> Tuple(a, b) -> Tuple(a, b)
maxTuples t1 t2 = 
    case compare t1 t2 of 
        ((Tuple.first t1) >= (Tuple.first t2)) -> GT
         _                                     -> LT


sortByFirst : List (Tuple (a, b)) -> List (Tuple (a, b))
sortByFirst lst = 
    List.sortWith maxTuples lst

然而,我遇到了以下类型的编译器错误:
I ran into something unexpected when parsing your code!

99|         ((Tuple.first t1) >= (Tuple.first t2)) -> GT
                ^
I am looking for one of the following things:

an upper case name

我猜编译器正在按照List库的API寻找GT/LT/EQ,但如果是这样,我不确定如何使用sortBysortWith来按每个元素的第一个索引对Elm中的Tuple列表进行排序。
3个回答

5
您找到了正确的函数。在您的代码中,实际上有多个问题:
  1. 类型注释应该只是 (a, b),而不是 Tuple(a, b)
  2. 您将 t1t2 进行比较,这将按字典顺序比较元组。您实际上想要的是 compare (Tuple.first t1) (Tuple.first t2)
  3. case 分支需要在 -> 之前加上一个模式。在这种情况下,会是类似于 EQ 的东西,因为您正在匹配compare 的结果,它返回Order 类型。
您可以像这样修复代码:
maxTuples : (comparable, b) -> (comparable, b) -> (comparable, b)
maxTuples t1 t2 = 
    case compare (Tuple.first t1) (Tuple.first t2) of 
        GT -> GT
        EQ -> EQ
         _ -> LT

但现在出现了不必要的重复,你只是返回了compare函数的结果。

maxTuples t1 t2 = 
    compare (Tuple.first t1) (Tuple.first t2)

结合排序功能,代码如下:

sortByFirst lst = 
    List.sortWith (\t1 t2 -> compare (Tuple.first t1) (Tuple.first t2)) lst

原来这种操作相当普遍,尤其是针对记录列表。因此,Elm 提供了另一个函数 - sortBy。它接受一个函数并在应用该函数后比较元素:

sortBy f lst =
    List.sortWith (\a b -> compare (f a) (f b)) lst

因此,您可以使用sortBy函数来大大简化代码:
sortByFirst : List (comparable, b) -> List (comparable, b)
sortByFirst =
    sortBy Tuple.first

编译器指示签名应为List(comparable, a2) -> List(comparable, a2)但这是正确的想法。 - Adam Freymiller
@AdamFreymiller 是的,那更精确。我会把它编辑到答案中。 - Jan Tojnar

4
值得注意的是,您想要的是List.sort的默认行为(排序将使用元组的第一个元素对列表进行排序,如果两个元素具有相同的第一个元素,则会移动到比较第二个元素等等)。 List.sort [(5, "a"), (1, "c"), (1, "a"), (7, "d")] == [(1,"a"),(1,"c"),(5,"a"),(7,"d")] 在这种情况下使用sortBy是多余的。

0

我不确定在 case compare t1 t2 of 中的 compare 是什么,但你可以直接使用 if 而不是 case(如果你喜欢,也可以使用解构代替 Tuple.first):

maxTuples ( val1, _ ) ( val2, _ ) =
    if val1 >= val2 then
        GT
    else
        LT

我在https://ellie-app.com/ZZ9Hzhg7yva1/2上有一个完整的工作示例(还需要进行一些类型注释更改才能编译)

然而,sortBy是更简单的选项,因为它可以直接使用Tuple.first按其中任何内容进行排序:

sortByFirst lst =
    List.sortBy Tuple.first lst

那个版本在https://ellie-app.com/ZZ9Hzhg7yva1/3


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接