用 pointfree 风格写 f ?
Write f in pointfree-style?
假设我有函数
g :: a -> b, h :: a -> c
和
f :: b -> c -> d.
是否可以写出函数
f' :: a -> a -> d
由
提供
f' x y = f (g x) (h y)
在点free style?.
可以写函数
f' a -> d, f' x = f (g x) (h x)
点自由风格通过设置
f' = (f <$> g) <*> h
但我不知道如何处理更一般的情况。
已转换
f' x y = f (g x) (h y)
进入
f' = (. h) . f . g
随转换流程
f' = id (fix (const (flip ((.) . f . g) h)))
f' = fix (const (flip ((.) . f . g) h))
f' = fix (const ((. h) . f . g))
f' = (. h) . f . g
我们有:
k x y = (f (g x)) (h y)
并且我们希望以无点样式编写k
。
传递给 k
的第一个参数是 x
。我们需要用 x
做什么?好吧,首先我们需要调用 g
,然后调用 f
,然后做一些花哨的事情将其应用到 (h y)
.
k = fancy . f . g
这是什么fancy
?嗯:
k x y = (fancy . f . g) x y
= fancy (f (g x)) y
= f (g x) (h y)
所以我们渴望fancy z y = z (h y)
。 Eta-reducing,我们得到fancy z = z . h
,或fancy = (. h)
.
k = (. h) . f . g
一种更自然的思考方式可能是
┌───┐ ┌───┐
x ───│ g │─── g x ───│ │
/ └───┘ │ │
(x, y) │ f │─── f (g x) (h y)
\ ┌───┐ │ │
y ───│ h │─── h y ───│ │
└───┘ └───┘
└──────────────────────────────┘
k
k = curry ((g *** h) >>> uncurry f)
这比 (. h) . f. g
.
稍长,但更容易理解
首先,稍微重写 f'
以获取一个元组而不是两个参数。 (换句话说,我们正在取消您原来的 f'
。)
f' (x, y) = f (g x) (h y)
您可以使用 fst
和 snd
来分开一个元组,而不是对其进行模式匹配:
f' t = f (g (fst t)) (h (snd t))
使用函数组合,上面变成
f' t = f ((g . fst) t) ((h . snd) t)
嘿,这看起来很像你可以使用应用风格制作的无点版本:
f' = let g' = g . fst
h' = h . snd
in (f <$> g') <*> h'
剩下的唯一问题是f' :: (a, a) -> d
。您可以通过显式柯里化来解决此问题:
f' :: a -> a -> d
f' = let g' = g . fst
h' = h . snd
in curry $ (f <$> g') <*> h'
(顺便说一下,这与 Lynn 添加的 Control.Arrow
解决方案非常相似。)
将"three rules of operator sections"应用于(.)
函数组合运算符,
(.) f g = (f . g) = (f .) g = (. g) f -- the argument goes into the free slot
-- 1 2 3
这可以通过几个简单的机械步骤得出:
k x y = (f (g x)) (h y) -- a (b c) === (a . b) c
= (f (g x) . h) y
= (. h) (f (g x)) y
= (. h) ((f . g) x) y
= ((. h) . (f . g)) x y
最后,(.)
是关联的,因此可以删除内部括号。
一般的过程是力求达到可以进行eta缩减的情况,即如果参数顺序相同并且在任何括号之外,我们可以去掉它们:
k x y = (......) y
=>
k x = (......)
Lather, rinse,重复。
另一个技巧是将两个参数变成一个,反之亦然,用等式
curry f x y = f (x,y)
那么,你的
f (g x) (h y) = (f.g) x (h y) -- by B-combinator rule
= (f.g.fst) (x,y) ((h.snd) (x,y))
= (f.g.fst <*> h.snd) (x,y) -- by S-combinator rule
= curry (f.g.fst <*> h.snd) x y
这与 相同,但更简洁。
所以,你看,你的 (f.g <*> h) x
1 就变成了 (f.g.fst <*> h.snd) (x,y)
。一样的区别。
1(因为对于函数,(<$>) = (.)
)
(g ~> h ~> id) f
f $* g $$ h *$ id
lurryA @N2 (f <$> (g <$> _1) <*> (h <$> _2))
lurryA @N5 (_1 <*> (_2 <*> _4) <*> (_3 <*> _5)) f g h
相关文章
- Semantic Editor Combinators,科纳尔·埃利奥特,2008/11/24
- Pointless fun,马特·赫利格,2008/12/03
假设我有函数
g :: a -> b, h :: a -> c
和
f :: b -> c -> d.
是否可以写出函数
f' :: a -> a -> d
由
提供f' x y = f (g x) (h y)
在点free style?.
可以写函数
f' a -> d, f' x = f (g x) (h x)
点自由风格通过设置
f' = (f <$> g) <*> h
但我不知道如何处理更一般的情况。
已转换
f' x y = f (g x) (h y)
进入
f' = (. h) . f . g
随转换流程
f' = id (fix (const (flip ((.) . f . g) h)))
f' = fix (const (flip ((.) . f . g) h))
f' = fix (const ((. h) . f . g))
f' = (. h) . f . g
我们有:
k x y = (f (g x)) (h y)
并且我们希望以无点样式编写k
。
传递给 k
的第一个参数是 x
。我们需要用 x
做什么?好吧,首先我们需要调用 g
,然后调用 f
,然后做一些花哨的事情将其应用到 (h y)
.
k = fancy . f . g
这是什么fancy
?嗯:
k x y = (fancy . f . g) x y
= fancy (f (g x)) y
= f (g x) (h y)
所以我们渴望fancy z y = z (h y)
。 Eta-reducing,我们得到fancy z = z . h
,或fancy = (. h)
.
k = (. h) . f . g
一种更自然的思考方式可能是
┌───┐ ┌───┐
x ───│ g │─── g x ───│ │
/ └───┘ │ │
(x, y) │ f │─── f (g x) (h y)
\ ┌───┐ │ │
y ───│ h │─── h y ───│ │
└───┘ └───┘
└──────────────────────────────┘
k
k = curry ((g *** h) >>> uncurry f)
这比 (. h) . f. g
.
首先,稍微重写 f'
以获取一个元组而不是两个参数。 (换句话说,我们正在取消您原来的 f'
。)
f' (x, y) = f (g x) (h y)
您可以使用 fst
和 snd
来分开一个元组,而不是对其进行模式匹配:
f' t = f (g (fst t)) (h (snd t))
使用函数组合,上面变成
f' t = f ((g . fst) t) ((h . snd) t)
嘿,这看起来很像你可以使用应用风格制作的无点版本:
f' = let g' = g . fst
h' = h . snd
in (f <$> g') <*> h'
剩下的唯一问题是f' :: (a, a) -> d
。您可以通过显式柯里化来解决此问题:
f' :: a -> a -> d
f' = let g' = g . fst
h' = h . snd
in curry $ (f <$> g') <*> h'
(顺便说一下,这与 Lynn 添加的 Control.Arrow
解决方案非常相似。)
将"three rules of operator sections"应用于(.)
函数组合运算符,
(.) f g = (f . g) = (f .) g = (. g) f -- the argument goes into the free slot
-- 1 2 3
这可以通过几个简单的机械步骤得出:
k x y = (f (g x)) (h y) -- a (b c) === (a . b) c
= (f (g x) . h) y
= (. h) (f (g x)) y
= (. h) ((f . g) x) y
= ((. h) . (f . g)) x y
最后,(.)
是关联的,因此可以删除内部括号。
一般的过程是力求达到可以进行eta缩减的情况,即如果参数顺序相同并且在任何括号之外,我们可以去掉它们:
k x y = (......) y
=>
k x = (......)
Lather, rinse,重复。
另一个技巧是将两个参数变成一个,反之亦然,用等式
curry f x y = f (x,y)
那么,你的
f (g x) (h y) = (f.g) x (h y) -- by B-combinator rule
= (f.g.fst) (x,y) ((h.snd) (x,y))
= (f.g.fst <*> h.snd) (x,y) -- by S-combinator rule
= curry (f.g.fst <*> h.snd) x y
这与
所以,你看,你的 (f.g <*> h) x
1 就变成了 (f.g.fst <*> h.snd) (x,y)
。一样的区别。
1(因为对于函数,(<$>) = (.)
)
(g ~> h ~> id) f
f $* g $$ h *$ id
lurryA @N2 (f <$> (g <$> _1) <*> (h <$> _2))
lurryA @N5 (_1 <*> (_2 <*> _4) <*> (_3 <*> _5)) f g h
相关文章
- Semantic Editor Combinators,科纳尔·埃利奥特,2008/11/24
- Pointless fun,马特·赫利格,2008/12/03