【问题标题】:Haskell -- sort list with impure functionHaskell——具有不纯函数的排序列表
【发布时间】:2012-07-13 11:42:03
【问题描述】:

如何使用 IO 比较功能对列表进行排序?

sortWith :: [String] -> (String -> String -> IO Ordering) -> IO [String]

Sortby 期待(a->a->Ordering),我不知道如何处理它。我懒得自己实现快速排序。

【问题讨论】:

  • Bool 是什么意思? sortBy 的类型为 (a -> a -> Ordering) -> [a] -> [a],它使用的函数返回 Ordering,而不是 Bool

标签: sorting haskell io


【解决方案1】:

恐怕没有简单的方法。如果可以举起

sortBy :: Ord a => (a -> a -> Ordering) -> [a] -> [a]

sortByM :: (Ord a, Monad m) => (a -> a -> m Ordering) -> [a] -> m [a]

你可以看到sortBy的实现中的比较顺序,违反了引用透明性。

一般来说,从xxxMxxx 很容易,但反之则不然。

可能的选择:

  • 实现一元排序方法
  • 使用 monadlist 库,其中包含插入排序(如 dflemstr 的回答)
  • 使用unsafePerformIO 作为黑客
  • 切换到按键排序并使用Schwartzian transform

    sortOnM :: (Monad m, Ord k) => (a -> m k) -> [a] -> m [a]
    sortOnM f xs = liftM (map fst . sortBy (comparing snd)) $
                     mapM (\x -> liftM (x,) (f x)) xs
    

【讨论】:

  • 您能否详细说明为什么sortByM 违反了引用透明度?
  • sortByM 没有违反它。将sortBy 提升到sortByM 的潜在功能确实如此。两个排序函数sortBy1, sortBy2 :: Ord a => (a -> a -> Ordering) -> [a] -> [a] 应该是完全可互换的,即使它们以不同的顺序执行比较。如果你让f i j = print (i,j) >> return (compare i j) 然后lift sortBy1 f 可能会打印与lift sortBy2 f 不同的序列。
【解决方案2】:

sortBy 函数使用归并排序作为 GHC 中的算法,但 Haskell 98 报告规定应该使用插入排序。

为简单起见,因为我没有编译器,所以我无法测试我的代码,我将在这里实现插入排序:

import Data.Foldable (foldrM)

insertByM :: (a -> a -> IO Ordering) -> a -> [a] -> IO [a]
insertByM _   x [] = return [x]
insertByM cmp x ys@(y:ys') = do
  p <- cmp x y
  case p of
    GT -> do
      rest <- insertByM cmp x ys'
      return $ y : rest
    _ -> return $ x : ys

sortByM :: (a -> a -> IO Ordering) -> [a] -> IO [a]
sortByM cmp = foldrM (insertByM cmp) []

正如我所说,我没有测试过这段代码,但它可以/应该工作。

【讨论】:

【解决方案3】:

哦,我以前做过这个!使用一元比较器进行合并排序:

type MComparator m a = a -> a -> m Ordering

sortByM :: (Monad m, Functor m) => MComparator m a -> [a] -> m [a]
sortByM cmp []  = return []
sortByM cmp [x] = return [x]
sortByM cmp xs = do
  let (ys, zs) = partition xs
  ys' <- sortByM cmp ys
  zs' <- sortByM cmp zs
  merge ys' zs'
  where merge [] bs = return bs
        merge as [] = return as
        merge (a:as) (b:bs) = do
          comparison <- cmp a b
          case comparison of
            LT -> (a:) <$> merge as (b:bs)
            _  -> (b:) <$> merge (a:as) bs
        partition xs = splitAt (length xs `quot` 2) xs

来自我的博文:http://unknownparallel.wordpress.com/2012/07/03/using-monadic-effects-to-reverse-a-merge-sort/

【讨论】:

    【解决方案4】:

    Larry Wall 是不是说过懒惰是程序员的三大美德之一?

    您似乎想将 (a -> a -> b) 类型的函数转换为 (a -> a -> c b) 类型的函数。让我们将其插入Hoogle。现在,如果您知道 IO 是 Monad,您将在 liftM2 中看到第 10 场比赛。检查(liftM2 sortBy)的类型,是你想要的吗?

    【讨论】:

    • 这不是正确的答案。您应该注意,比较函数(第二个参数)也在 IO monad 中。
    猜你喜欢
    • 2018-08-16
    • 1970-01-01
    • 2021-09-04
    • 2012-04-06
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多