我正在查看一些列表操作并遇到!!
:
(!!) :: [a] -> Int -> a
xs !! n
| n < 0 = negIndex
| otherwise = foldr (\x r k -> case k of
0 -> x
_ -> r (k-1)) tooLarge xs n
函数(\x r k -> ...)
的类型为a -> (Int -> a) -> Int -> a
,但foldr
采用的函数只能接受两个参数:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr k z = go
where
go [] = z
go (y:ys) = y `k` go ys
有人可以向我解释为什么foldr
接受一个带有以下类型a -> (Int -> a) -> Int -> a
的3个参数的函数吗?特别是因为结果应该与第二个参数具有相同的类型?
->
是正确联想的。所以a -> b -> c
是a -> (b -> c)
。因此,你的类型
a -> (Int -> a) -> Int -> a
是相同的
a -> (Int -> a) -> (Int -> a)
我们可以看到它非常适合foldr
的类型。
(对他人的更多解释;)
(!!) :: [a] -> Int -> a
xs !! n
| n < 0 = negIndex
| otherwise = foldr (\x r k -> case k of
0 -> x
_ -> r (k-1)) tooLarge xs n
foldr :: (a -> b -> b) -> b -> [a] -> b
-- ^1 ^2
foldr
通常会产生累积(?)值。在这种情况下,foldr
生成b
类型的累积函数((Int -> a)
)! foldr ... tooLarge xs
被评估为累积函数,并且这个累积函数(^2
)采用n
参数。 ^1
是tooLarge
的功能。有趣的是,这种累积函数的积累取决于自由变量n
(即k
)的值。
例如,['a', 'b', 'c'] !! 2
的评估如下:
\x r k
= \'a' r 2 -> r (2-1)
(r
尚未知晓,并推动进一步评估。)
\x r k
= \'b' r 1 -> r (1-1)
\x r k
= \'c' r 0 -> 'c'
['a', 'b', 'c'] !! 3
是这样的:
\x r k
= \'a' r 3 -> r (3-1)
\x r k
= \'b' r 2 -> r (2-1)
\x r k
= \'c' r 1 -> r (1-1)
(r
原来是tooLarge
。)= tooLarge (1-1)
(错误!)
您可以检查调试跟踪:
module Main where
import Debug.Trace
tooLarge _ = errorWithoutStackTrace "!!!: index too large"
negIndex = errorWithoutStackTrace "!!!: negative index"
(!!!) :: Show a => [a] -> Int -> a
xs !!! n
| n < 0 = negIndex
| otherwise = foldr (\x r k -> trace ("x: " ++ show x ++ ", k: " ++ show k) $
case k of
0 -> x
_ -> r (k-1)) tooLarge xs n
main = do
print $ ['a', 'b', 'c'] !!! 2
print $ ['a', 'b', 'c'] !!! 3
-- x: 'a', k: 2
-- x: 'b', k: 1
-- x: 'c', k: 0
-- 'c'
-- x: 'a', k: 3
-- x: 'b', k: 2
-- x: 'c', k: 1
-- sample: !!!: index too large
(!!)
implementation is a report version. The report version of the prelude is more efficient than a familiar naive recursive implementation,
due to optimizations of foldr
.