编码 · 编程语言
Julia 入门教程
简短回答
Julia 是一门高级动态类型语言,为快速的数值和科学计算而设计。它读起来像 Python 或 MATLAB,但每个函数在第一次被调用时就会编译成原生代码,因此循环的运行速度可以接近 C。它的核心思想是多重分派:函数根据所有参数的类型来选择方法。用 juliaup 安装,用 julia hello.jl 运行文件。
Julia 是什么,谁在用
Julia 由 Jeff Bezanson、Stefan Karpinski、Viral Shah 和 Alan Edelman 创建,2012 年公开。1.0 版本于 2018 年发布,1.x 系列保持向后兼容。Julia 旨在解决“双语言问题”:先用缓慢但简单的语言做原型,再用 C 或 Fortran 重写性能关键部分。
科学家、工程师和数据分析师用 Julia 做仿真、微分方程、优化、统计、机器学习和高性能计算。它的包生态涵盖数据框、绘图、GPU 计算和自动微分。
安装 Julia 并运行 Hello, world
推荐使用 juliaup 安装,它可以在 Windows、macOS 和 Linux 上安装并更新 Julia 版本(见 julialang.org/downloads)。安装之后,julia 启动 REPL,julia file.jl 运行脚本。
# hello.jl
println("Hello, world!")julia hello.jl
# Hello, world!
julia
# julia> 1 + 2
# 3在 REPL 中,按 ] 进入包模式(例如 add DataFrames),按 ? 查看某个名字的帮助,按 ; 进入 shell。按退格键回到普通提示符。
变量与基本类型
变量不需要声明,但每个值都有具体类型,可以用 typeof 查看。需要时,你可以给函数参数和结构体字段加上类型标注。
| 类型 | 示例 | 说明 |
|---|---|---|
| Int64 | 42 | 64 位系统上的默认整数;溢出会回绕 |
| Float64 | 3.14, 1e-3 | 默认的浮点数 |
| Rational | 1//3 | 精确分数 |
| String | "hello" | UTF-8;用 "$name" 插值 |
| Char | 'a' | 单个 Unicode 字符 |
| Bool | true, false | 逻辑值 |
| Symbol | :name | 一个驻留的名字,常用作键或选项 |
| Nothing | nothing | “没有值”,由没有结果的函数返回 |
name = "Ana"
age = 30
println("$name is $age, next year $(age + 1)")
# Ana is 30, next year 31
typeof(age) # Int64
7 / 2 # 3.5 (always a float)
7 ÷ 2 # 3 (integer division, also div(7, 2))
2^10 # 1024Julia 接受 π 或 α 这样的 Unicode 名字;在 REPL 和大多数编辑器中,输入 \pi 后按 Tab 即可得到 π。
控制流
代码块以关键字 end 结束;缩进只是为了便于阅读。if、for 和 while 的用法与大多数语言相同,推导式可以一行构建数组。
function sign_name(n)
if n > 0
"positive"
elseif n < 0
"negative"
else
"zero"
end
end
for i in 1:3
println("$i squared is $(i^2)")
end
squares = [x^2 for x in 1:5 if isodd(x)] # [1, 9, 25]
x = 5
result = x > 3 ? "big" : "small" # "big"1:3 是包含两端的区间。函数或 if 块的最后一个表达式就是它的值,所以 sign_name(-2) 不需要显式的 return 就会返回 "negative"。
函数
add(x, y) = x + y # one-line definition
function greet(name = "world"; punctuation = "!")
return "Hello, $name$punctuation"
end
greet() # "Hello, world!"
greet("Ana", punctuation = ".") # "Hello, Ana."
square = x -> x^2 # anonymous function
map(square, [1, 2, 3]) # [1, 4, 9]
v = [3, 1, 2]
sort!(v) # v is now [1, 2, 3]位置参数在分号之前,关键字参数在分号之后。按照惯例,名字以 ! 结尾的函数会修改它的某个参数,例如 sort! 或 push!,而 sort 返回新数组。
数组、元组与字典
| 结构 | 字面量 | 说明 |
|---|---|---|
| 向量 | [1, 2, 3] | 从 1 开始索引,可变,可用 push! 增长 |
| 矩阵 | [1 2; 3 4] | 空格分隔列,; 分隔行 |
| 元组 | (1, "a") | 不可变,长度固定 |
| 具名元组 | (name = "Ana", age = 30) | 用 .name 访问字段 |
| 字典 | Dict("a" => 1) | 可变的哈希映射 |
| 集合 | Set([1, 2, 2]) | 唯一值 |
v = [10, 20, 30]
v[1] # 10 (first element)
v[end] # 30
push!(v, 40)
A = [1 2; 3 4]
A * A # matrix product: [7 10; 15 22]
A .* A # element-wise: [1 4; 9 16]
v .+ 1 # broadcasting: [11, 21, 31, 41]
sqrt.([1, 4, 9]) # [1.0, 2.0, 3.0]点语法称为广播:f.(x) 把 f 应用到每个元素上,.+、.* 逐元素运算。Julia 会把一连串带点的运算融合成一个循环。
标志性特性:多重分派
一个 Julia 函数可以有许多方法。调用时,Julia 会根据所有参数的类型选择最具体的方法。这取代了类方法:类型保存数据,行为则通过为它们定义方法来添加,甚至可以在另一个包中添加。
abstract type Shape end
struct Circle <: Shape
r::Float64
end
struct Rect <: Shape
w::Float64
h::Float64
end
area(c::Circle) = π * c.r^2
area(r::Rect) = r.w * r.h
shapes = [Circle(1.0), Rect(2.0, 3.0)]
println(round.(area.(shapes), digits = 2)) # [3.14, 6.0]由于 Julia 会针对它见到的参数类型编译函数的专门版本,函数的第一次调用较慢(包含编译),之后的调用就很快。当字段在创建后需要改变时,请使用 mutable struct。
错误处理
Julia 使用异常。可以抛出 ArgumentError 或 DomainError 这样的内置异常类型,也可以自己定义一个继承 Exception 的结构体。tryparse 返回 nothing 而不是抛出异常。
function parse_age(s)
n = tryparse(Int, s)
n === nothing && throw(ArgumentError("not a number: $s"))
n < 0 && throw(DomainError(n, "age must not be negative"))
return n
end
try
parse_age("abc")
catch e
if e isa ArgumentError
println("Error: ", e.msg)
else
rethrow()
end
end
# Error: not a number: abc一个完整的小程序:词频统计
text = "the cat and the hat and the bat"
counts = Dict{String,Int}()
for w in split(text)
counts[w] = get(counts, w, 0) + 1
end
top = sort(collect(counts), by = last, rev = true)
for (word, n) in first(top, 2)
println(word, " ", n)
end
# the 3
# and 2split 按空白字符拆分字符串,get 为新单词返回默认值 0,collect 把字典转成 word => count 对的向量,by = last 则按次数排序。
接下来读什么
- docs.julialang.org 上的官方 Julia 手册,从“Getting Started”开始。
- julialang.org 的学习页面,列出了教程、书籍和课程。
- 优化代码之前,先读手册中的“Performance Tips”一章。
- 了解 DataFrames.jl、Plots.jl 和 DifferentialEquations.jl 等包。
常见问题
Julia 比 Python 快吗?
对于循环多、数值运算多的代码,Julia 通常比纯 Python 快得多,因为它编译为原生代码。Python 配合 NumPy 等库,做向量化运算也可以很快,所以差异取决于具体任务。
为什么 Julia 从 1 开始计数?
Julia 遵循数学以及 MATLAB、R、Fortran 等语言的惯例,数组的第一个元素索引为 1。
为什么我的 Julia 代码第一次运行很慢?
Julia 在第一次调用时,会针对参数类型编译每个函数。之后的调用复用编译好的代码,所以请在预热调用之后再测量性能,或使用基准测试包。
Julia 是免费的吗?
是。Julia 以 MIT 许可证开源,几乎所有的包也都是开源的。