更新函数以包括尾递归

时间:2017-05-15 16:39:14

标签: haskell recursion functional-programming tail-recursion

我有以下函数来确定给定列表的最大元素。

def ResponseMessage = prev.getResponseDataAsString();

然而,它不使用尾递归,我希望它能这样做。 我试图使用累加器来遵守Haskell中的尾递归原则。

maxList :: Ord a => [a] -> a
maxList l =
    let iMaxList :: Ord a => [a] -> a
        iMaxList [] = error( "Empty list" )
        iMaxList [x] = x
        iMaxList ( x:xs )
            | x > t = x
            | otherwise = t
            where t = iMaxList xs
    in iMaxList l

然而,由于这个警卫maxList :: Ord a => [a] -> a maxList ( x:xs ) = loop( xs, x ) where loop( x:xs, m ) | ( null xs ) = m | ( x >= m ) = loop( xs, x ) | otherwise = loop( xs, m ) ,它在逻辑上失败了。实际上,如果我们采用列表(null xs) = m,则永远不会评估[1,2,3,4]

我该如何解决?

3 个答案:

答案 0 :(得分:1)

我想这就是你要搜索的内容:

(0, u'0.559*"delivery" + 0.124*"area" + 0.018*"mile" + 0.016*"option" + 0.012*"partner" + 0.011*"traffic" + 0.011*"hub" + 0.011*"thanks" + 0.010*"city" + 0.009*"way"')
(1, u'0.397*"package" + 0.073*"address" + 0.055*"time" + 0.047*"customer" + 0.045*"apartment" + 0.037*"delivery" + 0.031*"number" + 0.026*"item" + 0.021*"support" + 0.018*"door"')
(2, u'0.190*"time" + 0.127*"order" + 0.113*"minute" + 0.075*"pickup" + 0.074*"restaurant" + 0.031*"food" + 0.027*"support" + 0.027*"delivery" + 0.026*"pick" + 0.018*"min"')
(3, u'0.072*"code" + 0.067*"gps" + 0.053*"map" + 0.050*"street" + 0.047*"building" + 0.043*"address" + 0.042*"navigation" + 0.039*"access" + 0.035*"point" + 0.028*"gate"')
(4, u'0.434*"hour" + 0.068*"time" + 0.034*"min" + 0.032*"amount" + 0.024*"pay" + 0.019*"gas" + 0.018*"road" + 0.017*"today" + 0.016*"traffic" + 0.014*"load"')
(5, u'0.245*"route" + 0.154*"warehouse" + 0.043*"minute" + 0.039*"need" + 0.039*"today" + 0.026*"box" + 0.025*"facility" + 0.025*"bag" + 0.022*"end" + 0.020*"manager"')
(6, u'0.371*"location" + 0.110*"pick" + 0.097*"system" + 0.040*"im" + 0.038*"employee" + 0.022*"evening" + 0.018*"issue" + 0.015*"request" + 0.014*"while" + 0.013*"delivers"')
(7, u'0.182*"schedule" + 0.181*"please" + 0.059*"morning" + 0.050*"application" + 0.040*"payment" + 0.026*"change" + 0.025*"advance" + 0.025*"slot" + 0.020*"date" + 0.020*"tomorrow"')
(8, u'0.138*"stop" + 0.110*"work" + 0.062*"name" + 0.055*"account" + 0.046*"home" + 0.043*"guy" + 0.030*"address" + 0.026*"city" + 0.025*"everything" + 0.025*"feature"') 

该函数使用相同的列表来处理存储到目前为止找到的最大数字。它似乎符合尾递归定义,即:递归调用是函数计算中的最后一件事。

答案 1 :(得分:1)

listMax :: Ord a => [a] -> a
listMax [] = error "Tried to find maximum of an empty list."
listMax (x:xs) = listMax' xs x where
  listMax' :: Ord a => [a] -> a -> a
  listMax' [] y = y
  listMax' (x:xs) y | x > y     = listMax' xs x
                    | otherwise = listMax' xs y

在这种情况下,y是累加参数,它保存到目前为止找到的最大值。正确性的简要证明:算法终止,因为每个尾递归调用从输入列表中删除一个元素,直到它为空。它返回的y的最终值是最大值,因为对于输入中的每个其他元素xy > xy > z > xz之后的x { {1}}之前和y之前。 (这假定>是可传递的。)

您也可以这样编写辅助函数:

listMax' :: Ord a => [a] -> a -> a
listMax' [] y = y
listMax' (x:xs) y = listMax' xs (max x y)

这个实现做了同样的事情:

listMax2 :: Ord a => [a] -> a
listMax2 [] = error "Tried to find maximum of an empty list."
listMax2 list = foldl1 max list

foldl1函数是从前到后的尾递归惰性求值,但在这种情况下严格的foldl1'版本或foldr1可能更有效。第一个版本比懒惰更接近严格评估。

答案 2 :(得分:0)

不要担心。

我在文件中写了以下内容:

module MaxList (maxList) where

import Data.List

maxList :: Ord a => [a] -> a
maxList = foldl1' max

然后我用-O2 -ddump-simpl编译它,看看优化的Core。经过一些清理后,GHC会生成许多名称难以阅读的变量 - 生成的代码如下所示:

maxList [] = error "empty list"
maxList (x:xs) = go xs x
    where go ys y =
              case ys of
                   [] -> y;
                   (z:zs) -> case y of  -- force y to WHNF before continuing
                                  _ -> go zs (max y z)

go是尾递归的。实际上它与@Davislor's answer中的代码相同!我使用了foldl1' - 一个高级控件结构 - 如果你想编写一个尾递归循环,GHC就会生成你手写的代码。

Haskell的哲学是你应该使用高级工具,如folds,展开,monad,类等,并依靠编译器生成好的代码。编写代码肯定有一种艺术,GHC可以很好地进行优化 - 你不总是免费获得它 - 但你通常不需要将高级结构展开到低级循环中因为GHC擅长这一点。

相关问题