我目前正在学习哈斯克尔的第6章......刚刚开始研究99个问题。
第三个问题是找到列表的第K个元素。我已使用take
和zip
实现了它。
我遇到的问题是了解所提供的替代解决方案:
elementAt''' xs n = head $ foldr ($) xs
$ replicate (n - 1) tail
我“几乎在那里”,但我不太明白。我知道$
的定义,但是..你可以向我解释执行上述代码的顺序。此外,这经常被用作各种问题的解决方案,这是惯用的还是只是......杂技?
答案 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)