我刚看过关于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]
。什么是正确的版本?
答案 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艺术技巧不符合文章中的标准设置,因此您只需要使用此绘图: 前馈是列表中到目前为止看到的最小值。引物启动了前馈通道。反馈通道将前馈通道的结果值返回到列表的开头。最后,反馈值成为用于填充输出列表的最小值。
答案 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
还计算列表的最小值。 trace
将repIIminList
应用于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