有一天我很无聊,想锻炼一下大脑,所以我决定做99 个 Haskell 问题,但我限制自己以无点的方式做。当我以无点风格做事时,似乎经常出现的一个问题是:如何将多个函数应用于相同的值,同时将每个结果保持为独立实体?使用尖头符号:
foobar x = [id x, reverse x]
到目前为止,我以无点表示法提出了什么:
foobar' = `map` [id, reverse] ($ x)
我似乎无法从x
那里结束。
有一天我很无聊,想锻炼一下大脑,所以我决定做99 个 Haskell 问题,但我限制自己以无点的方式做。当我以无点风格做事时,似乎经常出现的一个问题是:如何将多个函数应用于相同的值,同时将每个结果保持为独立实体?使用尖头符号:
foobar x = [id x, reverse x]
到目前为止,我以无点表示法提出了什么:
foobar' = `map` [id, reverse] ($ x)
我似乎无法从x
那里结束。
其他人已经发布了如何使用Reader
monad 来做到这一点,但这不是唯一的方法。事实证明,您的第二个功能非常接近。我想你是想发帖
foobar' x = (`map` [id, reverse]) ($ x)
由于x
已经在最右边的位置附近,因此您就快到了。首先,将部分($ x)
转换为函数,因为它更容易使用:
-- by the definition of a right operator section
foobar'2 x = (`map` [id, reverse]) (\y -> ($) y x)
接下来,x
通过将一个新变量带入作用域,并将函数应用于x
-- lambda abstraction I think...
foobar'2 x = (`map` [id, reverse]) $ (\z y -> ($) y z) x
将此应用程序重写为函数组合,然后您可以 eta reduce:
-- by definition of '.'
foobar'3 x = (`map` [id, reverse]) . (\z y -> ($) y z) $ x
-- eta reduction
foobar'4 = (`map` [id, reverse]) . (\z y -> ($) y z)
最后,请注意我们可以用函数替换 lambda
-- by definition of `flip`
foobar'5 = (`map` [id,reverse]) . flip ($)
你有一个无点形式。
您将对Applicative
reader monad 的实例感兴趣:
instance Applicative (e ->)
使用它,您可以轻松地分发参数:
liftA2 (+) sin cos 3
这里sin
和cos
是函数,它们都接收值 3。然后使用 组合各个结果(+)
。您可以进一步将其与 的Category
实例结合使用,但和(->)
的专用版本已经在.(.)
id
Prelude
背景: 的Applicative
实例(e ->)
真正代表了 SKI 演算,其中(<*>)
是S组合子,pure
是K组合子。 S精确地用于将参数分配给两个函数:
S f g x = f x (g x)
它需要一个函数应用程序 ( fg ) 并使两者都依赖于值x ( (fx) (gx) )。
使用顺序:
> let foobar' = sequence [id, reverse]
> foobar' "abcde"
["abcde","edcba"]
有一些基本的惯用组合子反复出现,并用各种更高的概念和库重新实现,但它们本质上非常简单。名称可能会有所不同,有些可以根据其他名称来实现:
fork (f,g) x = (f x, g x) -- == (f &&& g)
prod (f,g) x = (f $ fst x, g $ snd x) -- == (f *** g)
pmap f (x,y) = (f x, f y) -- == (f *** f)
dup x = (x,x)
等等。当然uncurry f (x,y) == f x y
也经常使用这些。
&&&
和***
定义在Control.Arrow
, 以及first
和second
中。然后prod (f,id) == first f
,prod(id,g) == second g
等等等等。
所以你的foobar
变成
foobar = (\(a,b)->[a,b]) . fork (id,reverse)
= (\(a,b)->[a,b]) . (id &&& reverse)
= (\(a,b)->[a,b]) . (id *** reverse) . dup
= join $ curry ( (\(a,b)->[a,b]) . second reverse)
对于最后一个,您还需要导入Control.Monad
和Control.Monad.Instances
. 另请参阅此问题。
后期编辑:另外,使用ertesControl.Applicative
回答中的提示,
= (:) <*> ((:[]) . reverse)