我正在查看一些列表操作并遇到!!
:
(!!) :: [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个参数的函数?特别是因为结果应该与第二个参数具有相同的类型?
答案 0 :(得分:2)
->
是正确关联的。因此a -> b -> c
是a -> (b -> c)
。因此,您的类型
a -> (Int -> a) -> Int -> a
与
相同a -> (Int -> a) -> (Int -> a)
我们可以看到它很适合foldr
的类型。
答案 1 :(得分:1)
(对其他人的更多解释;)
const expected = [
{ value: "Angle" },
{ value: "Mass" }
]
(!!) :: [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
实现是报告版本。前言的报告版本比熟悉的朴素递归实现更有效,
由于(!!)
的优化。