按字符串长度对字符串列表排序

时间:2015-10-24 09:26:18

标签: sorting haskell

我想首先按字符串的长度对String的列表进行排序,如果长度相同则应该按词法排序。我以为我可以使用Data.List库并编写我自己的比较函数来做到这一点。因此compare函数应该以{{1​​}}列表作为参数,并比较所有元素(String s)。 String s的比较函数看起来像这样

String

如何使用这样的函数处理所有列表元素?

2 个答案:

答案 0 :(得分:5)

首先,您的cmp函数无法处理长度相等的情况:您需要添加它。否则,您将收到运行时模式匹配错误:

comp a b
    | length a > length b   = GT
    | length a < length b   = LT
    | otherwise             = undefined  -- TODO

另外,请注意,此实现有时会计算两次长度,但GHC很可能会自行优化这一长度,而且我们以后会更加基本地解决这个问题。

然后,一旦您修复了comp,您需要做的就是将其与您想要排序的字符串列表一起传递给Data.List.sortBy 。下面提供了类似的实现(<$>fmap的运算符别名,其作用与列表上map的作用相同。)

然而,通过将每个元素映射到一对,其中第一个成员是原始字符串,并且首先计算列表中所有元素的长度,这是一个更好的解决方案。第二个是它的长度。然后使用修改后的comp函数,该函数需要2对而不是2个字符串,否则其行为与原始comp的行为相同。但是,您需要将中间列表映射回仅包含字符串(这是fst <$>的用途,这相当于map fst但是,再次使用,IMO更好看,{ {1}}操作员)。

所以有些天真的解决方案将是:

<$>

效率更高,如左下所示,将是:

sortByLenOrLex :: [String] -> [String]
sortByLenOrLex as = sortBy cmp as where
  cmp a b | n > m     = GT
          | n < m     = LT
          | otherwise = compare a b
    where n = length a
          m = length b

首先使用每个元素的长度修改列表,以避免重复,昂贵的sortByLenOrLex' :: [String] -> [String] sortByLenOrLex' as = fst <$> sortBy cmp (addLen <$> as) where cmp (a,n) (b,m) | n > m = GT | n < m = LT | otherwise = compare a b addLen x = (x, length x) 调用。

编辑:请参阅chi's answer以获得更好的此算法实施方法!

<强>此外:

您可以通过对 length 列表的列表进行操作来使您的功能变得通用:

Ord

这会给你:

sortByLenOrLex'' :: Ord a => [[a]] -> [[a]]
sortByLenOrLex'' as = fst <$> sortBy cmp (addLen <$> as) where
  cmp (a,n) (b,m) | n > m     = GT
                  | n < m     = LT
                  | otherwise = compare a b
  addLen x = (x, length x)

...如果您想让它尽可能通用,您可以对{{1>} *Main> sortByLenOrLex'' [[1,2], [1,3], [1,2,3]] [[1,2],[1,3],[1,2,3]] 列表进行排序:

Foldable

这会给你:

Ord

答案 1 :(得分:3)

@ Erik解决方案的变体,使用库中的一些组合器:

Main

这基本上是Schwartzian transform