为什么你应该用Julia编程?

822 阅读21分钟

你可以从数百种编程语言中进行选择,其中许多语言远比 Julia 更加知名。那么,为什么要选择 Julia 呢?简短的回答是,Julia 具有广泛的应用,许多功能,并且易于学习和使用。它在数据科学、复杂的线性代数、数据挖掘和机器学习方面非常出色,但你可以用它来做更多事情。

什么是Julia?

Julia是一种通用的、多平台的编程语言它:

  • 是数值型的
  • 是动态类型的
  • 具有自动内存管理(垃圾收集)功能
  • 是高性能和即时编译(JIT)

好吧,这有很多,其中有些东西听起来像是矛盾的。茱莉亚怎么可能既是一种通用语言,又是为数字编程量身定做的?它是通用的,因为像 Python 一样,Julia 几乎可以用于任何东西。它是数值型的,因为像 Matlab 一样,Julia 很适合数值型编程。

静态类型语言和动态类型语言的优点和缺点

让我们关注一下Julia的一个方面:它是动态类型的。通常,编程语言被分为两大类:

  • 动态类型的
  • 静态类型的

"在静态语言中,表达式有类型;在动态语言中,值有类型。"

- Stefan Karpinski,Julia的创建者

静态类型语言的例子有C/C++、C#、Java、Swift、Go、Rust、Pascal和Fortran。在静态类型语言中,在你的程序被允许运行之前,会对你的所有代码进行类型检查。

动态类型语言的例子有Python、Perl、Ruby、JavaScript、Matlab和LISP。动态类型的语言在程序运行时进行类型检查。不幸的是,动态类型的语言往往非常慢。

在动态语言中,数字、字符和字符串等值都有附加标签,说明它们是什么类型。这些标签使得用动态类型语言编写的程序可以在运行时检查类型的正确性。

Julia 的不寻常之处在于,它既是一种动态类型的语言,又是一种高性能的语言。对许多人来说,这是个矛盾的问题。茱莉亚的这一独特特征之所以能够实现,是因为该语言是专门为即时编译(JIT)而设计的,并对所有的函数调用使用了一种叫做多重调度的特性。像C/C++和Fortran这样的语言使用超时空(AOT)编译。通过AOT编译,编译器在运行前将整个程序翻译成机器代码。其他语言,如Python、Ruby和Basic,使用解释器。对于解释型语言,程序会读取每一行源代码,并在运行时对其进行解释以执行指令。

现在你对 Julia 是哪种语言有了一些了解,我们可以开始谈论 Julia 的吸引力了。

Julia结合了优雅、生产力和性能

虽然性能是Julia的关键卖点之一,但在2013年我第一次发现Julia时,吸引我注意的是它是多么的深思熟虑、强大和易于使用。我有一个用几种语言重写的程序,以比较每种语言的表现力、易用性和生产力如何。通过Julia,我成功地做出了这段代码的最优雅、最紧凑和最容易阅读的变体。从那时起,我尝试了许多编程语言,但从未接近过我用Julia实现的目标。这里有几个单行本,可以让人感受到 Julia 的表达能力。

清单 1.茱莉亚的单行语

 filter(!isempty, readlines(filename)) # strip out empty lines
 filter(endswith(".png"), readdir())   # get PNG files
 findall(==(4), [4, 8, 4, 2, 5, 1])    # find every index of the number 4

我从 20 世纪 90 年代就开始编程,我曾有过对编程感到厌倦的时期。Julia 帮助我重新获得了编程的乐趣。部分原因是,一旦你掌握了 Julia,你就会觉得你有一门为你工作的语言,而不是反对你的语言。我想我们中的许多人都有过这样的经历:我们对如何解决一个问题有很好的想法,但我们所使用的语言却阻碍了我们。语言的局限性迫使我们拼凑出不恰当的解决方案。有了Julia,我可以按照我想要的方式构建软件,而不需要语言设置障碍。

另一个增加你的生产力和乐趣的方面是,Julia 捆绑了一个丰富的标准库。你可以立即投入工作。你可以完成很多事情,而不需要在网上到处寻找一些库来做你想做的事情。无论你想做线性代数、统计、HTTP、字符串操作,还是处理不同的日期格式,Julia 都能满足你的要求。如果你想要的功能不在标准库中,Julia 有一个紧密集成的包管理器,使得添加第三方库变得轻而易举。

对Julia进行编程几乎让你感到内疚或被宠坏了,因为你可以建立丰富而优雅的抽象,而不会对性能产生影响。

Julia 的另一个重要优势是,它很容易学习。这种易学性帮助Julia随着时间的推移获得了一个更大的社区。为了理解为什么 Julia 易于学习,请考虑一下用 Julia 写的无处不在的 "hello world "程序:

 print("Hello world")

当运行时,这段代码将文本 "hello world "写到屏幕上。虽然是小事一桩,但许多语言需要很多复杂的脚手架来做这么简单的事情。这是一个做同样事情的Java程序:

 public class Main {
     public static void main(String[] args) {
         System.out.print("hello world");
     }
 }

这让初学者一下子接触到了更多的概念,这可能会让人不知所措。Julia更容易学习,因为我们可以一次集中学习一个概念。你可以在没有看到类型定义的情况下学会写一个函数。由于有很多开箱即用的功能,你甚至不需要知道如何导入外部库来编写有用的代码。

为什么创建了Julia

为了真正理解 Julia 所带来的东西,我们需要更好地理解为什么Julia 首先被创建。Julia编程语言的创造者们想要解决所谓的双语言问题

它指的是这样一个事实:很多软件都是用两种不同的编程语言编写的,每一种都有不同的特点。在科学领域,机器学习和数据分析的动态语言往往是首选。然而,这些语言通常不能提供良好的性能。因此,解决方案往往要用性能更强的静态类型的语言来重写。但为什么会有这种偏好呢?为什么不用传统的高性能静态类型语言来写一切呢?

科学家们需要动态类型语言所提供的交互式编程

科学家们开始用Fortran[1]编写大型天气模拟软件,用C或C++[3]编写神经网络[2]。这些语言提供了你处理大型问题所需要的那种性能。然而,这种性能是有代价的。这些语言往往是死板的、啰嗦的,而且缺乏表达能力,所有这些都会降低程序员的工作效率。

然而,最根本的问题是,这些语言不适合于交互式编程。我这么说是什么意思呢?交互式编程意味着能够编写代码并获得即时反馈。

交互式编程在数据科学和机器学习中非常重要。在一个典型的数据分析过程中,数据的探索是由开发人员将大量的数据加载到一个交互式编程环境中。然后,开发人员对这些数据进行各种分析。它可能是寻找平均数和最大值。它可能是绘制直方图。第一次分析的结果告诉程序员接下来的步骤应该是什么。

图1显示了动态类型语言的这个过程。你从运行代码开始,代码加载了数据,然后你就可以观察这些数据。然而,你不必在改变代码后再去经历这整个过程。你可以改变代码并立即观察变化。你不需要再次加载大量的数据。


图1.在动态类型的语言中,我们可以在编码和观察之间乒乒乓乓。大型数据集不需要重新加载到内存中。


让我们把这种体验与使用静态类型的语言,如Fortran、C/C++或Java[4]进行对比。开发者会写一些代码来加载数据并进行探索,而对数据的样子一无所知。然后他们将不得不等待该程序。

  1. 编译
  2. 启动
  3. 载入大量的数据

在这一点上,开发者看到了数据和统计数据的图表,这为他们提供了选择下一步分析所需的信息。但是,选择下一个分析需要再次重复整个周期。大块的数据在每次迭代时都要重新加载。这使得每次迭代都很慢。这就减慢了整个分析过程的速度。这是一种静态的、非交互式的编程方式。


图2.静态类型的语言需要重复整个循环。


其他领域的开发者也需要动态类型的语言所提供的互动性

这个问题并不是科学家独有的。长期以来,游戏开发者也面临着同样的问题。我们所说的游戏引擎通常是用C或C++这样的语言编写的,可以编译成快速的机器代码。软件的这一部分通常会做一些被充分理解和定义的事情,比如在屏幕上画物体,并检查它们是否相互碰撞。

像数据分析师一样,游戏开发者有大量的代码,这些代码需要多次迭代才能令人满意地工作。具体来说,开发好的游戏需要大量的实验和迭代。人们必须调整和改变代码,以确定游戏中人物的行为方式。地图或关卡的布局必须经过反复试验才能获得正确的结果。由于这个原因,几乎所有的游戏引擎都使用第二种语言,允许对代码进行即时修改。这些语言通常是Lua[5]、JavaScript或Python[6]等语言。

使用这些语言,游戏角色和地图的代码可以被改变,而不需要重新编译和重新加载地图、关卡和角色。因此,人们可以尝试游戏的玩法,暂停,进行代码修改,然后直接继续新的修改。

机器学习专业人士也面临类似的挑战。他们建立预测模型,如神经网络,他们提供大量的数据来训练。这往往是一门科学,也是一门艺术。要做到这一点,需要进行实验。如果你每次修改模型都需要重新加载训练数据,就会拖慢开发过程。由于这个原因,动态类型的语言,如Python、R和MATLAB在科学界变得非常流行。

然而,由于这些语言的速度并不快,它们会与Fortran或C/C++等语言配对,以获得良好的性能。像TensorFlow[7]或PyTorch[8]这样的神经网络是由用C/C++编写的组件组成的。Python被用来安排和连接这些组件。因此,在运行时,我们可以使用Python重新排列这些组件,而无需重新加载整个程序。

气候模型和宏观经济模型可能会先用动态语言开发,并在开发时在一个小的数据集上进行测试。一旦模型完成,许多组织会雇用C/C++或Fortran开发人员,用高性能语言重写解决方案。因此,我们得到了一个额外的步骤,使开发过程复杂化并增加了成本。

Julia的更高的性能解决了双语言的问题

为了解决必须使用两种语言的问题,Julia应运而生。它使得动态类型语言的灵活性与静态类型语言的性能相结合成为可能。这就是为什么我们喜欢这样说。

"Julia 走路像 Python。C一样运行"。

- 在Julia社区的流行说法

使用Julia,许多领域的开发者可以用与Python、Ruby、R和Matlab等语言一样的生产力来编写代码。正因为如此,Julia对行业产生了深远的影响。在2019年7月的《自然》杂志中,有对多位科学家关于他们使用Julia的采访。

例如,在墨尔本大学,他们通过将计算模型从R移植到Julia,获得了800倍的改进。

"你可以在一小时内完成原本需要几周或几个月的事情。"

- Michael Stumpf

系统生物学家 UOM

加州理工学院材料科学系的Jane Herriman报告说,自从用Julia重写她的Python代码以来,运行速度提高了10倍。

在2019年国际超级计算会议(SC19)上,Julia的创建者之一Alan Edelman讲述了麻省理工学院(MIT)的一个小组如何用Julia重写他们的Fortran气候模型的一部分。他们提前决定,他们可以容忍他们的代码慢3倍的速度。在他们看来,这是一个可以接受的交易,以获得一种具有更高生产力的高级语言。相反,他们通过切换到Julia得到了3倍的速度提升。

这些只是今天关于Julia是如何彻底改变科学计算和高性能计算的众多故事中的几个而已。通过避免两种语言的问题,科学家可以比以前更快地工作。

茱莉亚适用于所有人

这些故事可能会给人一种错误的印象,认为Julia只适合穿白大褂的脑力劳动者。但没有什么比这更远离事实了。事实证明,使Julia成为科学家的最佳语言的许多特征,也使它成为其他所有人的最佳语言Julia 提供了:

  • 模块化和重复使用代码的强大设施。
  • 一个严格的类型系统,它有助于在你的代码运行时发现错误。
  • 一个复杂的系统用于减少重复的模板代码(元编程[9])。
  • 一个丰富而灵活的类型系统,允许你对各种各样的问题进行建模。
  • 一个装备精良的标准库和各种第三方库来处理各种任务。
  • 伟大的字符串处理设施。这通常是任何瑞士军刀风格的编程语言的一个关键卖点。这也是使Perl、Python和Ruby等语言最初流行的原因。
  • 易于与其他各种编程语言和工具对接。

虽然Julia的最大卖点是它修复了双语言问题,但这并不意味着完全缓解了与现有的Fortran、C或C++代码对接的需要。修复双语言问题的意义在于,避免每次遇到性能问题时都要编写 Fortran 或 C 代码。你可以一直坚持使用 Julia。然而,如果有人已经用另一种语言解决了你的问题,那么你在 Julia 中从头开始重写这个解决方案可能就没有意义了。Python、R、C、C++ 和 Fortran 都有多年来建立的大型软件包。Julia 社区不可能在一夜之间取代这些。为了提高生产力,Julia 开发人员需要能够利用现有的软件解决方案。

从长远来看,将传统的软件过渡到Julia是有明显优势的。维护旧的 Fortran 库可能需要比维护 Julia 库多得多的开发人员的努力。

最大的好处可能是 Julia 提供的组合能力。有一些类型的问题需要构建大型的单体库。相比之下,Julia 特别适合于制作小型库,这些小型库可以很容易地被组合起来,以匹配其他语言中大型单体库所提供的功能。让我举一个例子。

机器学习为自动驾驶汽车、人脸识别、语音识别和许多其他创新技术提供动力。最著名的机器学习包是PyTorch和TensorFlow。这些软件包是由大型团队维护的巨大单体。它们之间不存在代码共享。Julia有大量的机器学习库,如Knet、Flux[10]和Mocha[11]。相比之下,这些库都很小。为什么呢?因为PyTorch和TensorFlow的能力可以通过结合Julia中的多个小库来匹配。解释更多关于这一点的原因是一个复杂的话题,需要对Julia和神经网络库的工作原理有更深的了解。如果你有兴趣,我在书中会更多地谈及这个问题

许多小的库是一个具有普遍应用的优势。任何构建任何种类的软件的人都会从以多种新的方式重用现有的软件而不是重新发明轮子的能力中受益。对于传统的编程语言,人们往往不得不重复实现相同的功能。例如,TensorFlow和PyTorch就有很多重复的功能。Julia通过将更多的功能放在许多机器学习库之间共享的库中来避免重复。随着你对Julia的进一步了解,你会越来越清楚它是如何做到这一点的,以及为什么这种能力在许多其他语言中是难以实现的。

我可以用 Julia 构建什么?

原则上说,你可以用 Julia 来构建任何东西。然而,每一种语言都有一个软件包的生态系统和一个社区,它们可能会推动你更多地去进行其他类型的开发而不是其他。Julia 也不例外。

Julia 在科学领域的应用

Julia 在科学领域有很强的影响力。例如,它被用于以下方面:

  • 计算生物学
  • 统计学
  • 机器学习
  • 图像处理
  • 计算微积分
  • 物理学

Julia 也被用于其他领域。例如,它被用于能源交易。美国联邦储备局用它来建立复杂的宏观经济模型。诺贝尔奖得主 Thomas J. Sargent 创立了 QuantEcon,这是一个使用 Julia 和 Python 推进量化经济学教学的平台。他是Julia的坚定支持者,因为宏观经济学中的大问题很难用其他编程语言解决。

在采访Lukas Biewald时,在谷歌工作的著名人工智能(AI)研究员Peter Norvig曾表示,他认为机器学习领域将从改用Julia中获益匪浅。

"如果Julia是人工智能的主要语言,我会更高兴。"

- 彼得-诺维格

人工智能的作者*。A Modern Approach》一书的作者。*

生命科学是Julia的另一个明显的领域。到2025年,每年将有2-40兆字节的人类基因组数据被收集。大多数主流软件无法处理这种规模的数据。你需要像Julia这样的高性能语言,它可以在各种硬件上以尽可能高的性能处理各种格式的数据。

Julia在当代医学科学中也有体现。Julia软件包Pathogen被用来模拟传染病,并被COVID19的研究人员广泛使用。

茱莉亚的非科学用途

那么非科学的东西呢?Julia 也有大量的包用于其他兴趣:

  • Genie - 一个全栈的MVC网络框架。
  • Blink - 让你在 Julia 中制作 Electron GUI 应用程序。
  • Gtk - 使用流行的 Linux GUI 工具包 Gtk 来制作 Julia GUI 应用程序。
  • QML - 让你使用Qt GUI工具包中使用的QML标记语言创建跨平台GUI。
  • GameZero - 面向初学者的游戏开发。
  • Luxor - 绘制矢量图像。
  • Miletus - 编写金融合同。
  • TerminalMenus - 允许在终端的交互式菜单。
  • Gumbo - 解析HTML页面。
  • Cascadia - 一个CSS选择器库。人们可以用它来进行网络刮削,从网页上提取有用的信息。
  • QRCode - 创建流行的QR码的图像,并添加显示机器可读的URL。

正如你所看到的,Julia 有用于通用编程的包。

Julia 不太理想的地方

原则上,Julia 几乎可以用来做任何事情,但是作为一种年轻的语言,意味着在每个领域的库的选择都不太全面。例如,用于网络开发的包的选择是有限的。用 Julia 构建类似于移动应用程序的东西会很困难。它也不适合小型的、短时运行的脚本,就是你经常用 Bash、Python 或 Ruby 写的那种。其原因是,Julia 是 JIT 编译的。

这意味着Julia程序的启动速度比Python或Bash程序慢,但一旦JIT编译器将代码的关键部分转换为机器代码,就会开始快速运行。在Julia社区中,有一个持续的努力来减少这个问题。有无数的方法来解决这个问题,从更好地缓存以前的JIT编译,到更有选择性地对某些东西进行JIT编译。

Julia也不是实时系统的理想选择。在一个实时系统中,软件必须对现实世界中发生的事情做出反应。你可以将其与天气模拟器进行对比,比如说,天气模拟器。对于一个天气模拟器来说,在运行模拟的计算机之外的世界发生什么并不重要。

然而,如果你的程序必须处理每一毫秒从测量仪器传来的数据,那么你就不能有突然的打嗝或延迟。否则你就有可能失去重要的测量结果。Julia是一种垃圾收集语言。这意味着在你的程序中不再使用的数据会被自动回收用于其他用途。在确定回收哪些内存的过程中,往往会在程序执行中引入小的随机延迟和打嗝。

这个问题不应该被夸大。需要实时行为的机器人技术正在用Julia进行。麻省理工学院的研究人员对波士顿动力公司的Atlas仿人机器人在平地上的平衡进行了实时控制模拟。这是为了证明Julia可以通过调整Julia分配和释放内存的方式用于机器人的在线控制。

Julia不太适合于内存有限的嵌入式系统。原因是,Julia通过创建同一代码的高度专业化版本来实现高性能。因此,在Julia中,代码本身的内存使用量会比C/C++或Python等要高。

最后,就像Python,Ruby和其他动态语言一样,Julia不适合典型的系统编程,如制作数据库系统或操作系统内核。这些任务往往需要对资源的使用进行详细的控制,而 Julia 并不提供这种控制。Julia 是一种以易用性为目标的高级语言,这意味着许多关于资源使用的细节都被抽象出来了。

希望我已经吊起了你的胃口,你想更多地了解Julia!


[1]Fortran是一种用于科学计算的古老语言

[2]神经网络是一种受到人脑工作原理启发的算法。

[3]C和C++是两种相关的、广泛用于系统编程的静态类型语言

[4]Java被用于大量的网络服务器软件和安卓手机上。

[5]Lua最初是作为一种配置语言,但今天主要用于编写计算机游戏

[6]Python是当今数据科学和机器学习中最流行的语言之一。

[7]TensorFlow是一个著名的Python神经网络库。

[8]PyTorch是一个著名的Python神经网络库。

[9]元编程是写代码的代码。本书中没有涉及的一个高级概念。

[10]在这里了解更多关于Flux机器学习库的信息:https://fluxml.ai

[11]Mocha是一个由MIT创建的Julia机器学习库:https://developer.nvidia.com/blog/mocha-jl-deep-learning-julia/