使用尾递归查找给定元素的索引

时间:2013-02-13 00:11:55

标签: haskell

我正在尝试编写一个函数来使用尾递归来查找给定元素的索引。可以说列表中包含110的数字,我正在搜索5,然后输出应为4。我遇到的问题是使用尾递归'计数'。但是,在这种情况下,我甚至不确定是否需要对递归调用的数量进行“计数”。我尝试使用!!没有帮助,因为它返回特定位置的元素。我需要函数来返回特定元素的位置(正好相反)。

我一直想把这个拿出一个小时了。

代码:

  whatIndex a [] = error "cannot search empty list"
  whatIndex a (x:xs) = foo a as
    where
       foo m [] = error "empty list"
       foo m (y:ys) = if m==y then --get index of y
                         else foo m ys

注意:我试图在不使用库函数的情况下实现它

2 个答案:

答案 0 :(得分:4)

您的辅助函数需要一个额外的计数参数。

whatIndex a as = foo as 0
  where
    foo [] _ = error "empty list"
    foo (y:ys) c
        | a == y    = c
        | otherwise = foo ys (c+1)
顺便说一下,给这个函数一个Maybe返回类型而不是使用错误的形式更好。这也是elemIndex的工作原理,这是有充分理由的。这看起来像

whatIndex a as = foo as 0
  where
    foo [] _ = Nothing
    foo (y:ys) c
        | a == y    = Just c
        | otherwise = foo ys (c+1)

答案 1 :(得分:3)

  

注意:我试图在不使用库函数的情况下实现它

这一般不是一个好主意。这是一个更好的练习:

  1. 弄清楚如何使用库函数实现它。
  2. 了解如何实现您自己在步骤1中使用的任何库函数。
  3. 通过这种方式,您将学习三项关键技能:

    • 什么是标准库函数,以及它们何时有用的示例。
    • 如何将问题分解成小块
    • 如何编写库中的基本功能。

    但是,在这种情况下,您的whatIndex或多或少与elemIndex in Data.List的功能相同,因此您的问题会减少到编写自己的此库函数版本。

    这里的诀窍是你想要在递减列表时增加一个计数器。有一种编写尾递归函数的标准技术,称为累积参数。它的工作原理如下:

    1. 您编写了一个辅助函数,与“前端”函数相比,它需要一个额外的参数(或更多)来跟踪额外信息。
    2. 然后,您将“真实”功能定义为对辅助功能的调用。
    3. 因此对于elemIndex,辅助函数将是这样的(使用i作为当前元素索引的累积参数):

      -- I'll leave the blanks for you to fill.
      elemIndex' i x []      = ...
      elemIndex' i x (x':xs) = ...
      

      然后“驱动程序”功能是这样的:

      elemIndex x xs = elemIndex 0 x xs
      

      但是这里有一个严重的问题我必须提到:让这个函数在Haskell中表现良好是很棘手的。尾递归是严格(非惰性)函数式语言中的一个有用技巧,但在Haskell中并不是很多,因为:

      1. Haskell中的尾递归函数仍然可以破坏堆栈,
      2. 非尾递归函数可以在恒定空间中运行。
      3. This older answer of mine显示了第二点的示例。

        因此,在您的情况下,非尾递归解决方案可能是您可以在恒定空间中运行的最简单的解决方案(即,不会将堆栈放在长列表中):

        elemIndex x xs = elemIndex' x (zip xs [0..])
        
        elemIndex' x pairs = snd (find (\(x', i) -> x == x') pairs)
        
        -- | Combine two lists by pairing together their first elements, their second 
        -- elements, etc., until one of the lists runs out.
        --
        -- EXERCISE: write this function on your own!
        zip :: [a] -> [b] -> [(a, b)]
        zip xs ys = ...
        
        -- | Return the first element x of xs such that pred x == True.  Returns Nothing if
        -- there isn't one, Just x if there is one.
        --
        -- EXERCISE: write this function on your own!
        find :: (a -> Bool) -> [a] -> Maybe a
        find pred xs = ...