有一门语言,它看起来像 Python ,感觉起来像 Lisp ,运行起来又像 C 一样快速,他就是 Julia。
近年来,Julia 语言已然成为编程界的新宠,在去年 TOIBE 8 月份编程语言排行榜上,Julia 已迅速攀升至第 50 名,尤其在科学计算和人工智能领域炙手可热。
与其他语言相比,Julia 易于使用,大幅减少了代码行数;并且能够很容易地部署于云容器,有更多的工具包和库,并且结合了多种语言的优势。据 Julia Computing 的宣传,在七项基础算法的测试中,Julia 比 Python 快 20 倍,比 R 快 100 倍,比 Matlab 快 93 倍。
为什么 Julia 能做到这些,看一下它的开发者的初衷:
“我们想要的是一种自由开源的语言,它同时拥有 C 的速度和 Ruby 的动态性;……我们想要一个可以像 Python 一样作为通用编程语言的工具, 像 R 那样适用于统计分析,像 Perl 那样自然地处理字符串,像 MATLAB 那样给力地处理矩阵运算,它还要能像 Shell 一样作为胶水将各种程序粘合在一起……”
Julia 在开发之初就将这些理念纳入其中,才有了今天的成绩。实验楼上线了一门免费的 Julia 入门课程,包含 6 个实验和一个挑战。你将学习到 Julia 的基础语法,并亲手用 Julia 开发出一个小项目。
Julia 简明教程 - https://www.shiyanlou.com/courses/1485
以下为课程一二节内容:
一、Julia 简介和安装
实验简介
本课程作为 Julia 这门编程语言的入门教程,旨在介绍其基础语法,希望大家能够通过本课程的学习,熟练掌握 Julia 的语法风格和编程习惯。
知识点
Julia 简介
在实验环境中安装 Julia
Julia REPL 用法
Julia 简介
根据维基百科中的描述,大约在 2009 年,一群拥有各种语言丰富编程经验的 Matlab 高级用户,对现有的科学计算编程工具感到不满 —— 这些软件对自己专长的领域表现得非常棒,但在其它领域却非常糟糕。
他们想要的是一个开源的软件,它要像 C 语言一般快速而又拥有如同 Ruby 的动态性;要具有 Lisp 般真正的同向性而又有 Matlab 般熟悉的数学记号;要像 Python 般通用、像 R 一般在统计分析上得心应手、像 Perl 般自然地处理字符串、像 Matlab 般具有强大的线性代数运算能力、像 shell 一般拥有胶水语言的能力,易于学习而又不让真正的黑客感到无聊;还有,它应该是交互式的,同时又是编译型的。
最后 Julia 出现之后,大家评价它看起来像 Python ,感觉起来像 Lisp ,运行起来像 C 。
以下描述来自官方文档:
科学计算对性能一直有着最高的需求, 但现在这个领域的专家开始大量使用比较慢的动态语言来完成日常工作。我们相信有很多使用动态语言的理由, 所以我们不会舍弃这样的特性。幸运的是, 现代语言设计和编译器技术使得为原型设计提供单一的高效开发环境,并且配置高性能的应用成为可能。Julia 语言在这其中扮演了这样一个角色:
作为灵活的动态语言,适合科学和数值计算,性能可与传统静态类型语言媲美。
由于 Julia 的编译器和其它语言比如 Python 或 R 有所不同,一开始您或许会觉得 Julia 中什么样的代码运行效率高,什么样的代码运行效率低似乎并不很直观。如果您发现 Julia 变慢了,我们非常建议您在尝试其它功能前读一下 代码性能优化 。只要您理解 Julia 的工作方式,就会很容易地写出运行效率甚至可以和 C 相媲美的代码。
通过使用类型推断和 即时 (JIT) 编译 ,以及 LLVM , Julia 具有可选的类型声明,重载,高性能等特性。Julia 是多编程范式的,包含指令式、函数式和面向对象编程的特征。它提供了简易和简洁的高等数值计算,它类似于 R 、 MATLAB 和 Python ,支持一般用途的编程。为了达到这个目的,Julia 在数学编程语言的基础上,参考了不少流行动态语言: Lisp 、 Perl 、 Python 、 Lua 和 Ruby 。
Julia 与传统动态语言最大的区别是:
核心语言很小;标准库是用 Julia 本身写的,如整数运算在内的基础运算
完善的类型,方便构造对象和做类型声明
基于参数类型进行函数 重载
参数类型不同,自动生成高效、专用的代码
高性能,接近静态编译语言,如 C 语言
动态语言是有类型的:每个对象,不管是基础的还是用户自定义的,都有类型。许多动态语言没有类型声明,意味着它不能告诉编译器值的类型,也就不能准确的判断出类型。静态语言必须告诉编译器值的类型,类型仅存在于编译时,在运行时则不能更改。在 Julia 中,类型本身就是运行时对象,同时它也可以把信息传递给编译器。
重载函数由参数(参数列表)的类型来区别,调用函数时传入的参数类型,决定了选取哪个函数来进行调用。对于数学领域的程序设计来说,这种方式比起传统面向对象程序设计中操作属于某个对象的方法的方式更显自然。在 Julia 中运算符仅仅是函数的别名。程序员可以为新数据类型定义 “+” 的新方法,原先的代码就可以无缝地重载到新数据类型上。
因为运行时类型推断(得益于可选的类型声明),以及从开始就看重性能,Julia 的计算性能超越了其他动态语言,甚至可与静态编译语言媲美。在大数据处理的问题上,性能一直是决定性的因素:在刚刚过去的十年中,数据量还在以摩尔定律增长着。
Julia 想要变成一个前所未有的集易用、强大、高效于一体的语言。除此之外,Julia 的优势还在于:
免费开源( MIT 协议 )
自定义类型与内置类型同样高效、紧凑
不需要把代码向量化;非向量化的代码跑得也很快
为并行和分布式计算而设计
轻量级 “绿色” 线程( 协程 )
低调又牛逼的类型系统
优雅、可扩展的类型转换
高效支持 Unicode, 包括且不只 UTF-8
直接调用 C 函数(不需封装或 API)
像 Shell 一样强大的管理其他进程的能力
像 Lisp 一样的宏和其他元编程工具
安装 Julia
打开实验环境,我们要安装目前最新的 Julia1.3 版本(截止 2020 年 1 月)。
首先,在终端执行 wget-c https://julialang-s3.julialang.org/bin/linux/x64/1.3/julia-1.3.1-linux-x86_64.tar.gz
命令下载安装包,这是官方提供的下载地址。因为此安装包比较大,90 多 M ,所以建议大家执行如下命令下载它:
wget https://labfile.oss.aliyuncs.com/courses/1485/julia-1.3.1-linux-x86_64.tar.gz
首先在终端命令行执行 cd
命令切换到用户家目录 /home/shiyanlou ,然后再执行上述命令:
下载完成之后,会在当前目录下出现 Julia 的安装包 julia-1.3.1-linux-x86_64.tar.gz ;执行 tar xzvf julia-1.3.1-linux-x86_64.tar.gz
命令,即可完成解包;然后将 Julia 的可执行文件创建一个软连接到 /usr/bin 目录下即可完成全部安装工作:
cd /usr/bin
sudo ln -s ~/julia-1.3.1/bin/julia julia
cd
julia
如上图所示,终端执行 julia 命令即可进入命令行交互解释器,也叫 REPL 。这个是不是有些熟悉呢?各个数据库以及 Python 都有这个交互工具。输入 exit()
或按下快捷键 Ctrl + D 即可退出,终端执行 julia-version
命令可以查看版本:
启动 Julia 命令行交互解释器的时候,会打印一张横幅,如上图所示。我们可以创建一个文件,使其在打印横幅之前,打印一些别的:
mkdir .julia/config
echo 'println("Hello Shiyanlou")'> .julia/config/startup.jl
julia
如上图所示,在启动 REPL 时,先打印了一行字:Hello Shiyanlou 。熟悉 Python 的同学都知道打印信息到屏幕上使用的是 print 方法,在 Julia 中是 println 方法,而且需要切记的是,在 Julia 中单引号和双引号不可混用,只有双引号才能描述字符串。
Julia REPL 的四种模式
进入 REPL 之后,会有 julia>
字样的提示符,这是常规模式,我们可以在其中执行一些代码:
julia> println("hello kitty")
hello kitty
julia> 2+ 3
5
julia> ans
5
julia> x = 5
5
julia> x -= 1
4
julia> x
4
julia> x *= 2
8
julia> x
8
julia>
如上所示,ans 这个变量会保存上一次命令的执行结果,它仅在命令行交互解释器中适用,不可写到 jl 文件里,Julia 的文件后缀为 jl ,如同 Python 文件的后缀为 py 。
除了常规模式,REPL 还有 help 模式、shell 模式和 package 模式。
help 模式
在常规模式下输入问号即可进入 help 模式,此模式的提示符是 help?>
,输入一个方法,即可显示此方法的帮助信息,然后自动回到常规模式:
这等同于在常规模式下使用 @doc 这个宏来查看帮助信息:
shell 模式
在常规模式下输入分号 ;
进入 shell 模式,提示符为 shell>
,这等同于环境终端,可以执行 zsh 命令,同样是执行完一个命令后自动退回到常规模式:
package 模式
常规模式下输入中括号 ]
进入 pkg 模式,提示符为 (v1.3)pgk>
,括号里面是版本号,这是包管理模式。
该模式下输入 status
命令可以查看包状态, add
命令添加工具包, rm
命令删除工具包:
安装这个包需要较长时间,执行 rm
命令删除它:
按退格键 Backspace
回到常规模式。
总结
本节实验主要介绍了 Julia 语言的来源,它与很多其它语言很像,这是因为作者是集各家之长创造的 Julia 。在实验环境中的安装方法十分简单,下载安装包,解包,设置软连接。
此外还介绍了命令行交互解释器的四种模式,在后面的课程中,我们会用到它们。
下一节实验来学习 Julia 的变量。
二、Julia 变量和数据类型
实验简介
本节实验我们来了解 Julia 的变量命名规则和一些常见的数据类型。
知识点
变量
整数和浮点数
布尔值
运算符
复数
字符串
变量
Julia 作为动态语言,它的变量可以随时被赋值为任意数据类型:
julia> s = 1
1
julia> s = "Hello Kitty"
"Hello Kitty"
julia> s = 1.23
1.23
julia> s = true
true
julia> s
true
julia>
变量名的使用规则与大多数语言类似,但稍有不同:
变量名须以大小写字母或下划线开头
变量的命名区分大小写
单词之间可以使用下划线连接 hell_world
类名要使用大驼峰命名法 OneTwoThree
函数名和宏名使用全小写
修改参数的函数结尾使用叹号
!
此外还可以使用 Unicode 字符来命名,这其中就包括各国文字,例如中文、希腊字母。其中希腊字母表的第一个字母 α 可以使用 \alpha+Tab
键生成,第二个字母 β 使用 \beta+Tab
生成:
julia> α = "Hello"
"Hello"
julia> β = "World"
"World"
julia> println(α)
Hello
julia> 你好 = "How are you?"
"How are you?"
julia> println(你好)
How are you?
julia>
与其它语言一样,Julia 的保留字不可作为变量名:
julia> else= "Hello"
ERROR: syntax: unexpected "else"
Stacktrace:
[1] top-level scope at REPL[11]:0
julia> end= 2
ERROR: syntax: unexpected "end"
Stacktrace:
[1] top-level scope at REPL[11]:0
julia> for= 3.14
ERROR: syntax: unexpected "="
Stacktrace:
[1] top-level scope at REPL[11]:0
julia>
在后面的实验中,我们会陆续接触一些 Julia 的保留字。
数据类型
Julia 提供了丰富的基础数据类型,包括整数、浮点数、复数、分数、字符串等,接下来我们依次了解它们。
整数
整数分为两大类:Int 和 UInt 。它们的区别是有没有表示负数的负号 -
。整数的默认数据类型取决于操作系统的位数,32 位操作系统的整数默认类型是 Int32 ,64 位操作系统的整数默认类型是 64 位,在 shell 模式中执行 sudo uname--m
命令查看操作系统位数:
下划线后面的数字就是系统位数,实验环境中的操作系统是 64 位。此外,还可以直接输入 Int ,打印结果即可显示操作系统位数:
julia> Int
Int64
所以一个整数的默认数据类型就是 Int64 ,可以使用 typeof 方法查看:
julia> s = 12
12
julia> typeof(s)
Int64
julia> typeof(12)
Int64
julia> typeof(-4)
Int64
julia>
以上所写的整数均为有符号的整数,无符号整数以 0x
开头,它们的数据类型是 UInt :
julia> s = 0x12
0x12
julia> s
0x12
julia> typeof(s)
UInt8
julia> typeof(0x12)
UInt8
julia> typeof(0x1234567)
UInt32
julia>
对一个整数进行加减乘除可以简写为 +=-=*=/=
,注意进行除法运算后,会修改数据类型为 Float64 :
ulia> s = 10
10
julia> s += 1
11
julia> s
11
julia> s -= 1
10
julia> s *= 3
30
julia> s /= 6
5.0
julia> 2/ 1
2.0
julia>
浮点数
浮点数即带有小数点的数值,它的默认数据类型是 Float64 ,因为实验环境的操作系统是 64 位,同样如果操作系统的位数是 32 ,浮点数的默认数据类型是 Float32 。
举例如下:
julia> s = 1.2
1.2
julia> typeof(s)
Float64
julia> typeof(1.2)
Float64
julia> s = .3
0.3
julia> s
0.3
julia> s = 1.
1.0
julia> s
1.0
julia>
我们可以使用字母 e 表示科学计数法:
julia> 3.14e3
3140.0
julia> -8e-2
-0.08
julia> 1e10
1.0e10
julia> typeof(2e2)
Float64
julia>
这种表达式 aeb 的值为 a 乘以 10 的 b 次方,3.14e3 的值即为 3.14 乘以 10 的 3 次方。
基础数值类型的最小值和最大值,可由 typemin
和 typemax
函数查询:
julia> a = Int64
Int64
julia> typemin(a), typemax(a)
(-9223372036854775808, 9223372036854775807)
julia> typemin(Float16), typemax(Float16)
(-Inf16, Inf16)
julia>
带符号的浮点数有三种:
类型
精度
位数
| 半精度 | 16 |
| 单精度 | 32 |
| 双精度 | 64 |
一个 Float64 类型的数值占 8 个字符,因为每个字符是 8 位二进制。同理,Float32 类型的数值占 4 个字符的空间,我们可以使用类型名转换一个值为此类型,使用 sizeof 方法查看数值的大小:
julia> s = 12
12
julia> typeof(s)
Int64
julia> sizeof(s)
8
julia> sizeof(Float32(12))
4
julia> sizeof(Float16(12))
2
布尔值
同大多数语言一样,布尔值只有 true 和 false 两个,表示真和假,通常用于判断。前面提到双等号判断值的大小,结果为真即是 true ,否则为 false 。布尔值只需 1 位二进制即可存储,所以它们的大小为 1 个字节:
julia> sizeof(true)
1
julia> sizeof(false)
1
在布尔值前面加叹号 !
表示非:
julia> !true
false
julia> !false
true
运算符
算术运算
加减乘除不必说了,此外还有反除 \
、幂 ^
和取余数 %
:
julia> 2 \ 10
5.0
julia> 2^ 3
8
julia> 10% 3
1
比较运算符
除了上面介绍的双等号判断值是否相等以外,还有如下表所示这些:
运算符
名称
| 等于 |
| 不等于 |
| 小于 |
| 小于等于 |
| 大于 |
| 大于等于 |
示例如下:
julia> 1== 1
true
julia> 1== 2
false
julia> 1!= 2
true
julia> 1== 1.0
true
julia> 1< 2
true
julia> 1.0> 3
false
julia> 1>= 1.0
true
julia> -1<= 1
true
julia> -1<= -1
true
julia> -1<= -2
false
julia> 3< -0.5
false
复数
Julia 提供复数类型,并对其支持所有的标准数学运算 。
关于复数,维基百科上的定义是:复数,为实数的延伸,它使任一多项式方程都有根。复数当中有个 “虚数单位” i ,它是 -1 的一个平方根,即 i² = -1。任一复数都可表达为 x + yi ,其中 x 和 y 皆为实数,分别称为复数之 “实部” 和 “虚部”。
在 Julia 中,有一个全局变量 im 表示 -1 的平方根:
julia> im
im
julia> s = 2+ 5im
2+ 5im
julia> s
2+ 5im
julia> s + 3- 2im
5+ 3im
julia> a = 1; b = 2; a + b * im
1+ 2im
julia>
也可以对复数做标准算术:
julia> (1+ 2im)*(2- 3im)
8+ 1im
julia> (1+ 2im)/(1- 2im)
-0.6+ 0.8im
julia> (1+ 2im) + (1- 2im)
2+ 0im
julia> (-3+ 2im) - (5- 1im)
-8+ 3im
julia> (-1+ 2im)^2
-3- 4im
字符串
Julia 中处理 ASCII 文本简洁高效,也可以处理 Unicode 。使用 C 风格的字符串代码来处理 ASCII 字符串,性能和语义都没问题。如果这种代码遇到非 ASCII 文本,会提示错误,而不是显示乱码。
单个字符使用单引号表示,多个字符即为字符串,须使用双引号表示:
julia> s = 's'
's': ASCII/Unicode U+0073(category Ll: Letter, lowercase)
julia> s
's': ASCII/Unicode U+0073(category Ll: Letter, lowercase)
julia> s = "Hello Kitty"
"Hello Kitty"
julia> s
"Hello Kitty"
julia> typeof('s')
Char
julia> typeof("Hello Kitty")
String
julia>
可以使用 Int
函数将字符转换为数字,对应的就是 ASCII 或 UNICODE 编码表中的数值,反之,使用 Char
可以将数值转换为对应的字符(注意单个的中文汉字或中文标点也是字符):
julia> Int('a')
97
julia> Char(97)
'a': ASCII/Unicode U+0061(category Ll: Letter, lowercase)
julia> Int('你')
20320
julia> Char(20320)
'你': Unicode U+4f60(category Lo: Letter, other)
julia> Int('?')
65311
julia> Int('?')
63
julia>
上面的代码倒数第二个是中文的问号,最后一个是英文的问号。
字符串可以使用切片功能,它类似于 Python 的可迭代对象的切片,稍有不同的是 Julia 中的切片是左闭右闭的,例如 str [2: 5] ,既包括索引为 2 的字符,也包括索引为 5 的字符,而 Python 中的 str [2: 5] 则包括索引为 2 的字符,但不包括索引为 5 的字符。
索引又称作 “下标” 。Julia 的索引是从 1 开始计数,Python 的索引是从 0 开始计数。
Julia 的切片不允许出现负数,不像 Python 中 -1 表示最后一个元素,Julia 使用 end 来表示最后一个元素:
julia> s = "HelloWorld"
"HelloWorld"
julia> s[2: 5]
"ello"
julia> s[3]
'l': ASCII/Unicode U+006c(category Ll: Letter, lowercase)
julia> s[end]
'd': ASCII/Unicode U+0064(category Ll: Letter, lowercase)
julia> s[6: end]
"World"
julia> s[end-2]
'r': ASCII/Unicode U+0072(category Ll: Letter, lowercase)
julia>
索引不可以出现越界,例如下面这两个:
julia> s[0]
ERROR: BoundsError: attempt to access String
at index [0]
Stacktrace:
[1] checkbounds at ./strings/basic.jl:193[inlined]
[2] codeunit at ./strings/string.jl:89[inlined]
[3] getindex(::String, ::Int64) at ./strings/string.jl:210
[4] top-level scope at REPL[24]:1
julia> s[end+1]
ERROR: BoundsError: attempt to access String
at index [11]
Stacktrace:
[1] checkbounds at ./strings/basic.jl:193[inlined]
[2] codeunit at ./strings/string.jl:89[inlined]
[3] getindex(::String, ::Int64) at ./strings/string.jl:210
[4] top-level scope at REPL[25]:1
julia>
此外需要注意,选择 1 个元素的方法有两个,它们的结果是不同的,注意引号的区别,单引号为字符,双引号为字符串:
julia> s[1]
'H': ASCII/Unicode U+0048(category Lu: Letter, uppercase)
julia> s[1: 1]
"H"
julia> typeof(s[1])
Char
julia> typeof(s[1: 1])
String
julia>
多行字符串可以使用三引号,注意是三个双引号:
julia> s = """大家好
今天天气不错噢"""
"大家好\n今天天气不错噢"
julia> s
"大家好\n今天天气不错噢"
julia> println(s)
大家好
今天天气不错噢
julia>
连接字符串可以使用 string
方法,将需要连接的字符串作为参数,注意不可以使用加号连接,否则会报错:
julia> a = "Hello"
"Hello"
julia> b = "Shiyanlou"
"Shiyanlou"
julia> s = string(a, " ", b)
"Hello Shiyanlou"
julia> a + b
ERROR: MethodError: no method matching +(::String, ::String)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:529
Stacktrace:
[1] top-level scope at REPL[37]:1
julia>
类似 Perl 一样, Julia 允许使用 $
来内插字符串文本,这与 JavaScrip 和 Python 的 f-String 格式化字符串的方法相似:
julia> name = "Tom"
"Tom"
julia> age = 12
12
julia> "Hello, I'm $(name), $(age) years old."
"Hello, I'm Tom, 12 years old."
julia> println("3 + 4 = $(3 + 4)")
3+ 4= 7
可以看到 Int 类型的数据会被自动转换为字符串填入其中,甚至可以在 \$ 内部进行计算。
字符串亦可比较大小,这与其它语言无异,都是按序比对字符的 ASCII 码或 Unicode 码:
julia> "abracadabra"< "xylophone"
true
julia> "abracadabra"== "xylophone"
false
julia> "Hello, world."!= "Goodbye, world."
true
julia> "1 + 2 = 3"== "1 + 2 = $(1 + 2)"
true
使用 length 函数查看字符串的长度:
julia> s
"Hello Shiyanlou"
julia> length(s)
15
使用 isdigit 函数查看字符是否是数字类型的,注意参数须是单引号表示的字符:
julia> isdigit('a')
false
julia> isdigit('2')
true
julia> isdigit('你')
false
总结
本节实验我们学习了 Julia 的变量命名规范和一些常见的数据类型:整数、浮点数、布尔值、复数、字符串。其中字符串的特点需要大家着重掌握,它与其它编程语言的差异要注意。
下一节实验,我们将学习 Julia 的元组、字典和集合。
????????????点击阅读原文,学习完整课程~