我将如何使用函数组合(。)/应用程序($)运算符来重写此表达式? [英] How would I re-write this expression using the function composition (.) /application ($) operators?

查看:106
本文介绍了我将如何使用函数组合(。)/应用程序($)运算符来重写此表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力去掌握Haskell中的(。)($)运算符。我相信我理解了它们之间的差异,但我仍然很难成功用这些运算符替换表达式中的括号。



例如,如果我有表达式

  print(show (以5 [1..10]))

我知道如何使用(。)($)操作符

 打印。显示 。 5 $ [1..10] 

但是,如果我有像

  print(show(take(snd(1,5))[1..10]))

我可以将这个简化为最远的是

 <$ c $打印。显示 。 (snd(1,5))$ [1..10] 

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

解决方案

您的重写...

 打印。显示 。 (snd(1,5))$ [1..10] 

...是就可读性而言,它很好。以下建议实际上会使事情变得比必要更复杂;如你所说,知道这样的事情是可能的,可以更好地理解($)(。)

适用于 [1..10] 的第一个函数是...

  take(snd(1,5))

...让我们暂时关注一下。如果你试图在没有表达式的其余部分的情况下消除括号,那么你可能会得到......

  take。 snd $(1,5)

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

 (take。snd $(1,5))[1 .. 10] 

...但由于我们避免括号,所以我们需要额外的欺骗。有两种即时选项。第一个是通过($)部分将列表传递给函数:

 ($ [1..10])。采取。 snd $(1,5)

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

  GHCi> :t($ [1..10])
($ [1..10])::(Num t,Enum t)=> ([t]→b)→> b

...接受函数并将其应用于 [1。 0.10 。 (是的,我刚刚添加了一对括号;但是,它们并未真正用于分组 - ($ [1..10])只是替代方法 flip($)[1..10] 的语法 - 所以我说他们不计算:))



在这一点上,我们可以用与原始版本相同的方式完成表达式:

  print。显示 。 ($ [1..10])。采取。 snd $(1,5)

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

  take。 snd $(1,5)

......并且暂时保留一对:

  take。 snd 

取。 snd 是一个函数...

  GHCi> :拿。 snd 
取。 snd ::(a1,Int) - > [a] - > [a]

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

  take。 snd ::(a1,Int) - > ([a]  - > [a])

...也就是说,我们有一个函数需要一对并返回一个函数。如果我们使用 take编写一些函数 foo 。 snd 以便我们得到 foo。采取。 snd foo 将应用于由 take生成的函数。 SND 。事实上,当我们在上面使用($ [1..10])时,我们已经做到了。除了提供参数之外,我们可以用函数做的另一件事是组合它们。我们确实有一个操作员来编写函数......

 ((print。show)。)。采取。 snd 

...即,(。)。括号中的奇怪表达式是(。)的一部分,就像我们用于($)之前,除了这是一个左侧部分,这是一个正确的部分。由于运算符优先级诡计,需要一对额外的括号;同样令人不满意的选择包括... ...

 (print。)。 (显示 。) 。采取。 snd 

...和...

 (。)打印。 (。) 显示 。采取。 snd 

利用我们手中的无点函数,我们可以根据自己的喜好提供参数 - - 例如,像这样...
$ b $

 (((print。show)。)。take。snd)(1, 5)[1..10] 

...或者这个...

 ($ [1..10])。 ((print。show)。)。采取。 snd $(1,5)

...或者甚至是...

 ($ [1..10])。 ($(1,5))$((print。show)。)。采取。 snd 

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

  let f =((print。show)。)。采取。 snd in f(1,5)[1..10] 

确实有很多选择 - 他们都会密切合作,展示你最初的改写是多么的好!

I've been trying very hard to grasp the (.) and ($) operators in Haskell. I believe I understand the differences between them well enough, but I am still having a hard time successfully replacing the parentheses in my expressions with these operators.

For example, if I have the expression

print(show(take 5 [1..10]))

I understand how to rewrite it using the (.) and ($) operators

print . show . take 5 $ [1..10]

However, if I had something like

print (show (take (snd (1,5)) [1..10]))

The furthest I can simplify this down to is

print . show . take (snd (1,5)) $ [1..10]

No matter what I try I can not seem to replace those inner parenthesis with the ($) or (.) operator and have it successfully compile. I understand that most Haskellers typically use the ($) and (.) operators over parentheses so I am trying really hard to follow that style as well. If someone could point out how I could rewrite that expression above without using parenthesis I think it would help me use those operators with much more success. Thanks.

解决方案

Your rewrite...

print . show . take (snd (1,5)) $ [1..10]

... is as good as it gets in terms of readability. The following suggestions will in fact make it more convoluted than necessary; still, knowing such things are possible can, as you say, bring a better appreciation of how ($) and (.) work.

The first function which is applied to [1..10] is...

take (snd (1,5))

... so let's focus on it for a moment. If you were trying to eliminate parentheses there without the rest of the expression, you might end up with...

take . snd $ (1,5)

To reconstruct the original function, we need to apply that to [1..10]. A natural way of doing so would be...

(take . snd $ (1,5)) [1..10]

... but since we are avoiding parentheses, we need extra trickery. There are two immediate options. The first one is passing the list to the function with a ($) section:

($ [1..10]) . take . snd $ (1,5)

Just like (* 2) is a function that takes a number and multiplies it by two, ($ [1..10]) is a function...

GHCi> :t ($ [1..10])
($ [1..10]) :: (Num t, Enum t) => ([t] -> b) -> b

... that takes a function and applies it to [1..10]. (And yes, I have just added a pair of parentheses; however, they are not really being used for grouping -- ($ [1..10]) is just alternative syntax for flip ($) [1..10] -- so I say they don't count :))

At this point, we can complete the expression in the same manner of your original version:

print . show . ($ [1..10]) . take . snd $ (1,5)

The other option is writing the whole function pointfree (that is, without explictly writing the arguments). To do that, let's get back to...

take . snd $ (1,5)

... and leave out the pair for a moment:

take . snd

take . snd is a function...

GHCi> :t take . snd
take . snd :: (a1, Int) -> [a] -> [a]

... that takes a pair and a list and gives back a list. We can, however, also read this type as:

take . snd :: (a1, Int) -> ([a] -> [a])

... that is, we have a function that takes a pair and returns a function. If we compose some function foo with take . snd so that we get foo . take . snd, foo will be applied to the function produced by take . snd. In fact, we have done just that when we used ($ [1..10]) just above. Besides supplying argments, another thing we can do with functions is composing them. And we do have an operator for composing functions...

((print . show) .) . take . snd

... namely, (.). The weird expression within the parentheses is a section of (.), just like the one we used for ($) before, except that this is a left section, and that was a right section. Due to operator precedence shenanigans an extra pair of parentheses is necessary; equally unsatisfactory alternatives include...

(print .) . (show .) . take . snd

... and...

(.) print . (.) show . take . snd

With the pointfree function in our hands, we can supply the arguments in whichever way we feel like -- for instance, like this...

(((print . show) .) . take . snd) (1,5) [1..10]

... or this...

($ [1..10]) . ((print . show) .) . take . snd $ (1,5)

... or even this...

($ [1..10]) . ($ (1,5)) $ ((print . show) .) . take . snd

... or, for something less bizarre:

let f = ((print . show) .) . take . snd in f (1,5) [1..10]

There are indeed many alternatives -- and they all conspire to show how nice your original rewrite was in the first place!

这篇关于我将如何使用函数组合(。)/应用程序($)运算符来重写此表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆