Haskell函数组合与部分应用困惑:自定义nub函数化简疑问
关于Haskell函数组合与部分应用结合的问题解答
你的化简过程完全正确,咱们拆解每一步的逻辑:
- 初始lambda:
(\x ys -> x : (filter (/=x) ys))
把中缀运算符:换成前缀形式(:),就得到(\x ys -> (:) x (filter (/=x) ys));而(x :)是(:)部分应用x后的结果(相当于\ys -> x : ys),所以这一步可以改写为(\x ys -> (x :) (filter (/=x) ys))。 - 下一步化简:
根据函数组合的定义(f . g) z = f (g z),这里f是(x :),g是filter (/=x),z是ys,所以(x :) (filter (/=x) ys)等价于((x :).filter (/=x)) ys。因此可以把参数ys柯里化掉,得到(\x -> (x :).filter (/=x)),这完全符合Haskell的函数柯里化规则。
关于进一步化简的问题
到这一步已经是最简洁的形式了,没办法继续拆解。原因在于x是两个部分应用的共享参数:(x :)需要x作为列表的头部元素,filter (/=x)需要x作为过滤的基准值,这个共享参数无法从函数组合中剥离出来变成无参数的组合。你觉得需要三个输入的感觉是对的,但实际上这个化简后的lambda类型是Eq a => a -> [a] -> [a],正好匹配foldr要求的第一个参数类型(foldr的类型是(a -> b -> b) -> b -> [a] -> b,这里b就是[a])。
学习技巧
- 把所有中缀运算符换成前缀形式,比如
x : ys写成(:) x ys、a /= b写成(/=) a b,这样更容易识别部分应用的可能性。 - 牢记函数组合的核心规则:
(f . g) = \x -> f (g x),反过来,凡是能写成\x -> f (g x)形式的lambda,都可以替换成f . g。 - 多写类型签名辅助理解,比如给化简后的lambda加上类型:
(\x -> (x :).filter (/=x)) :: Eq a => a -> [a] -> [a],通过类型对应关系能更清晰看到参数的流向。 - 用GHCi实时验证:输入
:t (x :)、:t filter (/=x)、:t (x :).filter (/=x)查看类型变化,直观感受部分应用与组合的结合效果。
内容的提问来源于stack exchange,提问作者Metiwi
相关产品推荐
相关产品推荐

