如何使用函数组合(.)/ application($)运算符重写此表达式?

Tyl*_*ler 1 haskell

我一直很努力把握(.)($)运营商在哈斯克尔.我相信我很了解它们之间的差异,但我仍然很难用这些运算符成功替换表达式中的括号.

例如,如果我有表达式

print(show(take 5 [1..10]))
Run Code Online (Sandbox Code Playgroud)

我理解如何使用(.)($)运算符重写它

print . show . take 5 $ [1..10]
Run Code Online (Sandbox Code Playgroud)

但是,如果我有类似的东西

print (show (take (snd (1,5)) [1..10]))
Run Code Online (Sandbox Code Playgroud)

最远的我可以将其简化为

print . show . take (snd (1,5)) $ [1..10]
Run Code Online (Sandbox Code Playgroud)

无论我尝试什么,我似乎无法用($)or (.)运算符替换那些内部括号,并让它成功编译.据我所知,大多数Haskeller通常在括号中使用($)(.)运算符,所以我也很努力地遵循这种风格.如果有人可以指出我如何在不使用括号的情况下重写上面的表达式,我认为这将有助于我使用那些更成功的运算符.谢谢.

dup*_*ode 5

你的改写......

print . show . take (snd (1,5)) $ [1..10]
Run Code Online (Sandbox Code Playgroud)

......在可读性方面同样出色.事实上,以下建议会使其比必要的更复杂; 但是,正如你所说,知道这些事情是可能的,可以更好地理解($)(.)工作.

应用的第一个功能[1..10]是......

take (snd (1,5))
Run Code Online (Sandbox Code Playgroud)

...所以让我们暂时关注它.如果你试图在没有表达式的其余部分的情况下消除括号,你最终可能会......

take . snd $ (1,5)
Run Code Online (Sandbox Code Playgroud)

要重建原始函数,我们需要将其应用于[1..10].这样做的一种自然方式是......

(take . snd $ (1,5)) [1..10]
Run Code Online (Sandbox Code Playgroud)

...但由于我们避免使用括号,我们需要额外的诡计.有两个直接的选择.第一个是通过一个($)部分将列表传递给函数:

($ [1..10]) . take . snd $ (1,5)
Run Code Online (Sandbox Code Playgroud)

就像(* 2)一个函数,它取一个数字并乘以它,($ [1..10])是一个函数...

GHCi> :t ($ [1..10])
($ [1..10]) :: (Num t, Enum t) => ([t] -> b) -> b
Run Code Online (Sandbox Code Playgroud)

...需要一个函数并将其应用于[1..10].(是的,我刚刚添加了一对括号;但是,它们并没有真正用于分组 - ($ [1..10])只是替代语法flip ($) [1..10]- 所以我说它们不算数:))

此时,我们可以用与原始版本相同的方式完成表达式:

print . show . ($ [1..10]) . take . snd $ (1,5)
Run Code Online (Sandbox Code Playgroud)

另一个选择是编写整个函数pointfree(也就是说,没有明确地编写参数).要做到这一点,让我们回到......

take . snd $ (1,5)
Run Code Online (Sandbox Code Playgroud)

......暂时离开这对:

take . snd
Run Code Online (Sandbox Code Playgroud)

take . snd 是一个功能......

GHCi> :t take . snd
take . snd :: (a1, Int) -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

...需要一对和一个列表并返回一个列表.但是,我们也可以将此类型读作:

take . snd :: (a1, Int) -> ([a] -> [a])
Run Code Online (Sandbox Code Playgroud)

...也就是说,我们有一个函数,它接受一对并返回一个函数.如果我们撰写了一些功能,footake . snd使我们获得foo . take . snd,foo将被应用到所产生的作用take . snd.事实上,当我们($ [1..10])刚才使用时,我们已经做到了.除了提供论证之外,我们可以用函数做的另一件事是组合它们.我们确实有一个运算符来组合函数......

((print . show) .) . take . snd
Run Code Online (Sandbox Code Playgroud)

...即(.).括号内的奇怪表达式是一段(.),就像我们($)之前使用的那样,除了这是左侧部分,这是一个正确的部分.由于操作员优先权诡计需要额外的一对括号; 同样令人不满意的替代品包括......

(print .) . (show .) . take . snd
Run Code Online (Sandbox Code Playgroud)

......而且......

(.) print . (.) show . take . snd
Run Code Online (Sandbox Code Playgroud)

有了我们手中的无点功能,我们可以以我们想要的方式提供参数 - 例如,像这样......

(((print . show) .) . take . snd) (1,5) [1..10]
Run Code Online (Sandbox Code Playgroud)

... 或这个...

($ [1..10]) . ((print . show) .) . take . snd $ (1,5)
Run Code Online (Sandbox Code Playgroud)

......甚至这......

($ [1..10]) . ($ (1,5)) $ ((print . show) .) . take . snd
Run Code Online (Sandbox Code Playgroud)

...或者,对于不那么奇怪的事情:

let f = ((print . show) .) . take . snd in f (1,5) [1..10]
Run Code Online (Sandbox Code Playgroud)

确实有很多替代方案 - 而且它们都会合谋显示你原来的重写是多么美好!