如何使用$
和无点样式组合?
一个明显的例子是以下效用函数:
times :: Int -> [a] -> [a]
times n xs = concat $ replicate n xs
只是写concat $ replicate
会产生错误,同样你也不能写concat . replicate
因为concat
期望值而不是函数。
那你怎么把上面的函数变成无点样式呢?
答案 0 :(得分:22)
你可以使用这个组合子:(冒号提示两个参数紧随其后)
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)
它允许你摆脱n
:
time = concat .: replicate
答案 1 :(得分:13)
您可以使用
轻松编写几乎无点的版本times n = concat . replicate n
完全无点的版本可以通过明确的curry和uncurry来实现:
times = curry $ concat . uncurry replicate
答案 2 :(得分:12)
上freenode并问lambdabot;)
<jleedev> @pl \n xs -> concat $ replicate n xs
<lambdabot> (join .) . replicate
答案 3 :(得分:1)
在Haskell中,函数组合是关联的:
f . g . h == (f . g) . h == f . (g . h)
任何infix operator只是一个很好的功能:
2 + 3 == (+) 2 3
f 2 3 = 2 `f` 3
composition operator也只是一个二元函数,一个higher-order,它接受2个函数并返回一个函数:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
因此,任何合成操作符都可以这样重写:
f . g == (.) f g
f . g . h == (f . g) . h == ((.) f g) . h == (.) ((.) f g) h
f . g . h == f . (g . h) == f . ((.) g h) == (.) f ((.) g h)
默认情况下,由于partially applied,Haskell中的每个函数都可以currying。使用sections:
可以以非常简洁的方式部分应用中缀运算符(-) == (\x y -> x - y)
(2-) == (-) 2 == (\y -> 2 - y)
(-2) == flip (-) 2 == (\x -> (-) x 2) == (\x -> x - 2)
(2-) 3 == -1
(-2) 3 == 1
由于合成运算符只是一个普通的二元函数,你也可以在部分中使用它:
f . g == (.) f g == (f.) g == (.g) f
另一个有趣的二元运算符是$,它只是函数应用程序:
f x == f $ x
f x y z == (((f x) y) z) == f x y z
f(g(h x)) == f $ g $ h $ x == f . g . h $ x == (f . g . h) x
有了这些知识,我如何将concat $ replicate n xs
转换为无点样式?
times n xs = concat $ replicate n xs
times n xs = concat $ (replicate n) xs
times n xs = concat $ replicate n $ xs
times n xs = concat . replicate n $ xs
times n = concat . replicate n
times n = (.) concat (replicate n)
times n = (concat.) (replicate n) -- concat is 1st arg to (.)
times n = (concat.) $ replicate n
times n = (concat.) . replicate $ n
times = (concat.) . replicate
¹ Haskell基于category theory。类别理论中的一个类别包括三个方面:一些对象,一些态射,以及一个组合的态射概念。每个态射都将源对象与目标对象连接起来。范畴理论要求态射的组合是联想的。 Haskell中使用的类别称为 Hask ,其对象是类型,其态射是函数。函数f :: Int -> String
是将对象Int
连接到对象String
的态射。因此,类别理论要求Haskell的函数组合是关联的。
答案 4 :(得分:1)
通过扩展FUZxxl的答案,我们得到了
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)
(.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(.::) = (.).(.:)
(.:::) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
(.:::) = (.).(.::)
...
非常好。
<强>加成强>
(.:::) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
(.:::) = (.:).(.:)
嗯...所以也许我们应该说
(.1) = .
(.2) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.2) = (.1).(.1)
(.3) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(.3) = (.1).(.2)
-- alternatively, (.3) = (.2).(.1)
(.4) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
(.4) = (.1).(.3)
-- alternative 1 -- (.4) = (.2).(.2)
-- alternative 2 -- (.4) = (.3).(.1)
更好。
我们也可以将其扩展到
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f = fmap (fmap f)
fmap4 :: (Functor f, Functor g, Functor h, functro i)
=> (a -> b) -> f (g (h (i a))) -> f (g (h (i b)))
fmap4 f = fmap2 (fmap2 f)
遵循相同的模式。
将fmap
或(.)
参数化的时间更好。但是,那些fmap
或(.)
在类型上实际上是不同的。因此,执行此操作的唯一方法是使用编译时计算,例如TemplateHaskell
。
对于日常用途,我只是建议
Prelude> ((.).(.)) concat replicate 5 [1,2]
[1,2,1,2,1,2,1,2,1,2]
Prelude> ((.).(.).(.)) (*10) foldr (+) 3 [2,1]
60