胡鑫宇事件新闻发布会:那只高举的手

美国梭哈,日荷跟进,中国芯片奋力一搏还是盖牌走人?

胡鑫宇案有了新线索......事实可能比我们想象的更残酷

母子乱伦:和儿子做了,我该怎么办?

胡鑫宇死法惨烈,生前曾多次提及“草席”,生怕有湿气

生成图片,分享到微信朋友圈

自由微信安卓APP发布,立即下载! | 提交文章网址
查看原文

向 for 循环说“不”!

CSDN 2022-12-22

要:在本文中,我们一起来揭开迭代循环的面纱,并介绍一些能够完全取代迭代的编程概念。

接:https://medium.com/codex/ive-almost-stopped-using-iteration-entirely-ee34f208d7ad

声明:本文为 CSDN 翻译,未经允许禁止转载。

作者 | Emmett Boudreau
译者 | 弯月   责编 | 郑丽媛
出品 | CSDN(ID:CSDNnews)

在编写软件时,我们经常需要使用包含不同类型的数据,这种数据通常被称为“结构”。更具体地说,有些特定类型的结构又称为“可迭代对象”。

可迭代对象是一种类型,其中包含名叫“元素”的数据类型,而不是字段或属性。举个例子,Julia 中的可迭代对象包括 Dict 和 Vector,就相当于 Python 中的 list 和 dict。Dict 包含 Pair 类型的元素,而 Vector 包含其参数指定的任意类型。这些类型的结构被称为“可迭代对象”,因为它们是可以迭代。

迭代是编程中的一个非常关键的概念。事实上,迭代非常重要,就连低级汇编代码中都包含类似于循环的实现。迭代,更具体地说,for 循环,是我们在学习第一门编程语言时学习的第一个知识点。然而,迭代也有其自身的一些问题。

许多进程的执行速度减慢,究其原因最大的障碍往往就是循环。因此,我们应该最大限度地提高循环的性能,避免嵌套,并尽可能避免循环。那么,为什么我们要将循环作为默认的方式之一呢?为什么不试试看其他有助于提升性能的方式呢?

避免使用循环


有几种技巧可以避免使用循环。首先是递归,不过递归的应用范围更窄,而且性能远远比不上循环。不过,第二种方法就比较实用了。这种方法叫做“广播”(broadcasting),它将一个函数应用到给定结构中的每个元素上,因此可以很容易地一次性改变多个元素。还有一个类似的概念“映射”(mapping),从某种意义上来说,它和广播非常相似。第三种方法与它们相似,叫做“解析式”(comprehension)。这几个概念的基本思想都是将一个函数应用到所有元素上,从而在多个值上同时进行某种运算。

在编写 Python 时,最常用的方法就是 map。为了映射一个函数,我们只需用  lambda 或 def 定义它:

myfunc = lambda x: x + 1

然后将这个函数传递给 map 函数,后者将返回一个 map 对象。该对象可以转换成一个 list,从而获取其值。map 函数的第一个参数是上述函数,第二个参数是要映射的 list。

map(myfunc, [1, 2, 3, 4, 5])<map object at 0x7f33562f8fa0>list(map(myfunc, [1, 2, 3, 4, 5]))[2, 3, 4, 5, 6]

虽然 Python 中的解析式也很强大,但与 Julia 等语言相比,其功能还是太受限了。因此,大部分情况下,解析式是快速操作多个元素的最佳选择。但是,由于解析式非常善于进行小型操作(如上例),我们应该在这里演示一下。

vals = [x + 1 for x in [1, 2, 3, 4, 5]]print(vals)[2, 3, 4, 5, 6]

Julia 也有 map 函数,而且像 Python 一样,该函数非常易于使用。不过我必须承认,我在 Julia 中使用 map 的次数远远少于广播以及更强大的解析式。Julia 的 map 的语法也和 Python 非常相似,最主要的区别就是它会返回一个 Vector:

julia> map(x -> x + 1, [1, 2, 3, 4, 5, 6])6-element Vector{Int64}:234567

Julia 也支持广播。与 map 很相似,我们可以利用广播在数组内的每个元素上应用任意函数。Python 的 NumPy 也提供了此功能,但是本文主要想讨论 Julia。在 Julia 中,只需在给定函数前写一个点(.), 即可将其应用到每个元素。Julia 的基本操作符中经常被引用的一个示例如下:

x = [5, 10, 15]y = [5, 10, 15]x .* y3-element Vector{Int64}:25100225

对于其他通用的应用,该操作也可以使用 @. 宏完成。如果你想了解有关 Julia 中广播的更多信息,可以阅读这篇文章:https://medium.com/chifi-media/broadcasting-power-in-julia-beginner-friendly-overview-11cdd099623a。

像 Python 一样,Julia 也有解析式。虽然 Python 的解析式很有局限性,特别是其可读性并非太好,但 Julia 则截然不同。当然,没有任何规定组禁止你在 Python 的解析式之外定义函数,但这样做会导致一些问题,比如当需要在函数中引某个变量的时候就会很麻烦。尽管如此,虽然有时候 Python 的解析式需要借助其他东西才能清晰易读,但 Julia 的解析式可以像函数一样书写,可以写在多行内,而且支持多种语句!下面是一个解析式,它很像 Python:

x = [x - 1 for x in 1:5]

替代循环


下面,我们来看看 Julia 的解析式究竟好在哪里。首先,我们可以利用 begin/end 语法,以函数的形式编写解析式。

x = [begin x - 1 end for x in 1:5]

在解析式中添加 begin/end 敞开了一扇大门,可以让解析式完成更多的工作。我喜欢将解析式写成这样:

x = [beginx - 1end for x in 1:5]

在这个新函数中,我们可以做任何循环能做的事情,除了一些例外。例如,下面是个条件语句:

julia> x = [beginif x > 15else3endend for x in 1:10]10-element Vector{Int64}:3555555555

虽然 Python 也能实现这一点,但别忘了,我们还能添加更多的语句,甚至可以使用 try/catch 和闭包!

julia> x = [beginif x > 15elsetry"hello" * 5catch3endendend for x in 1:10]10-element Vector{Int64}:3555555555


需要循环的情况


虽然用函数替换循环能节省不少时间,但循环在编程语言和软件中依然有着重要的作用。最值得一提的是,循环的关键字 break 和 continue 是其他方法都没有的。这可以节省很多不必要的循环。break 关键字能结束循环,而 continue 关键字可以直接跳转到下一次循环。下面是选择循环的一个例子,主要目的是在找到适当的值时,利用 break 跳出循环:

function display(d::OliveDisplay, m::MIME{<:Any}, o::Any)T::Type = typeof(o)mymimes = [MIME"text/html", MIME"text/svg", MIME"text/plain"]mmimes = [m.sig.parameters[3] for m in methods(show, [IO, Any, T])]correctm = nothingfor m in mymimesif m in mmimescorrectm = mbreakendendshow(d.io, correctm(), o)end

在这个例子中,我们依次遍历 MIME,找到最有可能的值。在已有的 MIME(即 mmimes)中找到所需的 mime 后,设置 correctm,然后将其传递给 show 函数。这个例子中,相较于映射或解析式,选择迭代更合适,因为它拥有独到的优势。


总结


一直以来,迭代式循环都是编程中的一个重要的概念,在高级语言中得到了广泛的应用。循环是从硬件层面上实现的,其重要性不言而喻。但是,许多现代编程语言完全可以不使用循环,我们可以利用函数、映射和解析式来实现相同的功能。但是,在有一些情况下,选择循环依然比解析式或映射更好。一种情况就是可以利用 break 和 continue 关键字跳过元素、加快循环。

雷军回应对标iPhone被笑话:没勇气就做不好高端手机;GitHub发布企业版Copilot;Linux 6.1 发布|极客头条
47 岁从华为退休,操作系统老兵转战 OpenHarmony 生态 | 近匠
☞“CSDN 2022 中国开发者影响力年度评选”正式开启报名!

文章有问题?点此查看未经处理的缓存