相关链接:
https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-3-1b0fd14eb1a7
Taking that first step to understanding Functional Programming concepts is the most important and sometimes the most difficult step. But it doesn’t have to be. Not with the right perspective.
- 采用第一步来理解功能编程概念是最重要的,有时也是最困难的步骤。 但它不一定是。 没有正确的观点。
Function Composition 函数组合
image.png
As programmers, we are lazy. We don’t want to build, test and deploy code that we’ve written over and over and over again.
- 作为程序员,我们很懒。 我们不想构建,测试和部署我们一遍又一遍地编写的代码。
We’re always trying to figure out ways of doing the work once and how we can reuse it to do something else.
- 我们总是试图找出一次完成工作的方法以及如何重复使用它来做其他事情。
Code reuse sounds great but is difficult to achieve. Make the code too specific and you can’t reuse it. Make it too general and it can be too difficult to use in the first place.
- 代码重用听起来很棒但很难实现。 使代码过于具体,您无法重复使用它。 使它太笼统,首先使用起来可能太难了。
So what we need is a balance between the two, a way to make smaller, reusable pieces that we can use as building blocks to construct more complex functionality.
- 所以我们需要的是两者之间的平衡,一种制作更小,可重复使用的部分的方法,我们可以将它们用作构建块来构建更复杂的功能。
In Functional Programming, functions are our building blocks. We write them to do very specific tasks and then we put them together like Lego™ blocks.
- 在功能编程中,函数是我们的构建块。 我们编写它们来执行非常具体的任务,然后我们将它们放在一起,就像Lego™块一样。
This is called Function Composition.
- 这称为功能组合。
So how does it work? Let’s start with two Javascript functions:
- 那么它是怎样工作的? 让我们从两个Javascript函数开始:
This is too verbose so let’s rewrite it using fat arrow notation:
- 这太冗长所以让我们用胖箭头表示法重写它:
That’s better. Now let’s imagine that we also want to have a function that takes a value and adds 10 to it and then multiplies the result by 5. We could write:
- 那更好。 现在让我们假设我们还想要一个函数,它接受一个值并将其加10,然后将结果乘以5.我们可以写:
Even though this is a very simple example, we still don’t want to have to write this function from scratch. First, we could make a mistake like forget the parentheses.
- 虽然这是一个非常简单的例子,但我们仍然不想从头开始编写这个函数。 首先,我们可能会犯错,比如忘记括号。
Second, we already have a function that adds 10 and another that multiplies by 5. We’re writing code that we’ve already written.
- 其次,我们已经有一个函数添加10和另一个乘以5的函数。我们正在编写我们已编写的代码。
So instead, let’s use add10 and mult5 to build our new function:
- 所以,让我们使用add10和mult5来构建我们的新函数:
We just used existing functions to create mult5AfterAdd10, but there’s a better way.
- 我们只是使用现有的函数来创建mult5AfterAdd10,但还有更好的方法。
In math, f ∘ g is functional composition and is read “f composed with g” or, more commonly, “f after g”. So (f ∘ g)(x) is equivalent to calling f after calling g with x or simply, f(g(x)).
- 在数学中,f g是功能组合,读作“f由g组成”,或者更常见的是“f after g”。 因此(f∘g)(x)等效于在用x或简单地用f(g(x))调用g之后调用f。
In our example, we have mult5 ∘ add10 or “mult5 after add10”, hence the name of our function, mult5AfterAdd10.
- 在我们的例子中,我们有mult5∘add10或“add10后的mult5”,因此我们的函数名称为mult5AfterAdd10。
And that’s exactly what we did. We called mult5 after we called add10 with value or simply, mult5(add10(value)).
- 而这正是我们所做的。 在我们使用值调用add10或简单地调用mult5(add10(value))之后,我们调用了mult5。
Since Javascript doesn’t do Function Composition natively, let’s look at Elm:
- 由于Javascript本身不进行函数组合,让我们看看Elm:
The << infixed operator is how you compose functions in Elm. It gives us a visual sense of how the data is flowing. First, value is passed to add10 then its results are passed to mult5.
- << infixed运算符是你如何在Elm中编写函数。 它让我们直观地了解数据的流动方式。 首先,将值传递给add10,然后将其结果传递给mult5。
Note the parentheses in mult5AfterAdd10, i.e. (mult5 << add10). They are there to make sure that the functions are composed first before applying value.
- 请注意mult5AfterAdd10中的括号,即(mult5 << add10)。 它们用于确保在应用值之前首先组合函数。
You can compose as many functions as you like this way:
- 你可以用这种方式编写任意数量的函数:
f x =
(g << h << s << r << t) x
Here x is passed to function t whose result is passed to r whose result is passed to s and so on. If you did something similar in Javascript it would look like g(h(s(r(t(x))))), a parenthetical nightmare
- 这里x传递给函数t,函数t的结果传递给r,其结果传递给s,依此类推。 如果你在Javascript中做了类似的东西,它看起来像g(h(s(r(t(x))))),一个括号的噩梦
Point-Free Notation
- 无点表示法
There is a style of writing functions without having to specify the parameters called Point-Free Notation. At first, this style will seem odd but as you continue, you’ll grow to appreciate the brevity.
- 有一种编写函数的样式,无需指定称为无点表示法的参数。 起初,这种风格看起来很奇怪,但随着你的继续,你会逐渐欣赏它的简洁。
But this parameter is unnecessary since add10, the rightmost function in the composition, expects the same parameter. The following point-free version is equivalent:
- 但是这个参数是不必要的,因为组合中最右边的函数add10需要相同的参数。 以下无点版本是等效的:
There are many benefits from using the point-free version.
- 使用无点版本有很多好处。
First, we don’t have to specify redundant parameters. And since we don’t have to specify them, we don’t have to think up names for all of them.
- 首先,我们不必指定冗余参数。 由于我们不必指定它们,因此我们不必考虑所有这些的名称。
Second, it’s easier to read and reason about since it’s less verbose. This example is simple, but imagine a function that took more parameters.
- 其次,它更容易阅读和推理,因为它不那么冗长。 这个例子很简单,但想象一个带有更多参数的函数。
Trouble in Paradise
- 天堂的麻烦
So far we’ve seen how Function Composition works and how we should specify our functions in Point-Free Notation for brevity, clarity and flexibility.
- 到目前为止,我们已经看到了函数组合如何工作以及我们应该如何在无点表示法中指定我们的函数,以简洁,清晰和灵活。
Now, let’s try to use these ideas in a slightly different scenario and see how they fare. Imagine we replace add10 with add:
- 现在,让我们尝试在稍微不同的场景中使用这些想法,看看它们的表现如何。 想象一下,我们用add替换add10:
How do we write mult5After10 with just these 2 functions?
- 我们如何用这两个函数编写mult5After10?
Think about it for a bit before reading on. No seriously. Think about. Try and do it.
-在阅读之前考虑一下。 不认真。 想一想。 尝试并做到这一点。
Okay, so if you actually spent time thinking about it, you may have come up with a solution like:
- 好的,所以如果你真的花时间考虑它,你可能会想出一个解决方案,如:
But this wouldn’t work. Why? Because add takes 2 parameters.
- 但这不起作用。 为什么? 因为add需要2个参数。
If this isn’t obvious in Elm, try to write this in Javascript:
- 如果这在Elm中不明显,请尝试在Javascript中编写:
This code is wrong but why?
- 这段代码错了,但为什么呢?
Because the add function is only getting 1 of its 2 parameters here then its incorrect results are passed to mult5. This will produce the wrong results.
- 因为add函数只获取其2个参数中的1个,所以将不正确的结果传递给mult5。 这会产生错误的结果。
In fact, in Elm, the compiler won’t even let you write such mis-formed code (which is one of the great things about Elm).
- 事实上,在Elm中,编译器甚至不会让你编写这样的错误代码(这是关于Elm的伟大事情之一)。
Let’s try again:
image.pngThis isn’t point-free but I could probably live with this. But now I’m no longer just combining functions. I’m writing a new function. Also, if this gets more complicated, e.g. if I want to compose mult5AfterAdd10 with something else, I’m going to get into real trouble.
- 这不是免费的,但我可能会忍受这一点。 但现在我不再仅仅是组合功能了。 我正在写一个新功能。 此外,如果这变得更复杂,例如 如果我想用其他东西组成mult5AfterAdd10,我将陷入真正的麻烦。
So it would appear that Function Composition has limited usefulness since we cannot marry these two functions. That’s too bad since it’s so powerful.
- 因此,函数组合似乎具有有限的用途,因为我们不能将这两个函数结合起来。 这太糟糕了,因为它太强大了。
How could we solve this? What would we need to make this problem go away?
- 我们怎么解决这个问题? 我们需要做什么才能解决这个问题?
Well, what would be really great is if we had some way of giving our add function only one of its parameters ahead of time and then it would get its second parameter later when mult5AfterAdd10 is called.
- 那么,真正伟大的是,如果我们有一些方法可以提前给我们的add函数提供一个参数,然后在调用mult5AfterAdd10时它将获得第二个参数。
Turns out there is way and it’s called Currying.
- 原来有方法,它叫做Currying。
Enough for now.
- 够了。
In subsequent parts of this article, I’ll talk about Currying, common functional functions (e.g map, filter, fold etc.), Referential Transparency, and more.
- 在本文的后续部分中,我将讨论Currying,常用功能函数(例如map,filter,fold等),Referential Transparency等。
网友评论