为什么每种语言都需要下划线

2020-07-24 08:00:51

(这是我在PyCon和DevDay上的一次演讲的改编。幻灯片和视频有俄语版本)。

你知道下划线是什么吗?一般而言,让生活变得更美好的是JavaScript库。适用于任何用JavaScript编写的人。我主要编写Python,我也想让我的生活更好,所以我继续为Python编写了一个类似的库。但问题是,你需要理解某种东西背后的魔力,才能恰当地复制它。那么,这背后的魔力是什么呢?

要回答这个问题,我们应该看看这种图书馆解决的问题。为了了解一些情况,我将用代码对它们进行说明。这是用Python编写的,但是所有的想法都是通用的,所以不要害怕。

对于URL中的url,图像=[]:对于范围内的尝试(Download_Tries):try:images。APPEND(download_image(Url))除HttpError:IF尝试+1==Download_TRIES:RAISE之外的中断。

这里有几件事情纠缠在一起,但我的观点是,这段文字可以写得更短:

如果一开始看起来很难,那就没问题了。它涉及一些函数和控制流抽象,您可能对此很陌生。一旦您习惯了它,您会发现后一种变体不仅更短,而且更简单。

对于请求中的k,v,d={}。Items():尝试:d[k]=int(V)Exclude(TypeError,ValueError):d[k]=None。

在这里,我们检查字典,并通过将其值强制为int来清除它们。或者,如果这是不可能的,那就什么都不做。清除输入和忽略格式错误的数据是一项相当频繁的任务,但它需要付出如此多的努力。我想这样写:

使用Funcy完全可以做到这一点。不过,让我们转到下一个。

序列中x的prev=NONE:如果prev不是NONE并且x<;=prev:is_ascending=false Break prev=x否则:is_ascending=True。

啊,迭代一个序列,跟踪前一个元素。你这样做过几次了?应该有一个函数来抽象它:

而配对就是这样做的。它使我们能够按相邻对顺序迭代。所以我们只需要检查一下它们是否都是按顺序订购的。

所有这些示例都有一个共同的属性-红色变体有更多的代码。和更多代码:

显然,下划线、函数式和好友可以帮助我们编写更少的代码(至少在这三个示例中是这样)。但他们是如何做到这一点的呢?

让我们再看一看第一个示例。它在一段代码中做三件事:

URL中的url的图像=[]:对于范围内的尝试(Download_Tries):try:images.append(download_image(Url))中断,HttpError:IF尝试+1==download_tries:RAISE

如你所见,三种颜色在这里交错。这暗示相应的方面是纠缠在一起的。我所说的“纠缠”是指它们不能单独重复使用。假设我们需要在其他地方重试失败,我们可能最终会复制整个块并对其进行一些更新。不完全是“代码重用”的最佳实践。

另一方面,如果我们设法分离RETIES,那么我们的代码将如下所示:

现在红色代码在顶部被很好地分组。绿色和蓝色仍然混合在一起,但现在它们代表了一种非常常见的模式,以至于大多数现代语言都有一个内置函数来处理这种情况:

最后一个变体有一些可爱的特性:手头任务(下载图像)的每个部分只出现一次,整个迭代方面由单个map()调用处理,重试被抽象到retry函数中。

将常见行为提取到高阶函数中是让您的生活变得更美好的第一个技巧、下划线和功能使用。

现在是回到第二个例子的时候了。我将放弃错误处理,以使代码段更加均匀:

现在他们都是一行程序,那么第一个有什么更好的呢?让我们确定每个代码变体的每个不同组件:

第二个看起来像彩虹。但是,除了看起来不错之外,这意味着每次你写或读它的时候,你都需要把所有这些成分加载到你的大脑中,占据你所有的认知资源。这就是一线更好的方式。

这样我们就可以看到,第二行大约有一半是低级别的细节。低级别意味着你不需要知道所有的细节就能理解正在发生的事情。

对于序列中的x,prev=NONE:如果prev不是NONE并且x<;=prev:is_ascending=false Break prev=x Else:is_ascending=True#从空的prev开始,迭代序列,记账prev现状,每个周期上的#如果prev存在并且当前元素小于或等于它#,则将is_ascending设置为false并中断。#If循环不是';t断集is_ascending to True is_ascending=all(l<;r表示l,r成对(Seq))#set is_ascending到相邻序列对中小于右侧的所有左侧元素#。

显然,代码越多,发出的文本就越多。更高级别的代码利用更高级别的抽象生成解释。这样,我们不仅可以在编码中使用更大的构建块,而且可以在解决问题时使用更大的构建块。

我们经历的所有事情都是完全独立于语言的。所以每种语言都要加下划线?不完全是,更重要的是,直截了当的端口并不总是一个好主意:抽象的常见行为因语言而异,尤其是每种应用程序。正确的做法应该是遵循核心理念。或者四处看看是否有人已经这么做了。

附注:如果你使用的是JVM,你可能还想看看Scala,如果你使用的是.NET,你可能还想看看F#。

另外,请停止对黑客新闻的评论,一项有争议的处罚正在扼杀这篇帖子。改用reddit线程。遗憾的是,HN不再是一个讨论的地方。