使用代码在python中运行

Ever heard about functional programming, but didn’t understand it and/or didn’t know where to start? Or maybe you would like to have a quick refresher into how this works in Python? Then you have come to the right place!

曾经听说过函数式编程,但听不懂和/或不知道从哪里开始? 或者,也许您想快速回顾一下它在Python中的工作方式? 那么你来对地方了!

编程范例 (Programming Paradigms)

Python is an interpreted all-purpose, high-level, dynamically typed, and garbage-collected programming language. Now, there are several programming paradigms, among which perhaps the most common is the one we probably use most of the time, i.e. imperative programming. This means that we give precise instructions in somehow sequential order, and alter values based on these instructions. Another paradigm is object-oriented programming (OOP), in which everything is an object. The beautiful thing is, this is not all there is to it. In functional programming, everything is… guess what: a function! Although Python is not inherently functional, it does have a number of functional capabilities, some of which you may find yourself using on a daily basis. In this article, I will explain some of the basics, along with other examples. Now, instead of giving more theory, let’s learn through examples!

Python是一种解释型通用,高级,动态类型和垃圾回收的编程语言。 现在,有几种编程范例 ,其中最常见的是我们大多数时候可能使用的一种,即命令式编程 。 这意味着我们以某种顺序给出精确的指令,并根据这些指令更改值。 另一个范例是面向对象的编程(OOP) ,其中的所有内容都是对象。 美丽的是,这还不是全部。 在函数式编程中 ,一切都是…猜测:函数! 尽管Python并不是天生就具有功能,但是它确实具有许多功能,您可能会发现每天都会使用其中的一些功能。 在本文中,我将解释一些基础知识以及其他示例。 现在,让我们通过示例来学习,而不是给出更多的理论!

Image for post

进口货 (Imports)

阶乘 (Factorial)

Let’s start with a simple example: factorial. The factorial of a natural number is defined as:

让我们从一个简单的例子开始:阶乘。 自然数的阶乘定义为:

Image for post

By definition

根据定义

Which is what you would expect.

这是您所期望的。

Functional way

功能方式

This implementation illustrates one main idea of tail-recursion; that is, the recursion only takes place tail-most, with only one recursive call:

此实现说明了尾递归的一个主要思想; 也就是说,递归只发生在最尾端, 只有一个递归调用:

So what changed? First, notice we are now using a helper function with an additional parameter: acc, an accumulator variable. As its name implies, now instead of putting the cumulative result outside, we pass the result as an argument to the next calls! Of course, always make sure there is a well-defined base case, otherwise, you will end up with infinite loops.

那么,什么改变了? 首先,请注意,我们现在正在使用带有附加参数的helper函数: acc ,一个累加器变量。 顾名思义,现在我们将结果作为参数传递给下一个调用,而不是将累积结果放在外面! 当然,请始终确保存在定义明确的基本案例 ,否则,您将陷入无限循环。

Speed tests

速度测试

Despite their names, is one faster than the other? Let’s see!

尽管有他们的名字,但一个比另一个快吗? 让我们来看看!

We see that the functional version is actually slightly slower. This is because we are storing an extra variable along the way. You will see with the next example, however, what a difference can functional programming do!

我们看到功能版本实际上稍微慢一些。 这是因为我们在此过程中存储了一个额外的变量。 您将在下一个示例中看到,但是,函数式编程有什么不同!

斐波那契 (Fibonacci)

The Fibonacci sequence is defined by

斐波那契数列定义为

Image for post

How fast can we calculate the 𝑛-th Fibonacci number?

我们能以多快的速度计算第Fi个斐波那契数?

Naive solution

天真的解决方案

Of course, we can just use the definition. However, you can show that the following runs in 𝑂(2^n) time. Why? at every recursive call, we have twice the calls!! Not nice!!

当然,我们可以只使用定义。 但是,您可以证明以下内容在𝑂(2 ^ n)时间中运行。 为什么? 在每个递归调用中,我们都有两次调用!! 不太好!!

Tail-recursive

尾递归

Let’s see what changed: once again we are using a helper function: we essentially make recursive calls by passing the second parameter as the new a, while on the right accumulate the result so far. Surprisingly enough, this will produce the 𝑛-th Fibonacci number in linear time!

让我们看看发生了什么变化:再次使用辅助函数:本质上,我们通过将第二个参数作为新的a传递来进行递归调用,而右侧则累加了到目前为止的结果。 令人惊讶的是,这将在线性时间内产生第Fi个斐波那契数!

Bonus: dynamic programming

奖励:动态编程

Dynamic programming is another programming technique, in which the idea is to store results that will be using again in a table, instead of re-computing it. The disadvantage is that this uses extra space. In this case, we use a loop and a dictionary to store the results. Also runs in 𝑂(1) time (constant time), however!

动态编程是另一种编程技术,其思想是将将要再次使用的结果存储在表中,而不是重新计算它。 缺点是这会占用额外的空间。 在这种情况下,我们使用循环和字典来存储结果。 但是也以𝑂(1)时间(恒定时间)运行!

Which one is faster?

哪一个更快?

That’s a massive time difference between the naive implementation versus the functional and dynamic versions!

天真的实现与功能和动态版本之间存在巨大的时间差异!

Lambda函数 (Lambda functions)

The lambda functions in python can be seen as shorthands for “relatively simple” functions. The syntax is as follows:

python中的lambda函数可以看作是“相对简单”函数的简写。 语法如下:

where f()is the actual implementation of what you want your function to do. For example, we can define a function to determine whether a number is odd, or create some other funky function as shows the example below:

其中f()是函数要执行的操作的实际实现。 例如,我们可以定义一个函数来确定数字是否为奇数,或者创建其他一些时髦的函数,如下例所示:

求幂 (Exponentiation)

Here’s another interesting problem: given an integer 𝑥, how fast can we compute x^m, where 𝑚≥0 (for simplicity)? That is,

这是另一个有趣的问题:给定整数𝑥,我们能以多快的速度计算x ^ m,其中≥0(为简单起见)? 那是,

Image for post

Naive implementation

天真的实现

Russian Peasant Exponentiation

俄罗斯农民的指数

If anybody knows why this is called this way, let me know. The idea is as follows:

如果有人知道为什么这样称呼我,请告诉我。 这个想法如下:

Image for post

Therefore we can pretty much reduce the computations by half at each time! That is, approximately 𝑂(𝑙𝑜𝑔(𝑛)) time!

因此,我们几乎可以每次将计算量减少一半! 也就是说,大约𝑂(𝑙𝑜𝑔(𝑛))时间!

and of course, the results are the same, except that this is much, much faster! But wait! Can we do even better? Yes, we can!

当然,结果是一样的,除了速度要快得多! 可是等等! 我们可以做得更好吗? 我们可以!

Tail-recursive Russian Peasant Exponentiation

尾递归的俄罗斯农民指数

Comparing times

比较时间

Now that’s a huge improvement!!! The tail-recursive RPE algorithm is so fast is almost instantaneous! Compare this to the naive implementation, and the plain RPE which already does much better, but not the best it can.

现在,这是一个巨大的进步!!! 尾递归RPE算法是如此之快,几乎是瞬时的! 将此与单纯的实现以及已经做得更好但不是最好的普通RPE进行比较。

函数式编程中的一些Python函数 (Some Python functions from functional programing)

Although Python is not inherently functional, it does provide a number of extremely useful functions that in fact have their origins in functional programming. If you program actively in Python, you will find yourself using these all the time. Let’s start!

尽管Python并非天生具有功能性,但它确实提供了许多极其有用的功能,这些功能实际上起源于函数式编程。 如果您积极地使用Python进行编程,那么您会发现自己一直在使用它们。 开始吧!

zip()

压缩()

One thing we want to do is the following: given two lists, `l1` and `l2`, we would like to pair their elements in a new list. For instance,

我们要做的一件事是:给定两个列表l1和l2,我们希望将它们的元素配对到一个新列表中。 例如,

This is useful because we can iterate through two lists of the same size at the same time!

这很有用,因为我们可以同时遍历两个相同大小的列表!

Note: In Python3, zip() returns a generator object instead of a list, so if you want to get the elements out, you have to iterate through it!

注意:在Python3中, zip()返回一个生成器对象而不是一个列表,因此,如果要删除元素,则必须对其进行迭代!

map()

地图()

Image for post

What if we want to apply a function to every element in a list? The map function is precisely what you need. How can we do this? Of course, the first thing that comes into mind would be:

如果我们想将函数应用于列表中的每个元素怎么办? 地图功能正是您所需要的。 我们应该怎么做? 当然,首先想到的是:

And well… this does the job. Can we do it without a loop? Yes sir! That’s functional programming for you:

好吧……就可以了。 我们能做到无循环吗? 是的先生! 那就是您的函数式编程:

The idea is as follows: if we have an empty list, nothing to recurse on. Else, we pop the last element of the list in O(1) time, then append it to whatever the recursion on the end of the list gives.

想法如下:如果我们有一个空列表,则没有任何递归依据。 否则,我们在O(1)中弹出列表的最后一个元素 时间,然后将其附加到列表末尾给出的任何递归。

Image for post

Once again, the actual Pythonmap() built-in function returns a generator:

再次,实际的Python map()内置函数返回一个生成器:

any() , all() and filter()

any(),all()和filter()

  • any() takes an iterable and returns True if any element is True.

    any()进行迭代,如果任何元素为True,则返回True。

  • all() takes an iterable and returns True if all elements are True.

    all()进行迭代,如果所有元素均为True,则返回True。

  • filter_fun() as argument a boolean function and a list, and returns a list containing all elements from the input list that passed the input test.

    filter_fun()作为布尔函数和列表的参数,并返回一个列表,其中包含通过输入测试的输入列表中的所有元素。

左折 (Fold-left)

Image for post

Here’s an interesting example showing what we can do with functional programming the core idea of efficient recursion and even feeding functions to functions: fold-left. It is defined as follows:

这是一个有趣的示例,展示了我们可以使用函数式编程实现高效递归甚至向函数馈入函数的核心思想: 向左折叠。 定义如下:

  • let 𝑓() be some function that takes one parameter

    令𝑓()是一个带有一个参数的函数
  • let [𝑥1,𝑥2,…,𝑥𝑛] be a list

    让[𝑥1,𝑥2,…,𝑥𝑛]成为列表
  • let 𝑎 be an accumulator parameter.

    令𝑎为累加器参数。
Image for post

That is, we first apply the function 𝑓 to the first element, then take that and pass it as an input to the next one, and so on until we have gone through every item in the list. In a purely functional programming language, this would be a good implementation:

也就是说,我们首先将函数𝑓应用于第一个元素,然后将其作为输入传递给下一个元素,依此类推,直到遍历列表中的每个项目。 在纯函数式编程语言中,这将是一个很好的实现:

You can see that this simply mimics the definition. The problem here lies in that Python lists are implemented as arrays, so the slicing operation is actually costly. If these were linked lists, however, the running time would be 𝑂(𝑛)! A more pythonic way to do it is the following:

您可以看到,这只是模仿了定义。 这里的问题在于Python列表是作为数组实现的,因此切片操作实际上是昂贵的。 但是,如果这些是链接列表,则运行时间将为𝑂(𝑛)! 一种更pythonic的方法如下:

Here, the := syntax is the assignment operator, which dynamically changes the value of acc. We can then, for instance, obtain the sum of all entries in a list as using fold_left as follows:

在这里, :=语法是赋值运算符,它可以动态更改acc的值。 然后,例如,我们可以使用fold_left获得列表中所有条目的总和,如下所示:

(Streams)

Yaaaay!!! Congratulations on having made it all the way to one of the most mindblowing topics in functional programming. The idea that your code does not necessarily need to run all at once, and a function containing such code can produce a different output every time is called, is just fascinating!

呀! 祝贺您将其一路推向了函数式编程中最令人振奋的主题之一。 令人着迷的想法是,您的代码不一定需要一次全部运行,而包含此类代码的函数每次都可以产生不同的输出。

A stream, also more commonly known as a generator, is essentially a finite or possibly infinite list, from which we can only get one element at the time.

,通常也称为生成器 ,本质上是一个有限或可能无限的列表,从中我们一次只能得到一个元素

  • Streams are definitions, and therefore they are not completely saved in memory.

    流是定义,因此它们没有完全保存在内存中。
  • In Python, we use the keyword yield, instead of return to return some results before continuing code execution.

    在Python中,我们使用关键字yield ,而不是return来继续执行代码之前返回一些结果。

  • In a sense, the code “is stopped in time” until the next call.

    从某种意义上说,代码“被及时停止”直到下一次调用。
  • To obtain the next element in a stream once it has been created, use the keyword next.

    要在创建流后获取流中的下一个元素,请使用关键字next

  • If we reach the end of the stream, the Stop iteration error is raised.

    如果我们到达流的末尾,则会出现Stop iteration error

List to stream

列表流

Of course, whenever a list is finite, there is a one-to-one correspondence with some stream. Let’s create a function that converts a list into a stream (generator) object:

当然,只要列表是有限的,就与某个流一一对应。 让我们创建一个将列表转换为流( generator )对象的函数:

This works as follows: we simply loop through every element yield ing each element every time.

它的工作方式如下:我们只需遍历每个元素,每次都yield每个元素。

Note that what we get out is a Pythongenerator object! So how do we get the elements out? Thenext keyword comes to save the day!

请注意,我们得到的是一个Python generator对象! 那么,我们如何找出要素呢? next关键字来节省一天!

So every time we call the resulting stream, we get the next element in the original list. Note that if you reach the end of the stream, a StopIteration error is raised.

因此,每次调用结果流时,我们都会获得原始列表中的下一个元素。 请注意,如果到达流的末尾,则会引发StopIteration错误。

Stream of ones

的流

Can we produce an infinite list of ones? Yes, we can! With streams, this as simple as:

我们可以产生一个无限的清单吗? 我们可以! 使用流,这很简单:

Numbers from n

从n开始的数字

Ok, just a bunch of ones doesn’t seem very interesting. How about all integers from n onwards? You may guess this can be done as

好的,只是一堆似乎不太有趣。 从n开始的所有整数怎么样? 您可能会猜到这可以做到

Say, for example, all numbers from 12 would be

举例来说,来自12的所有数字都是

Note that you can put any arbitrary integer in the range argument since the stream is infinite! Now, how about producing all natural numbers? Easy!!

请注意,由于流是无限的,因此可以在range参数中放入任意整数! 现在,产生所有自然数怎么样? 简单!!

All Fibonacci numbers

所有斐波那契数字

Producing every single possible Fibonacci number as a stream? Piece of cake with streams and functional programming:

将每个可能的斐波那契数作为流产生吗? 带有流和功能编程的小菜一碟:

Note for any n elements we want to fetch, it always runs in O(n) time!

请注意,对于我们要获取的任何n个元素,它总是在O (n)时间内运行!

Map for streams

映射流

Ok, now suppose that we want to apply a function to every single element of a stream, even when it has infinitely many elements. How can we do this? The answer is: we can go funkier and even come up with a map() function for streams; that is, we can apply a function to every element of an infinite list. Isn't that cool??? The idea is simple: the map takes as inputs the function to apply and the stream. At each call, we apply the function by obtaining the next element from the stream and yielding the result. Of course, the result is also a stream.

好的,现在假设我们想将函数应用于流的每个元素,即使它具有无限多个元素。 我们应该怎么做? 答案是:我们可以变得更时髦,甚至为流提供map()函数; 也就是说,我们可以将函数应用于无限列表的每个元素 。 那不是很酷吗??? 这个想法很简单:地图将要应用的功能和流作为输入。 在每次调用时,我们通过从流中获取下一个元素并产生结果来应用该函数。 当然,结果也是一个流。

Here, we make sure that the input is indeed a stream, and if so, at each call, we obtain the next element from the input stream, then apply the input function on it, and finally yield the result. As you may have guessed, the resulting object is… also a stream!

在这里,我们确保输入确实是一个流,如果是的话,则在每次调用时,我们将从输入流中获取下一个元素,然后将输入函数应用于其上,最后产生结果。 您可能已经猜到了,生成的对象是……也是一个流!

As a concrete example, suppose we want to square every Fibonacci number; we can use the function above as follows:

举一个具体的例子,假设我们要对每个斐波那契数平方。 我们可以如下使用上面的函数:

And that’s it!

就是这样!

最后的话 (Last words)

Although functional programming is simply too vast to cover in one article, I hope you have by now got a taste and a basic understanding of a different way to think about how you code, the basics of functional programming in Python, and will be able to use some of these new tricks next time you have the chance :). Feel free to leave comments and corrections, any feedback is appreciated!

尽管函数式编程太过庞大而无法在一篇文章中介绍,但我希望您现在已经对如何思考编码的另一种方式有所了解,并对Python的函数式编程的基础有了基本的了解,并且能够下次有机会时使用这些新技巧:)。 随时留下评论和更正,任何反馈表示赞赏!

资源资源 (Resources)

Much of this content was inspired by what I learned at a Programming Languages and Paradigms class I took at McGill University, taught in a purely functional language called OCaml (https://ocaml.org/). All the code used in this article can also be found at my GitHub repo. Also, if you are nuts about learning functional programming in Python, you can check out this page.

这些内容的大部分是受我在麦吉尔大学上的编程语言和范例课程上学到的启发而来的,该课程以纯功能语言OCaml ( https://ocaml.org/ )进行教学。 本文中使用的所有代码也可以在我的GitHub存储库中找到。 另外,如果您对使用Python学习函数式编程不屑一顾,则可以查看此页面。

Until next time, folks!

直到下一次,伙计们!

跟我来 (Follow me at)

  1. https://blog.jairparraml.com/

    https://blog.jairparraml.com/

  2. https://www.linkedin.com/in/hair-parra-526ba19b/

    https://www.linkedin.com/in/hair-parra-526ba19b/

  3. https://github.com/JairParra

    https://github.com/JairParra

  4. https://medium.com/@hair.parra

    https://medium.com/@hair.parra

在LinkedIn上与我联系 (Connect with me on LinkedIn)

版权 (Copyright)

Image for post

http://creativecommons.org/licenses/by-nc-nd/4.0

http://creativecommons.org/licenses/by-nc-nd/4.0

All original content, including presentation of mathematical formulas and examples provided, are the exclusive property of Hair Albeiro Parra Barrera, copyright protected. Please contact me via LinkedIn for business purposes.

所有原始内容(包括所提供的数学公式和示例的内容)均为Hair Albeiro Parra Barrera的专有财产,受版权保护。 请出于商业目的通过LinkedIn与我联系。

翻译自: https://medium.com/analytics-vidhya/going-functional-in-python-with-code-b50ba7742469

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值