Haskell函数组合运算符(.)多步组合的工作原理及疑问解析
函数组合运算符(.)的多步组合困惑解答
先明确运算符定义
函数组合运算符(.)
-- 结合性:右结合(infixr 9) (.) :: (b -> c) -> (a -> b) -> a -> c (f . g) x = f(g x) -- 等价写法 f . g = \x -> f(g x)
它的作用是把两个函数组合成一个新函数:新函数接收参数x后,先把x传给右侧函数g,再把g的结果传给左侧函数f。
加法运算符(+)
(+) :: Num a => a -> a -> a
它是左结合(infixl 6)的,所以多步运算时会从左往右分组:
-- 实际分组方式 4 + 3 + 2 = (4 + 3) + 2 -- 计算过程 -- 7 + 2 = 9
你的困惑点
你疑惑为什么执行replicate 4 . reverse . take 3 $ map (*2) [1..]能得到正确结果,按左结合的思路会误以为中间步骤是replicate 4 . [6,4,2]——但(.)要求两个参数都是函数,这明显不符合定义,为什么没报错?
核心原因:(.)是右结合运算符
(.)的定义里标注了infixr 9,意思是右结合,所以多步组合时会从右往左分组,而不是像(+)那样从左往右。
实际分组与执行过程
replicate 4 . reverse . take 3的实际分组是:
replicate 4 . (reverse . take 3)
每一步都是两个函数组合成新函数:
- 先组合
reverse和take 3:得到一个新函数,它接收一个列表,先取前3个元素,再反转这个子列表。 - 再把
replicate 4和上面的新函数组合:得到另一个新函数,它接收一个列表,先执行reverse . take 3的逻辑,再把结果复制4次。
最后,$运算符的作用是把右侧的map (*2) [1..](生成无限列表[2,4,6,8,...])作为参数传给前面组合好的整个函数,完整的执行流程是:
-- 分步拆解 let step1 = map (*2) [1..] -- 得到[2,4,6,8,...] let step2 = take 3 step1 -- 得到[2,4,6] let step3 = reverse step2 -- 得到[6,4,2] let step4 = replicate 4 step3 -- 得到[[6,4,2],[6,4,2],[6,4,2],[6,4,2]]
全程都是函数之间的组合,没有出现函数和值直接用(.)组合的情况,自然不会报错。
内容的提问来源于stack exchange,提问作者Akari
相关产品推荐
相关产品推荐

