循环编程 - 用最小值替换列表元素

时间:2015-03-24 06:46:31

标签: haskell

我刚看过关于circular programming的{​​{3}}。这对我来说似乎很陌生。虽然我可以想象反馈是懒惰的评估thunk,将在以后评估到期望的结果,但是无法绕过它。所以我决定编写一个用它的最小值替换列表中每个元素的函数。

trace :: (a -> c -> (b,c)) -> a -> b
trace f a = b
    where (b,c) = f a c

repminList :: (Num a, Ord a) => [a] -> [a]
repminList = trace repIIminList 

repIIminList [x] m = ([m], x)
repIIminList (a:as) m = let (replaced, m) = repIIminList as m 
                        in (m : replaced, min a m)

repminList [1,2,3]等于[2,3,3]。什么是正确的版本?

4 个答案:

答案 0 :(得分:8)

你的问题是你有两个不同的m变量和一个阴影而不是另一个,所以你根本不会使用实际的循环变量。以下是repIIminList的固定版本:

repIIminList [x] m = ([m], x)
repIIminList (a:as) m = let (replaced, m') = repIIminList as m
                        in (m : replaced, min a m')

此处m是您作为循环参数接收的列表的最后一个最小元素。从m'的递归调用返回的repIIminList是到目前为止看到的最小值,因此附加最终最小值(即m)非常重要到结果列表,然后通过返回min a m'更新当前最小值。

答案 1 :(得分:4)

这是一个非常酷的技术!这是一个受你的启发的工作程序(除了浏览图片之外,我没有真正阅读过这篇文章,所以这可能不是作者的意图,但它确实有效):

looper :: (inputT -> feedfwdT -> feedbackT -> (feedbackT, outputT)) -> inputT -> feedfwdT -> outputT
looper f input primer = output
  where (feedback, output) = f input primer feedback

min_feedback :: (Ord a) => [a] -> Maybe a -> a -> (a, [a])
min_feedback [] (Just p) _ = (p, [])
min_feedback (x:xs) partial_min minimum = (feedback, minimum:output)
  where new_partial_min = case partial_min
                of Nothing -> Just x
                   Just p -> Just $ min x p
    (feedback, output) = min_feedback xs new_partial_min minimum

min_looped :: (Ord a) => [a] -> [a]
min_looped input = looper min_feedback input Nothing

main = print $ min_looped [1,4,6,2,6,3,-1,6,3,6,10]

这里的关键是你需要的不仅仅是反馈通道,你还需要一个前馈通道来确定第一次通过循环时的最小值。我的ASCII艺术技巧不符合文章中的标准设置,因此您只需要使用此绘图: Circular programming example 前馈是列表中到目前为止看到的最小值。引物启动了前馈通道。反馈通道将前馈通道的结果值返回到列表的开头。最后,反馈值成为用于填充输出列表的最小值。

答案 2 :(得分:3)

我太累了,无法分析你的代码,神圣你的意图和错误。但是,我很高兴地告诉你,在做基本结打结时,我怎么能避免这么想。

它的状态Monad,耶!我使用状态monad(下面)只是一个小管道,它以一种允许查找和更新值的方式跟踪单个当前值。

  • repMin通过考虑空列表然后运行状态monad来启动计算。
  • 我们的工作人员操作f随输入列表和列表中的最小元素一起提供(目前是thunk,不评估!)
  • f遍历列表,计算最小值并使用即将知道但尚未评估的最小值m替换每个元素。

代码:

import Control.Monad.State

repMin :: [Int] -> [Int]
repMin [] = []
repMin xs@(x:_) = let (v,m) = runState (f m xs) x in  v

f :: Int -> [Int] -> State Int [Int]
f m xs = mapM (λx -> checkMin x >> return m) xs
 where
    checkMin :: Int -> State Int ()
    checkMin x = modify (min x) 

请注意,min a (min b ( min c ...)))的大部分内容都存在懒惰泄漏,但是您可以了解到这一点。

答案 3 :(得分:3)

这是

repIIminList (x:[]) m' = ([m'], x)
repIIminList (x:xs) m' = (m' : xs', min x m) where (xs', m) = repIIminList xs m'

m是当前最低要求,m'是最终最低要求,xs是当前列表,xs'是最终列表。也就是说,repIIminList接收一个列表和一个数字,并递归地用这个数字替换列表中的每个元素。 repIIminList还计算列表的最小值。 tracerepIIminList应用于repIIminList本身计算的最小值。

使用状态monad,你可以用非常明确的方式重写它:

repminList :: [Int] -> [Int]
repminList  []    = []
repminList (x:xs) = evalState (go xs) x where
    go  []    = get >>= return . (:[])
    go (x:xs) = modify (min x) >> flip (:) <$> go xs <*> get

或直接使用CPS样式:

repminList :: [Int] -> [Int]
repminList  []    = []
repminList (x:xs) = foldr (\x r -> (\(x:xs) -> x:x:xs) . r . min x) (:[]) xs x