【问题标题】:Haskell Inserstion sort countHaskell 插入排序计数
【发布时间】:2011-07-10 14:23:53
【问题描述】:

我的问题是转这个:

 iSort :: Ord a => [a] -> [a]
 iSort [] = []
 iSort (x:xs) = ins x (iSort xs)

 ins x [] = [x]
 ins x (y:ys)
   | x <= y    = x : y : ys
   | otherwise = y : ins x ys

进入一个跟踪比较次数的解决方案,这里是我需要生成的代码骨架:

 iSortCount :: Ord a => [a] -> (Integer, [a])
 iSortCount [] = ...
 iSortCount (x:xs) = ...

 insCount x (k, [])     = ... 
 insCount x (k, (y:ys)) -- Count the times when it reach's here     
   | x <= y    =  ...
   | otherwise = ...
   where ... 

我已经尝试了很多事情,从使用 let、wheres、writer monad,创建自己的类型,state monad,我似乎只是在看一些东西,因为我一直遇到“y : ins”的问题x ys" 因为该函数返回的应该是 (Int, [a]) 并且 : 不适用于元组。我试图把它拆分来做这样的事情

do
(a,b) <- ins x (k+1, ys)
return (k, (y : b))

但它似乎不认为 ins 在那个版本中返回一个元组,所以我猜它只是不是模式匹配。我的主要问题是我现在应该看哪里?我为此工作了很长时间,这个问题开始让我感到沮丧,因为它看起来很容易......

在以斯拉的帮助下回答:

iSort' [] = []
iSort' (x:xs) = ins' x (iSort' xs)

ins' x [] = [x]
ins' (x,i) (y:ys)
    | x <= fst y = (x,i+1) : y : ys
    | otherwise  =     y : ins' (x,i+1) ys

countInsertions x = sum $ map snd $ iSort' $ zip x $ repeat 0 

【问题讨论】:

  • 一个 MVar 浮现在脑海中,但这似乎是一个杂牌。我很好奇别人会怎么说。
  • 不能只写一个函数cmp x y = {-# SCC "cmp" #-} (x &lt;= y),用cmp代替&lt;=和ghc的profiling吗?还是您实际上需要程序中可用的比较次数,而不仅仅是分析结果?

标签: sorting haskell monads counting


【解决方案1】:

将纯代码转换为一元代码可能会很棘手,希望这些技巧可以为您提供正确的想法:

  • 选择一个单子。您也可以在 Sum 幺半群上使用 writer,但您可能会发现基于状态的代码更直接。

  • 考虑代码中的所有表达式:哪些表达式会导致状态变量递增? ins 进行了比较,但更微妙的是,因为对 iSort 的递归调用可以调用 ins,它也是您必须记住的这些表达式之一。

  • 请记住,monad 背后的想法是在幕后隐藏传递计数的管道。所以你的函数的包装返回类型不会改变;它们只是长出了一个单子皮肤,您可以使用&gt;&gt;= 将它们取出。

  • 回想一下所有可能导致状态变量递增的表达式:这些是您的单子调用。将它们重写为 do-block 内的tempVar &lt;- ins foo 形式,并将旧位置替换为您分配的临时变量。

  • 使用你的单子!将你的守卫浮动到一个内部 case 语句中,在执行 case-match 之前,增加 state 变量。

应该这样做!

还有一种邪恶的做法,涉及unsafePerformIO

【讨论】:

  • 非常感谢,这将非常有帮助!
【解决方案2】:

这看起来是作家 monad 的完美工作。见http://learnyouahaskell.com/for-a-few-monads-more#writer

【讨论】:

  • 这也是我的第一个想法,但我只是在增加日志(计数)时遇到问题,我试图在 do 表示法中使用 tell,“tell (+1)”但我很确定我错过了阅读或理解如何使用告诉。似乎告诉影响了日志,但由于在 learnyouahaskell 它通常只有日志作为 [String]
  • Int 没有为它定义一个幺半群;您必须改用“Sum Int”。幸运的是,只要你正确排列你的类型,tell 1 会做你想做的! (因为Sum 有一个Num 实例)。
【解决方案3】:

试试这个:

import Control.Monad.State

type Counter = State Int

incr :: Counter ()
incr = modify (+1)

ins :: Ord a => a -> [a] -> Counter a
ins x [] = return [x]
ins x (y:ys)
  | x <= y    = incr >> return $ x : y : ys
  | otherwise = incr >> ins x ys >>= (y :)

iSort :: Ord a => [a] -> Counter [a]
iSort [] = return []
iSort (x:xs) = iSort xs >>= ins x

cSort :: Ord a => [a] -> ([a],Int)
cSort = flip runState 0

但请注意,这是相当低效的。

【讨论】:

  • 不太正确:如果他计算比较,否则ins 的情况也应该增加。
  • @Edward Z. Yang 是的。其实应该的。但是iSort的案例呢,也是一个对比,值得一提。
  • 嗯,我不这么认为:cons-cell 上的模式匹配与调用 &lt;= 不同,我认为后者是被计算在内的。
【解决方案4】:

另一种方法是在您已有的解决方案中添加一个累加器:

iSort' [] = []
iSort' (x:xs) = ins' x (iSort' xs)

ins' x [] = [x]
ins' (x,i) (y:ys)
    | x <= fst y = (x,i) : y : ys
    | otherwise  =     y : ins' (x,i+1) ys

countInsertions x = sum $ map snd $ iSort' $ zip x $ repeat 1 

此解决方案的好处是熟悉。我只是将列表中的每个项目替换为一个表示项目的元组以及它被洗牌的次数。它们被初始化为1,因为我认为所有内容都至少被洗牌过一次。

排序例程基本相同,但现在需要一个“设置”功能,因此您不需要提供元组列表,而是提供项目列表。因为它是元组中的第二项,我们需要snd,因为我们想要总数,所以我们使用sum

【讨论】:

  • 我也尝试过类似的方法,但没有考虑用 x 制作计数器。另外,您在没有 monads 的情况下做到了,这是我在将其转换为 monadic 函数之前尝试做的事情。感谢您的回复。
  • 我不得不对你的代码做一点改动来产生我想要的东西,但这只是一个小改动。我需要让它计算它进行比较的次数,所以 [1,2,3,4,5] 将是 4。1 检查 2,2 检查 3 等。所以这是更新,感谢您的回答。 “编辑”将答案放在我原来的帖子中,因为 cmets 不允许代码缩进。希望我能很快得到一个单子的答案。
猜你喜欢
  • 1970-01-01
  • 2018-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 2021-03-22
  • 1970-01-01
相关资源
最近更新 更多