Chi*_*ien 3 monads haskell list-comprehension
我正在尝试编写一些自定义类型Martix a,它基本上是列表列表[[a]]。当我尝试实现一个名为 的函数时colAt,它应该给出矩阵的垂直元素,我首先使用了列表推导式:
colAt :: Int -> Matrix a -> [a]
colAt c m = [ e | r <- m, e <- r !! c ]
Run Code Online (Sandbox Code Playgroud)
但是 Ghci 告诉我
Occurs check: cannot construct the infinite type: a ~ [a]
In the expression: r !! c
Run Code Online (Sandbox Code Playgroud)
虽然 do 符号与
colAt :: Int -> Matrix a -> [a]
colAt c m = do
r <- m
return (r !! c)
Run Code Online (Sandbox Code Playgroud)
是什么导致了这个错误?我认为基本上列表理解是列表符号的语法糖,但是鉴于此错误,我的理解是错误的?
您的理解是完全正确的:列表推导式确实只是do表示法的语法糖!问题是您没有正确地对列表理解进行脱糖。
首先,让我们重复列表推导以供参考:
colAt :: Int -> Matrix a -> [a]
colAt c m = [ e | r <- m, e <- r !! c ]
Run Code Online (Sandbox Code Playgroud)
现在,我将部分脱糖,以将其移到r <- m理解之外:
colAt :: Int -> Matrix a -> [a]
colAt c m = do
r <- m
[e | e <- r !! c]
Run Code Online (Sandbox Code Playgroud)
这很容易完全脱糖:
colAt :: Int -> Matrix a -> [a]
colAt c m = do
r <- m
e <- r !! c
e
Run Code Online (Sandbox Code Playgroud)
与正确的实现进行比较:
colAt :: Int -> Matrix a -> [a]
colAt c m = do
r <- m
return (r !! c)
Run Code Online (Sandbox Code Playgroud)
这里的问题现在很明显了。在正确的实现中m,对于每个项目r <- m,依次找到元素r !! c :: a,将它包装在一个列表中,然后返回它。相比之下,您的实现r <- m正确提取了每个项目,然后尝试提取“列表”的每个“元素” r !! c :: a——实际上不一定是列表,给出了您看到的类型错误。修复很简单:在正确的实现中,只需添加一个return, 给[ e | r <- m, e <- return (r !! c) ]。或者,更简单地说,使用与[x | x <- return l]相同的事实[l],您可以更简单地将其重写为[ r !! c | r <- m ]。
如果您编写e <- r !! c,它r !! c应该是一个列表,因为您正在枚举该列表,但它r !! c是一个项目(类型a),因此只有在您使用例如 a 时才有效Matrix [a]。
这里不需要枚举,可以移到r !! c“yield”部分:
colAt :: Int -> Matrix a -> [a]
colAt c m = [ r !! c | r <- m ]Run Code Online (Sandbox Code Playgroud)
但是你在这里做的是一个映射,所以你可以使用map :: (a -> b) -> [a] -> [b]:
colAt :: Int -> Matrix a -> [a]
colAt c = map (!! c)Run Code Online (Sandbox Code Playgroud)