【问题标题】:Finding the index of a given element using tail recursion使用尾递归查找给定元素的索引
【发布时间】:2015-08-21 04:27:36
【问题描述】:

我正在尝试编写一个函数来使用尾递归查找给定元素的索引。假设列表包含数字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

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

【问题讨论】:

  • 感谢您的推荐。 Learn You a Haskell for Great Good! 是我目前用来学习 Haskell 的工具。您不应该让 cmets 假设您无法承担的事情。对你来说可能微不足道的问题,对其他人来说可能不是微不足道的。因此,我建议您将此类“垃圾邮件”cmets 留给自己;)

标签: haskell


【解决方案1】:

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

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)

【讨论】:

  • 另外,foo 不需要它的第一个参数 - 它在递归调用期间永远不会改变,您可以使用外部 a 代替。
【解决方案2】:

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

一般来说,这不是一个好主意。一个更好的练习是这样的:

  1. 弄清楚如何使用库函数来实现它。
  2. 弄清楚如何自行实现您在第 1 步中使用的任何库函数。

通过这种方式,您将学习三项关键技能:

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

但是,在这种情况下,您的 whatIndexelemIndex in Data.List 或多或少是相同的函数,因此您的问题归结为编写您自己的此库函数版本。

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

  1. 您编写了一个 辅助 函数,与“前端”函数相比,该函数需要一个(或更多)额外参数来跟踪额外信息。
  2. 然后将“真实”函数定义为对辅助函数的调用。

所以对于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 中表现良好是很棘手的。尾递归在 strict(非惰性)函数式语言中是一个有用的技巧,但在 Haskell 中则不然,因为:

  1. Haskell 中的尾递归函数仍然可以爆栈,
  2. 非尾递归函数可以在恒定空间中运行。

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 = ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-22
    • 2020-12-21
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 2022-06-28
    • 1970-01-01
    相关资源
    最近更新 更多