Last active
August 10, 2022 10:03
-
-
Save inkydragon/115c6e189500f49bc6634c841953773a to your computer and use it in GitHub Desktop.
Learn Julia in Y minutes 社区翻译版。讨论帖:http://discourse.juliacn.com/t/topic/611
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 单行注释由「#」号开始 | |
#= 以「#=」开头,用「=#」结尾就可以得到多行注释 | |
多行注释可以嵌套。 | |
=# | |
# VSC 代码高亮 patch' | |
#################################################### | |
## 1. 原始类型与操作符 | |
#################################################### | |
# Julia 中一切皆为表达式 | |
# 以下是几种常见的数值类型 | |
typeof(3) # => Int64 | |
typeof(3.2) # => Float64 | |
typeof(2 + 1im) # => Complex{Int64} | |
typeof(2 // 3) # => Rational{Int64} | |
# 常见的中缀操作符 | |
1 + 1 # => 2 | |
8 - 1 # => 7 | |
10 * 2 # => 20 | |
35 / 5 # => 7.0 | |
10 / 2 # => 5.0 # 整数除法总是返回浮点数 | |
div(5, 2) # => 2 # 使用 div 可以获得整除的结果 | |
5 \ 35 # => 7.0 | |
2^2 # => 4 # 幂运算,不是异或 (xor) | |
12 % 10 # => 2 | |
# 加括号可以调整计算顺序 | |
(1 + 3) * 2 # => 8 | |
# 位运算操作符 | |
~2 # => -3 # 按位非 (not) | |
3 & 5 # => 1 # 按位与 (and) | |
2 | 4 # => 6 # 按位或 (or) | |
xor(2, 4) # => 6 # 按位异或 (xor) | |
2 >>> 1 # => 1 # 逻辑右移 | |
2 >> 1 # => 1 # 算术右移 | |
2 << 1 # => 4 # 逻辑/算术左移 | |
# 使用 bitstring 函数可以查看一个数的二进制表述 | |
bitstring(12345) | |
# => "0000000000000000000000000000000000000000000000000011000000111001" | |
bitstring(12345.0) | |
# => "0100000011001000000111001000000000000000000000000000000000000000" | |
# 布尔值也是一种原始类型 | |
true | |
false | |
# 布尔运算符 | |
!true # => false | |
!false # => true | |
1 == 1 # => true | |
2 == 1 # => false | |
1 != 1 # => false | |
2 != 1 # => true | |
1 < 10 # => true | |
1 > 10 # => false | |
2 <= 2 # => true | |
2 >= 2 # => true | |
# 链式比较 | |
1 < 2 < 3 # => true | |
2 < 3 < 2 # => false | |
# 可以通过双引号「"」构建字符串 | |
"This is a string." | |
# 单引号「'」用来构建字符字面量 | |
'a' | |
# 字符串使用 UTF-8 编码 | |
# 可以像取数组取值一样用 index 取出字符串中的对应字符 | |
# 但只有在字符串仅由 ASCII 字符构成时,字符串才能够被安全的引索 | |
ascii("This is a string")[1] | |
# => 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase) | |
# Julia 的引索值从 1 开始计数 | |
# 另外,推荐使用迭代来处理字符串 (map, for loops 等) | |
# $ 用于插入字符串 | |
"2 + 2 = $(2 + 2)" # => "2 + 2 = 4" | |
# 你可以在括号中放入任何 Julia 表达式 | |
# 另一种输出格式化字符串的方法是使用标准库 Printf 中的 Printf 宏 | |
using Printf | |
@printf "%d is less than %f\n" 4.5 5.3 # => 5 is less than 5.300000 | |
# 输出字符串很容易 | |
println("I'm Julia. Nice to meet you!") # => I'm Julia. Nice to meet you! | |
# 字符串可以按字典序进行比较 | |
"good" > "bye" # => true | |
"good" == "good" # => true | |
"1 + 2 = 3" == "1 + 2 = $(1 + 2)" # => true | |
#################################################### | |
## 2. 变量与集合 | |
#################################################### | |
# 使用变量之前无需声明 | |
some_var = 5 # => 5 | |
some_var # => 5 | |
# 访问未赋值的变量会导致一个错误 | |
try | |
some_other_var # => ERROR: UndefVarError: some_other_var not defined | |
catch e | |
println(e) | |
end | |
# 变量名必须以下划线或字母开头 | |
# 之后使用字母、数字、下划线以及感叹号都是合法的 | |
SomeOtherVar123! = 6 # => 6 | |
# 你也可以使用特定的 Unicode 字符 | |
☃ = 8 # => 8 | |
# 这样使用数学记号就十分方便 | |
2 * π # => 6.283185307179586 | |
# Julia 的命名规约: | |
# | |
# * 名称可以用下划线「_」分割。 | |
# 不过一般不推荐使用下划线,除非不用变量名就会变得难于理解 | |
# | |
# * 类型名应以大写字母开头,单词间用驼峰命名法分隔,不使用下划线。 | |
# | |
# * 函数和宏的名称小写,不用下划线。 | |
# | |
# * 会改变输入的函数名字末尾加感叹号「!」。 | |
# 这些函数有时称为 mutating functions 或 in-place functions | |
# | |
# 数组储存着一系列值,用 1 到 n 的整数作为引索 | |
a = Int64[] # => 0-element Array{Int64,1} | |
# 一维数组可以写成由逗号分割值的形式 | |
b = [4, 5, 6] # => 3-element Array{Int64,1}: [4, 5, 6] | |
b = [4; 5; 6] # => 3-element Array{Int64,1}: [4, 5, 6] | |
b[1] # => 4 | |
b[end] # => 6 | |
# 二维数组用空格分割值,分号分割行 | |
matrix = [1 2; 3 4] # => 2×2 Array{Int64,2}: [1 2; 3 4] | |
# 指定数组的类型 | |
b = Int8[4, 5, 6] # => 3-element Array{Int8,1}: [4, 5, 6] | |
# 使用 push! 和 append! 向数组尾部添加元素 | |
push!(a, 1) # => [1] | |
push!(a, 2) # => [1,2] | |
push!(a, 4) # => [1,2,4] | |
push!(a, 3) # => [1,2,4,3] | |
append!(a, b) # => [1,2,4,3,4,5,6] | |
# 用 pop 弹出尾部的元素 | |
pop!(b) # => 6 | |
b # => [4,5] | |
# 再放回去 | |
push!(b, 6) # => [4,5,6] | |
b # => [4,5,6] | |
a[1] # => 1 # 永远记住 Julia 的引索从 1 开始!而不是 0! | |
# end 是数组最后一个引索的简称,它可以用在任何索引表达式中 | |
a[end] # => 6 | |
# 数组还支持 popfirst! 和 pushfirst! | |
popfirst!(a) # => 1 | |
a # => [2,4,3,4,5,6] | |
pushfirst!(a, 7) # => [7,2,4,3,4,5,6] | |
a # => [7,2,4,3,4,5,6] | |
# 以叹号结尾的函数名表示它会改变参数的值 | |
arr = [5,4,6] # => 3-element Array{Int64,1}: [5,4,6] | |
sort(arr) # => [4,5,6] | |
arr # => [5,4,6] | |
sort!(arr) # => [4,5,6] | |
arr # => [4,5,6] | |
# 数组越界会抛出 BoundsError | |
try | |
a[0] | |
# => ERROR: BoundsError: attempt to access 7-element Array{Int64,1} at | |
# index [0] | |
# => Stacktrace: | |
# => [1] getindex(::Array{Int64,1}, ::Int64) at .\array.jl:731 | |
# => [2] top-level scope at none:0 | |
# => [3] ... | |
# => in expression starting at ...\LearnJulia.jl:188 | |
a[end + 1] | |
# => ERROR: BoundsError: attempt to access 7-element Array{Int64,1} at | |
# index [8] | |
# => Stacktrace: | |
# => [1] getindex(::Array{Int64,1}, ::Int64) at .\array.jl:731 | |
# => [2] top-level scope at none:0 | |
# => [3] ... | |
# => in expression starting at ...\LearnJulia.jl:196 | |
catch e | |
println(e) | |
end | |
# 报错时错误会指出出错的文件位置以及行号,标准库也一样 | |
# 你可以在 Julia 安装目录下的 share/julia 文件夹里找到这些标准库 | |
# 可以用 range 初始化数组 | |
a = [1:5;] # => 5-element Array{Int64,1}: [1,2,3,4,5] | |
# 注意!分号不可省略 | |
a2 = [1:5] # => 1-element Array{UnitRange{Int64},1}: [1:5] | |
# 可以用切片的语法查看数组 | |
a[1:3] # => [1, 2, 3] | |
a[2:end] # => [2, 3, 4, 5] | |
# splice! 可以移除指定位置的元素 | |
arr = [3,4,5] | |
splice!(arr, 2) # => 4 | |
arr # => [3,5] | |
# append! 用于连接两个数组 | |
b = [1,2,3] | |
append!(a, b) # => [1, 2, 3, 4, 5, 1, 2, 3] | |
a # => [1, 2, 3, 4, 5, 1, 2, 3] | |
# 检查元素是否在数组中 | |
in(1, a) # => true | |
# length 用于获取数组长度 | |
length(a) # => 8 | |
# 元组(Tuples)是不可变的 | |
tup = (1, 2, 3) # => (1,2,3) | |
typeof(tup) # => Tuple{Int64,Int64,Int64} | |
tup[1] # => 1 | |
try | |
tup[1] = 3 | |
# => ERROR: MethodError: no method matching | |
# setindex!(::Tuple{Int64,Int64,Int64}, ::Int64, ::Int64) | |
catch e | |
println(e) | |
end | |
# 大多数组的函数同样支持元组 | |
length(tup) # => 3 | |
tup[1:2] # => (1,2) | |
in(2, tup) # => true | |
# 元组可以解包为变量 | |
a, b, c = (1, 2, 3) # => (1,2,3) | |
a # => 1 | |
b # => 2 | |
c # => 3 | |
# 不用括号也会创建一个元组,然后解包 | |
d, e, f = 4, 5, 6 # => (4,5,6) | |
d # => 4 | |
e # => 5 | |
f # => 6 | |
# 单元素的元组不等于它包含的值 | |
(1,) == 1 # => false | |
(1) == 1 # => true | |
# 交换两个值也十分简单 | |
e, d = d, e # => (5,4) | |
d # => 5 | |
e # => 4 | |
# 字典用于储存映射(mappings)(键值对) | |
empty_dict = Dict() # => Dict{Any,Any} with 0 entries | |
# 可以用字面量创建字典 | |
filled_dict = Dict("one" => 1, "two" => 2, "three" => 3) | |
# => Dict{String,Int64} with 3 entries: | |
# => "two" => 2, "one" => 1, "three" => 3 | |
# 用 [] 取得对应键的值 | |
filled_dict["one"] # => 1 | |
# 获得所有键 | |
keys(filled_dict) | |
# => Base.KeySet for a Dict{String,Int64} with 3 entries. Keys: | |
# => "two", "one", "three" | |
# 注意!键的顺序不是插入时的顺序 | |
# 获得所有值 | |
values(filled_dict) | |
# => Base.ValueIterator for a Dict{String,Int64} with 3 entries. Values: | |
# => 2, 1, 3 | |
# 注意!值的顺序也一样 | |
# in 检查键值对是否存在;haskey 检查键是否存在 | |
in(("one" => 1), filled_dict) # => true | |
in(("two" => 3), filled_dict) # => false | |
haskey(filled_dict, "one") # => true | |
haskey(filled_dict, 1) # => false | |
# 获取不存在的键的值会抛出异常 | |
try | |
filled_dict["four"] # => ERROR: KeyError: key "four" not found | |
catch e | |
println(e) | |
end | |
# get 方法可以提供默认值以避免异常 | |
# get(dictionary, key, default_value) | |
get(filled_dict, "one", 4) # => 1 | |
get(filled_dict, "four", 4) # => 4 | |
# Set 表示无序不可重复的值的集合 | |
empty_set = Set() # => Set(Any[]) | |
# 初始化一个带初值的 Set | |
filled_set = Set([1, 2, 2, 3, 4]) # => Set([4, 2, 3, 1]) | |
# 新增值 | |
push!(filled_set, 5) # => Set([4, 2, 3, 5, 1]) | |
# 检查 Set 中是否存在某值 | |
in(2, filled_set) # => true | |
in(10, filled_set) # => false | |
# 求 Set 的交集、并集和差集 | |
other_set = Set([3, 4, 5, 6]) # => Set([4, 3, 5, 6]) | |
intersect(filled_set, other_set) # => Set([4, 3, 5]) | |
union(filled_set, other_set) # => Set([4, 2, 3, 5, 6, 1]) | |
setdiff(Set([1,2,3,4]), Set([2,3,5])) # => Set([4, 1]) | |
#################################################### | |
## 3. 控制语句 | |
#################################################### | |
# 先声明一个变量 | |
some_var = 5 | |
# 这是一个 if 语句块 | |
# Julia 中的缩进只是为了美观 | |
if some_var > 10 | |
println("some_var is totally bigger than 10.") | |
elseif some_var < 10 # elseif 是可选的 | |
println("some_var is smaller than 10.") | |
else # else 也是可选的 | |
println("some_var is indeed 10.") | |
end | |
# => some_var is smaller than 10. | |
# For 循环用于迭代 | |
# 可迭代的类型包括:Range, Array, Set, Dict 和 AbstractString | |
for animal = ["dog", "cat", "mouse"] | |
println("$animal is a mammal") | |
# 你可以用 $ 将变量或表达式插入字符串中 | |
end | |
# => dog is a mammal | |
# => cat is a mammal | |
# => mouse is a mammal | |
# 你也可以不用「=」而使用「in」 | |
for animal in ["dog", "cat", "mouse"] | |
println("$animal is a mammal") | |
end | |
# => dog is a mammal | |
# => cat is a mammal | |
# => mouse is a mammal | |
for pair in Dict("dog" => "mammal", "cat" => "mammal", "mouse" => "mammal") | |
from, to = pair | |
println("$from is a $to") | |
end | |
# => mouse is a mammal | |
# => cat is a mammal | |
# => dog is a mammal | |
# 注意!这里的输出顺序和上面的不同 | |
for (k, v) in Dict("dog" => "mammal", "cat" => "mammal", "mouse" => "mammal") | |
println("$k is a $v") | |
end | |
# => mouse is a mammal | |
# => cat is a mammal | |
# => dog is a mammal | |
# While 循环 | |
let x = 0 | |
while x < 4 | |
println(x) | |
x += 1 # x = x + 1 的缩写 | |
end | |
end | |
# => 0 | |
# => 1 | |
# => 2 | |
# => 3 | |
# 使用 try/catch 处理异常 | |
try | |
error("help") | |
catch e | |
println("caught it $e") | |
end | |
# => caught it ErrorException("help") | |
#################################################### | |
## 4. 函数 | |
#################################################### | |
# 关键字 function 用于定义函数 | |
# function name(arglist) | |
# body... | |
# end | |
function add(x, y) | |
println("x is $x and y is $y") | |
# 函数会返回最后一行的值 | |
x + y | |
end | |
add(5, 6) | |
# => x is 5 and y is 6 | |
# => 11 | |
# 更紧凑的定义函数 | |
f_add(x, y) = x + y # => f_add (generic function with 1 method) | |
f_add(3, 4) # => 7 | |
# 函数可以将多个值作为元组返回 | |
fn(x, y) = x + y, x - y # => fn (generic function with 1 method) | |
fn(3, 4) # => (7, -1) | |
# 还可以定义接收可变长参数的函数 | |
function varargs(args...) | |
return args | |
# 使用 return 可以在函数内的任何地方返回 | |
end | |
# => varargs (generic function with 1 method) | |
varargs(1, 2, 3) # => (1,2,3) | |
# 省略号「...」称为 splat | |
# 我们刚刚在定义函数时使用了它 | |
# 在调用函数时也可以使用它,此时它会把数组或元组解包为参数列表 | |
add([5,6]...) # 等价于 add(5,6) | |
x = (5, 6) # => (5,6) | |
add(x...) # 等价于 add(5,6) | |
# 也能定义带可选参数的函数 | |
function defaults(a, b, x=5, y=6) | |
return "$a $b and $x $y" | |
end | |
# => defaults (generic function with 3 methods) | |
defaults('h', 'g') # => "h g and 5 6" | |
defaults('h', 'g', 'j') # => "h g and j 6" | |
defaults('h', 'g', 'j', 'k') # => "h g and j k" | |
try | |
defaults('h') # => ERROR: MethodError: no method matching defaults(::Char) | |
defaults() # => ERROR: MethodError: no method matching defaults() | |
catch e | |
println(e) | |
end | |
# 还可以定义带关键字参数的函数 | |
function keyword_args(;k1=4, name2="hello") # 注意分号 ';' | |
return Dict("k1" => k1, "name2" => name2) | |
end | |
# => keyword_args (generic function with 1 method) | |
keyword_args(name2="ness") # => ["name2"=>"ness", "k1"=>4] | |
keyword_args(k1="mine") # => ["name2"=>"hello", "k1"=>"mine"] | |
keyword_args() # => ["name2"=>"hello", "k1"=>4] | |
# 可以在一个函数中组合各种类型的参数 | |
function all_the_args(normal_arg, optional_positional_arg=2; keyword_arg="foo") | |
println("normal arg: $normal_arg") | |
println("optional arg: $optional_positional_arg") | |
println("keyword arg: $keyword_arg") | |
end | |
# => all_the_args (generic function with 2 methods) | |
all_the_args(1, 3, keyword_arg=4) | |
# => normal arg: 1 | |
# => optional arg: 3 | |
# => keyword arg: 4 | |
# Julia 有一等函数 | |
function create_adder(x) | |
adder = function (y) | |
return x + y | |
end | |
return adder | |
end | |
# => create_adder (generic function with 1 method) | |
# 短 λ 表达式(stabby lambda syntax) 可以用来创建匿名函数 | |
(x -> x > 2)(3) # => true | |
# 这个函数和上面那种实现时等价的 | |
function create_adder(x) | |
y -> x + y | |
end | |
# => create_adder (generic function with 1 method) | |
# 你也可以给内部函数起个名字 | |
function create_adder(x) | |
function adder(y) | |
x + y | |
end | |
adder | |
end | |
# => create_adder (generic function with 1 method) | |
add_10 = create_adder(10) # => (::getfield(Main, Symbol("#adder#11")){Int64}) | |
# (generic function with 1 method) | |
add_10(3) # => 13 | |
# 内置的高阶函数有 | |
map(add_10, [1,2,3]) # => [11, 12, 13] | |
filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7] | |
# 还可以使用 list comprehensions 让 map 更美观 | |
[add_10(i) for i = [1, 2, 3]] # => [11, 12, 13] | |
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] | |
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] | |
#################################################### | |
## 5. 类型 | |
#################################################### | |
# Julia 有一个类型系统 | |
# 所有的值都有类型;但变量本身没有类型 | |
# 你可以用 `typeof` 函数获得值的类型 | |
typeof(5) # => Int64 | |
# 类型是一等值 | |
typeof(Int64) # => DataType | |
typeof(DataType) # => DataType | |
# DataType 是代表类型的类型,也代表它自己的类型 | |
# 类型可用于文档化代码、执行优化以及多重派分(dispatch) | |
# Julia 并不只是静态的检查类型 | |
# 用户可以自定义类型 | |
# 就跟其他语言的 record 或 struct 一样 | |
# 用 `struct` 关键字定义新的类型 | |
# struct Name | |
# field::OptionalType | |
# ... | |
# end | |
struct Tiger | |
taillength::Float64 | |
coatcolor # 不带类型标注相当于 `::Any` | |
end | |
# 默认构造函数的参数是类型的属性,按类型定义中的顺序排列 | |
tigger = Tiger(3.5, "orange") # => Tiger(3.5, "orange") | |
# 对于该类型的值,该类型充当构造函数 | |
sherekhan = typeof(tigger)(5.6, "fire") # => Tiger(5.6, "fire") | |
# 类似 struct 的类型被称为具体类型 | |
# 它们可被实例化,但不能有子类型 | |
# 另一种类型是抽象类型 | |
# 抽象类型名 | |
abstract type Cat end # 仅仅是指向类型结构层次的一个名称 | |
# 抽象类型不能被实例化,但可以有子类型 | |
# 例如,Number 就是一个抽象类型 | |
subtypes(Number) # => 2-element Array{Any,1}: | |
# => Complex | |
# => Real | |
subtypes(Cat) # => 0-element Array{Any,1} | |
# AbstractString 类如其名,也是一个抽象类型 | |
subtypes(AbstractString) # => 4-element Array{Any,1}: | |
# => String | |
# => SubString | |
# => SubstitutionString | |
# => Test.GenericString | |
# 所有的类型都有父类型。可以用函数 `supertype` 得到父类型 | |
typeof(5) # => Int64 | |
supertype(Int64) # => Signed | |
supertype(Signed) # => Integer | |
supertype(Integer) # => Real | |
supertype(Real) # => Number | |
supertype(Number) # => Any | |
supertype(supertype(Signed)) # => Real | |
supertype(Any) # => Any | |
# 除了 Int64 外,其余的类型都是抽象类型 | |
typeof("fire") # => String | |
supertype(String) # => AbstractString | |
supertype(AbstractString) # => Any | |
supertype(SubString) # => AbstractString | |
# <: 是子类型化操作符 | |
struct Lion <: Cat # Lion 是 Cat 的子类型 | |
mane_color | |
roar::AbstractString | |
end | |
# 可以继续为你的类型定义构造函数 | |
# 只需要定义一个与类型同名的函数,并调用已有的构造函数得到正确的类型 | |
Lion(roar::AbstractString) = Lion("green", roar) # => Lion | |
# 这是一个外部构造函数,因为它在类型定义之外 | |
struct Panther <: Cat # Panther 也是 Cat 的子类型 | |
eye_color | |
Panther() = new("green") | |
# Panthers 只有这个构造函数,没有默认构造函数 | |
end | |
# 像 Panther 一样使用内置构造函数,让你可以控制如何构造类型的值 | |
# 应该尽量使用外部构造函数,而不是内部构造函数 | |
#################################################### | |
## 6. 多重派分(Multiple-Dispatch) | |
#################################################### | |
# Julia 中所有的函数都是通用函数,或者叫做泛型函数(generic functions) | |
# 也就是说这些函数都是由许多小方法组合而成的 | |
# Lion 的每一种构造函数都是通用函数 Lion 的一个方法 | |
# 我们来看一个非构造函数的例子 | |
# 首先,让我们定义一个函数 meow | |
# Lion, Panther, Tiger 的 meow 定义分别为 | |
function meow(animal::Lion) | |
animal.roar # 使用点记号「.」访问属性 | |
end | |
# => meow (generic function with 1 method) | |
function meow(animal::Panther) | |
"grrr" | |
end | |
# => meow (generic function with 2 methods) | |
function meow(animal::Tiger) | |
"rawwwr" | |
end | |
# => meow (generic function with 3 methods) | |
# 测试 meow 函数 | |
meow(tigger) # => "rawwwr" | |
meow(Lion("brown", "ROAAR")) # => "ROAAR" | |
meow(Panther()) # => "grrr" | |
# 回顾类型的层次结构 | |
Tiger <: Cat # => false | |
Lion <: Cat # => true | |
Panther <: Cat # => true | |
# 定义一个接收 Cat 类型的函数 | |
function pet_cat(cat::Cat) | |
println("The cat says $(meow(cat))") | |
end | |
# => pet_cat (generic function with 1 method) | |
pet_cat(Lion("42")) # => The cat says 42 | |
try | |
pet_cat(tigger) # => ERROR: MethodError: no method matching pet_cat(::Tiger) | |
catch e | |
println(e) | |
end | |
# 在面向对象语言中,通常都是单分派 | |
# 这意味着使用的方法取决于第一个参数的类型 | |
# 而 Julia 中选择方法时会考虑到所有参数的类型 | |
# 让我们定义一个有更多参数的函数,这样我们就能看出区别 | |
function fight(t::Tiger, c::Cat) | |
println("The $(t.coatcolor) tiger wins!") | |
end | |
# => fight (generic function with 1 method) | |
fight(tigger, Panther()) # => The orange tiger wins! | |
fight(tigger, Lion("ROAR")) # => The orange tiger wins! | |
# 让我们修改一下传入 Lion 类型时的行为 | |
fight(t::Tiger, l::Lion) = println("The $(l.mane_color)-maned lion wins!") | |
# => fight (generic function with 2 methods) | |
fight(tigger, Panther()) # => The orange tiger wins! | |
fight(tigger, Lion("ROAR")) # => The green-maned lion wins! | |
# 我们不需要一只老虎参与战斗 | |
fight(l::Lion, c::Cat) = println("The victorious cat says $(meow(c))") | |
# => fight (generic function with 3 methods) | |
fight(Lion("balooga!"), Panther()) # => The victorious cat says grrr | |
try | |
fight(Panther(), Lion("RAWR")) | |
# => ERROR: MethodError: no method matching fight(::Panther, ::Lion) | |
# => Closest candidates are: | |
# => fight(::Tiger, ::Lion) at ... | |
# => fight(::Tiger, ::Cat) at ... | |
# => fight(::Lion, ::Cat) at ... | |
# => ... | |
catch e | |
println(e) | |
end | |
# 试试把 Cat 放在前面 | |
fight(c::Cat, l::Lion) = println("The cat beats the Lion") | |
# => fight (generic function with 4 methods) | |
# 由于无法判断该使用哪个 fight 方法,而产生了错误 | |
try | |
fight(Lion("RAR"), Lion("brown", "rarrr")) | |
# => ERROR: MethodError: fight(::Lion, ::Lion) is ambiguous. Candidates: | |
# => fight(c::Cat, l::Lion) in Main at ... | |
# => fight(l::Lion, c::Cat) in Main at ... | |
# => Possible fix, define | |
# => fight(::Lion, ::Lion) | |
# => ... | |
catch e | |
println(e) | |
end | |
# 在不同版本的 Julia 中错误信息可能有所不同 | |
fight(l::Lion, l2::Lion) = println("The lions come to a tie") | |
# => fight (generic function with 5 methods) | |
fight(Lion("RAR"), Lion("brown", "rarrr")) # => The lions come to a tie | |
# 深入编译器之后 | |
# 你可以看看 llvm 以及它生成的汇编代码 | |
square_area(l) = l * l # => square_area (generic function with 1 method) | |
square_area(5) # => 25 | |
# 当我们喂给 square_area 一个整数时会发生什么? | |
code_native(square_area, (Int32,), syntax = :intel) | |
# .text | |
# ; Function square_area { | |
# ; Location: REPL[116]:1 # 函数序言 (Prologue) | |
# push rbp | |
# mov rbp, rsp | |
# ; Function *; { | |
# ; Location: int.jl:54 | |
# imul ecx, ecx # 求 l 的平方,并把结果放在 ECX 中 | |
# ;} | |
# mov eax, ecx | |
# pop rbp # 还原旧的基址指针(base pointer) | |
# ret # 返回值放在 EAX 中 | |
# nop dword ptr [rax + rax] | |
# ;} | |
# 使用 syntax 参数指定输出语法。默认为 AT&T 格式,这里指定为 Intel 格式 | |
code_native(square_area, (Float32,), syntax = :intel) | |
# .text | |
# ; Function square_area { | |
# ; Location: REPL[116]:1 | |
# push rbp | |
# mov rbp, rsp | |
# ; Function *; { | |
# ; Location: float.jl:398 | |
# vmulss xmm0, xmm0, xmm0 # 标量双精度乘法 (AVX) | |
# ;} | |
# pop rbp | |
# ret | |
# nop word ptr [rax + rax] | |
# ;} | |
code_native(square_area, (Float64,), syntax = :intel) | |
# .text | |
# ; Function square_area { | |
# ; Location: REPL[116]:1 | |
# push rbp | |
# mov rbp, rsp | |
# ; Function *; { | |
# ; Location: float.jl:399 | |
# vmulsd xmm0, xmm0, xmm0 # 标量双精度乘法 (AVX) | |
# ;} | |
# pop rbp | |
# ret | |
# nop word ptr [rax + rax] | |
# ;} | |
# 注意!只要参数中有浮点数,Julia 就会使用浮点指令 | |
# 让我们计算一下圆的面积 | |
circle_area(r) = pi * r * r # => circle_area (generic function with 1 method) | |
circle_area(5) # => 78.53981633974483 | |
code_native(circle_area, (Int32,), syntax = :intel) | |
# .text | |
# ; Function circle_area { | |
# ; Location: REPL[121]:1 | |
# push rbp | |
# mov rbp, rsp | |
# ; Function *; { | |
# ; Location: operators.jl:502 | |
# ; Function *; { | |
# ; Location: promotion.jl:314 | |
# ; Function promote; { | |
# ; Location: promotion.jl:284 | |
# ; Function _promote; { | |
# ; Location: promotion.jl:261 | |
# ; Function convert; { | |
# ; Location: number.jl:7 | |
# ; Function Type; { | |
# ; Location: float.jl:60 | |
# vcvtsi2sd xmm0, xmm0, ecx # 从内存中读取整数 r | |
# movabs rax, 497710928 # 读取 pi | |
# ;}}}}} | |
# ; Function *; { | |
# ; Location: float.jl:399 | |
# vmulsd xmm1, xmm0, qword ptr [rax] # pi * r | |
# vmulsd xmm0, xmm1, xmm0 # (pi * r) * r | |
# ;}} | |
# pop rbp | |
# ret | |
# nop dword ptr [rax] | |
# ;} | |
code_native(circle_area, (Float64,), syntax = :intel) | |
# .text | |
# ; Function circle_area { | |
# ; Location: REPL[121]:1 | |
# push rbp | |
# mov rbp, rsp | |
# movabs rax, 497711048 | |
# ; Function *; { | |
# ; Location: operators.jl:502 | |
# ; Function *; { | |
# ; Location: promotion.jl:314 | |
# ; Function *; { | |
# ; Location: float.jl:399 | |
# vmulsd xmm1, xmm0, qword ptr [rax] | |
# ;}}} | |
# ; Function *; { | |
# ; Location: float.jl:399 | |
# vmulsd xmm0, xmm1, xmm0 | |
# ;} | |
# pop rbp | |
# ret | |
# nop dword ptr [rax + rax] | |
# ;} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment