我们如何在Haskell中限制递归调用?

4

我的代码出了问题,如下所示:

import Data.List

splitat _ [] = ([],[])
splitat element (head:tail)
  | element == head = ([],(head:tail))
  | otherwise = ([head]++fst(splitat element tail), snd(splitat element tail))

它将列表分割为“element”的左侧和右侧子列表,然后将其组合成元组。 但是,在第三行中,“splitat element tail”命令被调用了两次,一次通过'fst',一次通过'snd'。 是否有办法只评估此项1次以保持递归树狭窄?

提前致谢。


3
使用 head : ... 替换 [head] ++ ... - chepner
2个回答

6

是的,你可以使用let表达式或where子句。例如:

splitat :: Eq a => a -> [a] -> ([a], [a])
splitat _ [] = ([],[])
splitat x' xa@(x:xs) | x == x' = ([], xa)
                     | otherwise = (x:ys1, ys2)
    where <b>(ys1, ys2) = splitat x' xs</b>

请注意:请勿使用head :: [a] -> atail :: [a] -> [a]或其他被定义为变量的函数,因为它们会“遮蔽”现有的绑定。这使得对代码进行推理更加困难,因为人们可能会认为headtail是指这些函数,而不是变量。

3
尽管有争议,但更好的做法是从base和所有使用它们的库中清除headtail - leftaroundabout
1
@leftaroundabout:我同意,因为这些函数是不安全的 :)。 - Willem Van Onsem
听着,这两个词也是完全可以作为变量/函数名使用的,总是在我的命名空间里弄得一团糟。 - luqui

5

使用 Control.Arrow.first(或者Data.Bifunctor.firstarrow库随GHC一起提供,而我不记得是否需要先安装bifunctor):

splitat _ [] = ([],[])
splitAt e lst@(h:t) | e == h = ([], lst)
                    | otherwise = first (h:) (splitAt e t)

3
现在 Data.Bifunctor 已经包含在 base 中了;如果我看版本号没错的话,它是在 GHC 7.10 加入的(base 版本为 4.8.0.0)。 - Antal Spector-Zabusky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接