这些函数是否递归?

我正在学习尾递归,并且在确定我的函数是否是递归的尾部方面遇到了一些困难(主要是我使用另一个函数的函数)。

我已经实现了以下两个函数,但我不确定它们是否是递归的尾部。

第一个是连接两个列表的函数。

conca list [] = list
conca [] result = result
conca (h:t) result = conca (init (h:t)) ( last(h:t):result ) 

concatenate::[a]->[a]->[a]
concatenate list1 list2 = conca list1 list2

函数中的计算在递归调用之前被处理,但是它使用last和init,它们不是尾递归的(我在http://ww2.cs.mu.oz.au/172/Haskell/tourofprelude中检查了它们的定义.html)

第二个功能是删除给定列表中给定数字的第一次出现。

invert [] result = result
invert (h:t) result = invert t (h:result)

remov n [] aux result = invert result []
remov n (h:t) aux result
        | aux==1 = remov n t 1 (h:result)
        | n==h = remov n t 1 (result)
        | otherwise = remov n t 0 (h:result)

remove n list = remov n list 0 []

参数aux(可以假设为0或1作为值)用于标记是否删除了出现。

在remove函数中,当部分结果通过递归调用传递时,列表被反转,最后列表没有第一次出现但倒置,因此它必须被反转以作为结果返回。


conca (h:t) result = conca (init (h:t)) ( last(h:t):result ) 

是一个尾部调用,但last(h:t):result作为一个未评估的thunk开始生命,所以它是一个(手形波浪),就像这些嵌套函数调用仍然在堆栈上一样。

conca模式匹配它的第一个参数,所以init将在递归调用中被评估。

conca在其第二个参数中是非严格的,所以在应用conca的递归调用时这些thunk不会被评估。


remov是尾递归,是的,但....

使用TrueFalse而不是01来使代码更清晰:

remov n [] found result = invert result []
remov n (h:t) found result
        | found = remov n t True (h:result)
        | n==h = remov n t True (result)
        | otherwise = remov n t False (h:result)

remove n list = remov n list False []

这样最好不要传递如此多的数据,减少n的拷贝并使用两个函数,而不是测试布尔参数的单个函数:

remove' n list = seek list [] where
   seek []     result = invert result []
   seek (h:t) result | h == n    = got t result
                     | otherwise = seek t (h:result)
   got []    result = invert result []
   got (h:t) result = got t (h:result)

got a result只是计算reverse result ++ a ,所以你可以写

remove'' n list = seek list [] where
   seek []     result = invert result []
   seek (h:t) result | h == n    = invert result [] ++ t
                     | otherwise = seek t (h:result)

然而,这似乎相当费力,并且仍然遍历两次。 为什么不去一个非尾巴的电话:

removeFast n [] = []
removeFast n (h:t) | h == n = t
                   | otherwise = h:removeFast n t

这有利于直接生成第一个元素而不是运行整个列表,并且一旦找到要删除的元素,快捷方式就会返回t而无需进一步计算。 根据你的处理器速度,改变length (remove 1 [1..100000]) (根据处理器速度改变零的数量length (remove 1 [1..100000])尝试比赛length (removeFast 1 [1..100000]) )。


如果你想做一个更有效的尾递归conca ,你可以使用remove技巧:

conc this result = prepend (invert this []) result

prepend [] result = result
prepend (h:t) result = prepend t (h:result)

和以前一样,你穿越this两次,一次invert荷兰国际集团它,其他的prepending它,但这仍然是一个线性算法,并且比使用更好的initlast对每个元素,这是二次。

链接地址: http://www.djcxy.com/p/80685.html

上一篇: Are these functions tail recursive?

下一篇: Tail Recursion in Haskell