2

如果某个变量小于某个阈值,则在实现算法时会经常发生这种情况,您会在 while 循环的每次迭代中进行检查。

在命令式语言中,它看起来像:

 while ( x < TRESHOLD ) {
      x = usefullStuff();
 }

当然,有用的东西会以某种方式影响 x...

您如何利用函数式编程范式将此构造转换为 haskell?

4

4 回答 4

6

尽管我不知道你的程序做了什么,但我有一种感觉,你会想要稍微不同地构建你的逻辑。查看论文Why Functional Programming Mattes的第 4 节- 了解一些想法。它处理类似的情况,涉及使用牛顿法找到方程的根。在那里,职责被划分,以便循环逻辑与逐次逼近的生成分离。

如果usefulStuff是一个单子 - 即一个有副作用的函数,你将不得不使用这样的东西:

whileLoop x
  | x < THRESHOLD = return x
  | otherwise     = do x <- usefulStuff
                       whileLoop x
于 2012-11-30T23:33:13.060 回答
5

来自 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
于 2012-11-30T23:33:32.473 回答
3

按照说明走

如果某个变量小于某个阈值,则在实现算法时会经常发生这种情况,您会在 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

将捕获模式。

于 2012-11-30T23:15:52.460 回答
3

第一:在命令式语言中,您通常会编写一个while循环 who's body updates x。在 Haskell 中,这不可能发生。因此,我们需要稍微不同地构建我们的算法。

您通常会做的是将循环体转换为一个以参数为参数的函数x,并返回新x值作为其结果。然后,您可以使用untilPrelude 中的函数反复应用该函数,直到满足某些条件。或者,您可以使用iterate生成所有结果的无限列表,然后使用takeWhile或一些类似的功能来扫描该列表以查找感兴趣的项目。

现在,如果你的循环体实际上更新了几个变量会发生什么?那么,在这种情况下,您可以将所有这些变量打包到一个数据结构中,然后应用上述过程。不用说,如果你有很多这些变量,这可能会变得相当复杂。尽量避免大而复杂的循环。(在任何语言中,不仅仅是 Haskell。)

你使用了“算法”这个词,这让我怀疑你在谈论诸如 Dijkstra 的分流算法、Barnes-Hut n 体模拟算法或其他一些“纯”算法之类的东西。另一方面,如果您的循环体需要对非常大的数据结构执行更新,或者需要执行实际的 I/O 操作(例如,网络服务器的主循环),那么我们需要在 monad 中运行。这通常意味着就地更新可能的,您可以遵循更常用的命令式方法。

于 2012-12-01T13:36:25.593 回答