imz*_*hev 1 parallel-processing haskell coding-style infix-operator
我在包中找到了一堆运算符$||parallel:
-- Strategic function application
{-
These are very handy when writing pipeline parallelism asa sequence of
@$@, @$|@ and @$||@'s. There is no need of naming intermediate values
in this case. The separation of algorithm from strategy is achieved by
allowing strategies only as second arguments to @$|@ and @$||@.
-}
-- | Sequential function application. The argument is evaluated using
-- the given strategy before it is given to the function.
($|) :: (a -> b) -> Strategy a -> a -> b
f $| s = \ x -> let z = x `using` s in z `pseq` f z
-- | Parallel function application. The argument is evaluated using
-- the given strategy, in parallel with the function application.
($||) :: (a -> b) -> Strategy a -> a -> b
f $|| s = \ x -> let z = x `using` s in z `par` f z
Run Code Online (Sandbox Code Playgroud)
但他们采取了3个论点.我没有看到使用这些运算符的例子,所以我需要想出一个很好的写作风格.
对他们来说通常的风格是:
f $|| strategy $ expr
Run Code Online (Sandbox Code Playgroud)
?
$所以Haskell有一个非常规则的语法.最高优先级是括号; 然后将一个函数应用于它的参数.函数应用程序是左关联的,或者,正如我更喜欢称之为greedy nom:意味着函数"吃掉"它在它作为参数之后看到的第一件事.所以,如果你写f g h这个变成(f g) h:f先吃g,然后返回值h.通常情况下,特别是当您定义一个函数时,您希望编写类似于f (Constructor parameter1 parameter2) = ...您真正需要明确括号的内容,这样您就不会意外地编写代码((f Constructor) parameter1) parameter2.
在括号和应用程序之后,我们有运算符:这些运算符具有由"中缀指令"给出的整体优先级和关联性层次结构.优先级最低的运算符定义为:
f $ g = f g
infixr 0 $
Run Code Online (Sandbox Code Playgroud)
这个运算符是一个完全正常的运算符,它似乎什么都不做:更确切地说,它将左边的函数应用到右边的参数.它是低优先级的右关联,因此它是"lazy nom"(之前的函数$应用于之后的所有内容$).关于是否f . g . h $ i更正或更不正确f $ g $ h $ i,有一个有趣的语法争议,它以不同的方式做同样的事情.
请记住,这$实际上只是一个普通的操作员/功能.例如,您可以执行以下操作:
Prelude> let factorial n = product [1..n]
Prelude> map ($ 3) [(5 +), (3 *), (3 +) . factorial . (2 *)]
[8,9,723]
Run Code Online (Sandbox Code Playgroud)
这里我们创建一个函数($ 3),它将函数作为参数,将其应用于3.我们将结果函数映射到其他几个函数.我们也可以像zipWith ($) functions (repeat 3)你真正想要的那样写这个,($)作为zipWith用来将两个列表压缩在一起的组合函数传递.他们是同一个东西,他们都是有趣的技巧.您甚至有一天可能想要map (flip ($))超过值列表,以函数的形式获取值列表.这是一个同构; 你可以重新获得价值id = map ($ id) . map (flip ($)),但也许有一天这种格式对你来说会更方便.
比这个低优先级的特殊形式一样if,let,case,do,where,和\.一般来说,Haskell要求它们不能在值之后立即)出现,(或者可能出现在一个或一个运算符之后.因此,如果你想写f \x -> 3 + 2 * xHaskell会抱怨,直到你把它变成以下之一:
f ((3 +) . (2 *)) -- no special forms
f (\x -> 3 + 2 * x) -- parenthesize the sub-expression
f $ \x -> 3 + 2 * x -- use $ to make the syntax "work" effortlessly.
Run Code Online (Sandbox Code Playgroud)
同样,您可能会看到以下内容:
main = complicatedProcessingStep . preprocessing $ do
input <- io_input
...
Run Code Online (Sandbox Code Playgroud)
在那里$被用来避免把周围的括号do,这样你就不必吊着一个)在空白的地方标记.
Haskell与其他语言的不同之处在于每个函数都只有一个参数.这可能会让你最初感到困惑:不是两个参数的运算符函数,那么\a b c -> ...,它有三个吗?
答案是否定的:\a b c -> ...是语法糖\a -> \b -> \c -> ...(这更不用说你可以在这些论点上进行模式匹配这一事实,所以秘密\a -> ...就是语法糖\random_token -> case random_token of a -> ...).每个函数都有一个参数,但有些函数返回一个函数.在Haskell中,我们可以做其他语言所做的事情,并接受一个元组; \(a, b) -> a + b工作正常,相当于uncurry (+).我们通常不这样做 - 我们通常会通过\a b -> a + b.
您可以从任何返回函数的函数中创建一个运算符.结果运算符将其左侧作为第一个函数的参数,将其右侧作为第二个参数的参数.这样做的规范方法是使用反引号:
13 `mod` 7 == mod 13 7
Run Code Online (Sandbox Code Playgroud)
但是如果类型不是多态的,或者你写了一个显式类型签名或禁用"单态限制",你也可以编写类似的东西(%%%) = mod.
所以有关于"三参数运算符"的答案:它返回一个函数,然后可以将其应用于其他值.当你写:
a x $|| b y $ c z
Run Code Online (Sandbox Code Playgroud)
由于上述规则,这解析为:
($) (($||) (a x) (b y)) (c z)
Run Code Online (Sandbox Code Playgroud)
根据定义($)变为:
($||) (a x) (b y) (c z)
Run Code Online (Sandbox Code Playgroud)
只需在子表达式上使用运算符ax $|| b y,就可以生成一个函数,该函数可以使用括号来应用,也可以使用将左侧应用于其右侧(a x $|| b y) (c z)的$运算符.