21

有时我会偶然发现我想表达“请使用最后一个参数两次”的问题,例如为了编写无点样式或避免使用 lambda。例如

sqr x = x * x

可以写成

sqr = doubleArgs (*) where
   doubleArgs f x = f x x

或者考虑这个稍微复杂一点的函数(取自这个问题):

ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)

如果有这样的函数,我可以无点编写这段代码:

ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
     dup f f1 f2 x = f (f1 x) (f2 x)

但是因为我在 Hoogle 中找不到类似 doubleArgs 或 dup 的东西,所以我想我可能会在这里错过一个技巧或成语。

4

3 回答 3

30

来自Control.Monad

join :: (Monad m) -> m (m a) -> m a
join m = m >>= id

instance Monad ((->) r) where
    return = const
    m >>= f = \x -> f (m x) x

扩展:

join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
       = \x -> id (f x) x
       = \x -> f x x

所以,是的,Control.Monad.join

哦,对于您的无点示例,您是否尝试过使用应用符号(来自Control.Applicative):

ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails

(我也不知道为什么人们如此喜欢a ++ (x:b)而不是a ++ [x] ++ b......它不是更快 - 内联会处理它 - 后者更加对称!哦好吧)

于 2010-12-02T10:29:18.053 回答
12

你所说的“doubleArgs”通常被称为 dup - 它是 W 组合器(在 To Mock a Mockingbird 中称为莺) - “基本复制器”。

你所说的“dup”实际上是“starling-prime”组合子。

Haskell 有一个相当小的“组合器基础”,参见 Data.Function,加上一些 Applicative 和 Monadic 操作通过 Applicative 和 Monad 的函数实例添加了更多“标准”组合器(Applicative 中的 <*> 是功能实例,liftA2 和 liftM2 是八哥素数)。社区中似乎对扩展 Data.Function 没有太大的热情,所以虽然组合器很有趣,但实际上我更喜欢在组合器不直接可用的情况下进行长手操作。

于 2010-12-02T12:01:03.840 回答
9

这是我问题第二部分的另一个解决方案:箭头!

import Control.Arrow

ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))

( &&&"fanout") 将参数分配给两个函数并返回结果对。>>>("and then") 反转函数应用顺序,允许从左到右进行一系列操作。second仅适用于一对的第二部分。当然,您uncurry最后需要一个在需要两个参数的函数中提供该对。

于 2011-05-25T06:55:35.053 回答