为何无法用scanr替代scanl?Haskell函数调用报错排查
关于Haskell中scanl与scanr调用的类型错误问题
正常运行的scanl调用
使用scanl搭配flip(:)可以正常执行并得到结果:
ghci> scanl (flip(:)) [] [3,2,1] [[],[3],[2,3],[1,2,3]]
使用scanr时的类型错误
改用scanr调用相同参数时,出现如下类型匹配错误:
ghci> scanr (flip(:)) [] [3,2,1] <interactive>:129:8: error: • Couldn't match type ‘a’ with ‘[a]’ Expected: [[a]] -> [a] -> [a] Actual: [[a]] -> [a] -> [[a]] • In the first argument of ‘scanr’, namely ‘(flip (:))’ In the expression: scanr (flip (:)) [] [3, 2, 1] In an equation for ‘it’: it = scanr (flip (:)) [] [3, 2, 1] • Relevant bindings include it :: [[a]] (bound at <interactive>:129:1)
问题核心
scanl和scanr对传入的二元函数参数顺序要求完全相反:
scanl的类型定义:(b -> a -> b) -> b -> [a] -> [b],二元函数需要先接收累积值(b类型),再接收列表元素(a类型),最终返回新的累积值(b类型)。scanr的类型定义:(a -> b -> b) -> b -> [a] -> [b],二元函数需要先接收列表元素(a类型),再接收累积值(b类型),最终返回新的累积值(b类型)。
(:)的原生类型是a -> [a] -> [a],flip(:)将其参数反转成[a] -> a -> [a],刚好匹配scanl对二元函数的参数顺序要求(累积值是[a],列表元素是a)。但放到scanr中时,flip(:)的参数顺序和scanr的要求完全相反,导致类型推导失败,触发错误。
修改方案
不需要使用flip(:),直接用原生的(:)就能匹配scanr的参数顺序:
ghci> scanr (:) [] [3,2,1] [[1,2,3],[2,3],[3],[]]
如果一定要保留flip的使用逻辑,需要调整二元函数的适配方式,但直接使用(:)是最简洁的解决办法——scanr从右往左遍历列表元素,(:)将元素添加到累积列表的头部,最终得到从最右侧元素开始逐步构建的列表序列。
内容的提问来源于stack exchange,提问作者Juanmac
相关产品推荐
相关产品推荐

