使用foldr和函数应用程序($)解释查找列表的第K个元素

时间:2013-01-25 16:38:44

标签: haskell

我目前正在学习哈斯克尔的第6章......刚刚开始研究99个问题。

第三个问题是找到列表的第K个元素。我已使用takezip实现了它。

我遇到的问题是了解所提供的替代解决方案:

elementAt''' xs n = head $ foldr ($) xs 
                     $ replicate (n - 1) tail

我“几乎在那里”,但我不太明白。我知道$的定义,但是..你可以向我解释执行上述代码的顺序。此外,这经常被用作各种问题的解决方案,这是惯用的还是只是......杂技?

2 个答案:

答案 0 :(得分:7)

如果扩展foldr

的定义
foldr f z (x1:x2:x3:...:[]) = x1 `f` x2 `f` x3 `f`... `f` z

您看到elementAt'''变为

elementAt''' xs n = head (tail $ tail $ ... $ tail $ xs)

(注意:如果索引从0开始,则应为replicate n tail而不是replicate (n-1) tail

因此,您将tail应用于xs适当的次数,如果drop (n-1) xs足够长,则与xs的结果相同,但如果是head则会引发错误太短了,并取结果列表的xs(如果drop (n-1)太短,后者也会引发n-1的错误。

它的作用是

  • 放弃列表的第一个元素
  • 丢弃结果列表的第一个元素(完全head次)
  • 获取结果列表的foldr
  

此外,这是否经常被用作各种问题的解决方案,这是惯用的还是只是......杂技

在这种情况下,只是杂技。 tail必须扩展完整的应用程序才能在{{1}} s之前返回到前端,因此效率低于直接遍历。

答案 1 :(得分:6)

将其分解为两个主要步骤。首先,该函数复制tail (n-1)次。所以你最终会得到像

这样的东西
elementAt''' xs n = head $ foldr ($) xs [tail, tail, tail, ..., tail]

现在,列表中foldr的定义扩展为类似

foldr f x [y1, y2, y3, ..., yn] = (y1 `f` (y1 `f` (... (yn `f` x))) ...)

因此,该折叠将扩展为(将f替换为$,将所有y替换为tail

foldr ($) xs [tail, tail, tail, ..., tail] 
= (tail $ (tail $ (tail $ ...  (tail xs))) ... )
{- Since $ is right associative anyway -}
= tail $ tail $ tail $ tail $ ... $ tail xs

(n-1)tail次调用。服用n-1后 它只是提取剩余列表的第一个元素并给出它 回来。

另一种写作方式可以使作品更明确(在我看来) 像这样

elementAt n = head . (foldr (.) id $ replicate (n-1) tail)