如果某个变量小于某个阈值,则在实现算法时会经常发生这种情况,您会在 while 循环的每次迭代中进行检查。
在命令式语言中,它看起来像:
while ( x < TRESHOLD ) {
x = usefullStuff();
}
当然,有用的东西会以某种方式影响 x...
您如何利用函数式编程范式将此构造转换为 haskell?
如果某个变量小于某个阈值,则在实现算法时会经常发生这种情况,您会在 while 循环的每次迭代中进行检查。
在命令式语言中,它看起来像:
while ( x < TRESHOLD ) {
x = usefullStuff();
}
当然,有用的东西会以某种方式影响 x...
您如何利用函数式编程范式将此构造转换为 haskell?
尽管我不知道你的程序做了什么,但我有一种感觉,你会想要稍微不同地构建你的逻辑。查看论文Why Functional Programming Mattes的第 4 节- 了解一些想法。它处理类似的情况,涉及使用牛顿法找到方程的根。在那里,职责被划分,以便循环逻辑与逐次逼近的生成分离。
如果usefulStuff
是一个单子 - 即一个有副作用的函数,你将不得不使用这样的东西:
whileLoop x
| x < THRESHOLD = return x
| otherwise = do x <- usefulStuff
whileLoop x
来自 Control.Monad.Loops:
iterateWhile :: Monad m => (a -> Bool) -> m a -> m a
iterateWhile p x = do
y <- x
if p y
then iterateWhile p x
else return y
然后,假设有用的东西有副作用:
iterateWhile (< threshold) usefullStuff
按照说明走
如果某个变量小于某个阈值,则在实现算法时会经常发生这种情况,您会在 while 循环的每次迭代中进行检查。
我希望您实际上并不是指无参数usefullStuff
返回控件 value 的新值x
,而是将转换x
作为参数。
的直接翻译是
until (>= threshold) usefullStuff x
但是,通常情况下,状态不仅仅是循环控制,所以会有类似的东西
until ((>= threshold) . getX) usefullStuff (someDataContaining x)
哪里usefullStuff :: RecordContainingX -> RecordContainingX
。
另一方面usefullStuff
,如果真的意味着不带参数,如果它是纯的,它总是会产生相同的结果,所以你要么永远不会进入或永远不会离开循环。这不太有用。所以让我们假设usefullStuff
一个IO
-action 产生一个值。然后
while :: Ord a -> (a -> Bool) -> a -> IO a -> IO a
while test value action
| test value = do
newValue <- action
while test newValue action
| otherwise = return value
将捕获模式。
第一:在命令式语言中,您通常会编写一个while
循环 who's body updates x
。在 Haskell 中,这不可能发生。因此,我们需要稍微不同地构建我们的算法。
您通常会做的是将循环体转换为一个以参数为参数的函数x
,并返回新x
值作为其结果。然后,您可以使用until
Prelude 中的函数反复应用该函数,直到满足某些条件。或者,您可以使用iterate
生成所有结果的无限列表,然后使用takeWhile
或一些类似的功能来扫描该列表以查找感兴趣的项目。
现在,如果你的循环体实际上更新了几个变量会发生什么?那么,在这种情况下,您可以将所有这些变量打包到一个数据结构中,然后应用上述过程。不用说,如果你有很多这些变量,这可能会变得相当复杂。尽量避免大而复杂的循环。(在任何语言中,不仅仅是 Haskell。)
你使用了“算法”这个词,这让我怀疑你在谈论诸如 Dijkstra 的分流算法、Barnes-Hut n 体模拟算法或其他一些“纯”算法之类的东西。另一方面,如果您的循环体需要对非常大的数据结构执行更新,或者需要执行实际的 I/O 操作(例如,网络服务器的主循环),那么我们需要在 monad 中运行。这通常意味着就地更新是可能的,您可以遵循更常用的命令式方法。