2017/08/22 @JuliaTokyo #7 (https://juliatokyo.connpass.com/event/62233/)
概要
目的
科学技術計算を目的として作られた動的プログラミング言語
for
ループが超速いfunction pca(X)
X̄ = X .- mean(X, 1)
U, Σ, V = svd(X̄)
return X̄ * V
end
pca (generic function with 1 method)
iris = readcsv("iris.csv", header=true)[1]
X = convert(Matrix{Float64}, iris[:,1:4])
150×4 Array{Float64,2}: 5.1 3.5 1.4 0.2 4.9 3.0 1.4 0.2 4.7 3.2 1.3 0.2 4.6 3.1 1.5 0.2 5.0 3.6 1.4 0.2 5.4 3.9 1.7 0.4 4.6 3.4 1.4 0.3 5.0 3.4 1.5 0.2 4.4 2.9 1.4 0.2 4.9 3.1 1.5 0.1 5.4 3.7 1.5 0.2 4.8 3.4 1.6 0.2 4.8 3.0 1.4 0.1 ⋮ 6.0 3.0 4.8 1.8 6.9 3.1 5.4 2.1 6.7 3.1 5.6 2.4 6.9 3.1 5.1 2.3 5.8 2.7 5.1 1.9 6.8 3.2 5.9 2.3 6.7 3.3 5.7 2.5 6.7 3.0 5.2 2.3 6.3 2.5 5.0 1.9 6.5 3.0 5.2 2.0 6.2 3.4 5.4 2.3 5.9 3.0 5.1 1.8
X_pca = pca(X)
150×4 Array{Float64,2}: -2.68413 -0.319397 0.0279148 0.00226244 -2.71414 0.177001 0.210464 0.0990266 -2.88899 0.144949 -0.0179003 0.0199684 -2.74534 0.318299 -0.0315594 -0.0755758 -2.72872 -0.326755 -0.0900792 -0.0612586 -2.28086 -0.74133 -0.168678 -0.0242009 -2.82054 0.0894614 -0.257892 -0.0481431 -2.62614 -0.163385 0.0218793 -0.0452979 -2.88638 0.578312 -0.0207596 -0.0267447 -2.67276 0.113774 0.197633 -0.0562954 -2.50695 -0.645069 0.075318 -0.0150199 -2.61276 -0.0147299 -0.10215 -0.156379 -2.78611 0.235112 0.206844 -0.00788791 ⋮ 1.16933 0.16499 -0.281836 0.0204618 2.10761 -0.372288 -0.0272911 0.210622 2.31415 -0.183651 -0.322694 0.277654 1.92227 -0.409203 -0.113587 0.505305 1.41524 0.574916 -0.296323 -0.0153047 2.56301 -0.277863 -0.29257 0.0579127 2.41875 -0.304798 -0.504483 0.241091 1.94411 -0.187532 -0.177825 0.426196 1.52717 0.375317 0.121898 0.254367 1.76435 -0.0788589 -0.130482 0.137001 1.90094 -0.116628 -0.723252 0.0445953 1.39019 0.282661 -0.36291 -0.155039
using PyPlot
species = iris[:,5]
cmap = Dict(s => "C$(i-1)" for (i, s) in enumerate(unique(species)))
scatter(X_pca[:,1], X_pca[:,2], c=getindex.(cmap, species));
多次元配列を標準搭載
高速な数値計算ライブラリを標準搭載
srand(1234);
rand(3) # 1次元配列
3-element Array{Float64,1}: 0.590845 0.766797 0.566237
rand(3, 3) # 2次元配列
3×3 Array{Float64,2}: 0.460085 0.200586 0.579672 0.794026 0.298614 0.648882 0.854147 0.246837 0.0109059
rand(3, 3, 3) # 3次元配列
3×3×3 Array{Float64,3}: [:, :, 1] = 0.066423 0.112486 0.0566425 0.956753 0.276021 0.842714 0.646691 0.651664 0.950498 [:, :, 2] = 0.96467 0.82116 0.314926 0.945775 0.0341601 0.12781 0.789904 0.0945445 0.374187 [:, :, 3] = 0.931115 0.0118196 0.732 0.438939 0.0460428 0.299058 0.246862 0.496169 0.449182
A = randn(4, 2)
B = randn(2, 3)
A * B # 行列積
4×3 Array{Float64,2}: 0.9481 1.22519 1.05156 1.40751 1.71371 0.97805 0.561272 0.733818 0.669705 0.0696456 0.112251 0.200629
rank(A * B)
2
U, Σ, V = svd(A * B); # 特異値分解
U
4×3 Array{Float64,2}: -0.571725 0.532279 0.619488 -0.740511 -0.643106 -0.146947 -0.347219 0.4311 -0.621254 -0.0649046 0.342412 -0.45682
Σ
3-element Array{Float64,1}: 3.26033 0.332009 2.64666e-16
V
3×3 Array{Float64,2}: -0.547103 -0.405747 0.732153 -0.684464 -0.286656 -0.670326 -0.481859 0.86787 0.120889
欧米を中心に、数値計算の授業でもJuliaが使われている: https://julialang.org/teaching/
function add(x, y)
output = similar(x)
@inbounds for i in 1:endof(x)
output[i] = x[i] + y[i]
end
return output
end
add (generic function with 1 method)
x = randn(100_000);
y = randn(100_000);
x + y
for _ in 1:5
@time x + y
end
0.008612 seconds (2 allocations: 781.328 KiB, 92.76% gc time) 0.000611 seconds (2 allocations: 781.328 KiB) 0.000707 seconds (2 allocations: 781.328 KiB) 0.000664 seconds (2 allocations: 781.328 KiB) 0.000565 seconds (2 allocations: 781.328 KiB)
add(x, y)
for _ in 1:5
@time add(x, y)
end
0.000556 seconds (2 allocations: 781.328 KiB) 0.000637 seconds (2 allocations: 781.328 KiB) 0.000612 seconds (2 allocations: 781.328 KiB) 0.000671 seconds (2 allocations: 781.328 KiB) 0.000528 seconds (2 allocations: 781.328 KiB)
using PyCall
PyCall.pyversion
v"3.5.2"
py"""
import numpy as np
def add(x, y):
output = np.empty(x.shape)
for i in range(len(x)):
output[i] = x[i] + y[i]
return output
"""
@time py"add($(x), $(y))"o;
0.047117 seconds (2.01 k allocations: 115.593 KiB)
using BenchmarkTools
versioninfo()
Julia Version 0.6.0 Commit 903644385b* (2017-06-19 13:05 UTC) Platform Info: OS: macOS (x86_64-apple-darwin16.6.0) CPU: Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz WORD_SIZE: 64 BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell) LAPACK: libopenblas64_ LIBM: libopenlibm LLVM: libLLVM-3.9.1 (ORCJIT, skylake)
b1 = @benchmark x + y
BenchmarkTools.Trial: memory estimate: 781.33 KiB allocs estimate: 2 -------------- minimum time: 71.093 μs (0.00% GC) median time: 126.816 μs (0.00% GC) mean time: 380.584 μs (21.48% GC) maximum time: 16.920 ms (0.00% GC) -------------- samples: 10000 evals/sample: 1
# FLOPS
100_000 / (minimum(b1).time / 10^9)
1.4066082455375352e9
b2 = @benchmark add(x, y)
BenchmarkTools.Trial: memory estimate: 781.33 KiB allocs estimate: 2 -------------- minimum time: 70.281 μs (0.00% GC) median time: 99.873 μs (0.00% GC) mean time: 318.488 μs (20.56% GC) maximum time: 7.014 ms (86.54% GC) -------------- samples: 10000 evals/sample: 1
# FLOPS
100_000 / (minimum(b2).time / 10^9)
1.4228596633514037e9
judge(minimum(b1), minimum(b2))
BenchmarkTools.TrialJudgement: time: +1.16% => invariant (5.00% tolerance) memory: +0.00% => invariant (1.00% tolerance)
add(x, y)
の型推論の結果
@code_typed add(x, y)
CodeInfo(:(begin $(Expr(:inbounds, false)) # meta: location array.jl similar 189 SSAValue(3) = (Base.arraysize)(x, 1)::Int64 # meta: pop location $(Expr(:inbounds, :pop)) output = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float64,1}, svec(Any, Int64), Array{Float64,1}, 0, SSAValue(3), 0)) # line 3: $(Expr(:inbounds, true)) $(Expr(:inbounds, false)) # meta: location abstractarray.jl endof 134 $(Expr(:inbounds, false)) # meta: location abstractarray.jl linearindices 99 # meta: location abstractarray.jl indices1 71 # meta: location abstractarray.jl indices 64 SSAValue(7) = (Base.arraysize)(x, 1)::Int64 # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) # meta: pop location $(Expr(:inbounds, :pop)) SSAValue(8) = (Base.select_value)((Base.slt_int)(SSAValue(7), 0)::Bool, 0, SSAValue(7))::Int64 SSAValue(9) = (Base.select_value)((Base.sle_int)(1, SSAValue(8))::Bool, SSAValue(8), (Base.sub_int)(1, 1)::Int64)::Int64 #temp# = 1 25: unless (Base.not_int)((#temp# === (Base.add_int)(SSAValue(9), 1)::Int64)::Bool)::Bool goto 36 SSAValue(10) = #temp# SSAValue(11) = (Base.add_int)(#temp#, 1)::Int64 i = SSAValue(10) #temp# = SSAValue(11) # line 4: SSAValue(2) = (Base.add_float)((Base.arrayref)(x, i)::Float64, (Base.arrayref)(y, i)::Float64)::Float64 (Base.arrayset)(output, SSAValue(2), i)::Array{Float64,1} 34: goto 25 36: $(Expr(:inbounds, :pop)) # line 6: return output end))=>Array{Float64,1}
LLVMによるネイティブコードへの変換
@code_native add(x, y)
.section __TEXT,__text,regular,pure_instructions Filename: In[15] pushq %rbp movq %rsp, %rbp pushq %r15 pushq %r14 pushq %rbx subq $40, %rsp movq %rsi, %r15 movq %rdi, %rbx movabsq $jl_get_ptls_states_fast, %rax callq *%rax movq %rax, %r14 vxorpd %xmm0, %xmm0, %xmm0 vmovupd %xmm0, -40(%rbp) movq $4, -56(%rbp) movq (%r14), %rax movq %rax, -48(%rbp) leaq -56(%rbp), %rax movq %rax, (%r14) Source line: 189 movq 24(%rbx), %rsi Source line: 2 movabsq $jl_alloc_array_1d, %rax leaq 99153040(%rax), %rdi callq *%rax movq %rax, -40(%rbp) Source line: 64 movq 24(%rbx), %rcx Source line: 3 testq %rcx, %rcx jle L354 Source line: 4 movq (%rbx), %r9 movq (%r15), %r10 movq (%rax), %r11 movl $1, %r15d Source line: 3 cmpq $8, %rcx jb L290 movq %rcx, %r8 movl $1, %r15d andq $-8, %r8 je L290 leaq (%r11,%rcx,8), %rsi leaq (%r9,%rcx,8), %rdx leaq (%r10,%rcx,8), %rdi cmpq %rdx, %r11 sbbb %dl, %dl cmpq %rsi, %r9 sbbb %bl, %bl andb %dl, %bl cmpq %rdi, %r11 sbbb %dl, %dl cmpq %rsi, %r10 sbbb %sil, %sil movl $1, %r15d testb $1, %bl jne L290 andb %sil, %dl andb $1, %dl jne L290 movq %r8, %r15 orq $1, %r15 leaq 32(%r9), %rdx leaq 32(%r10), %rsi leaq 32(%r11), %rdi movq %r8, %rbx nopw %cs:(%rax,%rax) Source line: 4 L240: vmovupd -32(%rdx), %ymm0 vmovupd (%rdx), %ymm1 vaddpd -32(%rsi), %ymm0, %ymm0 vaddpd (%rsi), %ymm1, %ymm1 vmovupd %ymm0, -32(%rdi) vmovupd %ymm1, (%rdi) Source line: 3 addq $64, %rdx addq $64, %rsi addq $64, %rdi addq $-8, %rbx jne L240 cmpq %r8, %rcx je L350 L290: addq $1, %rcx subq %r15, %rcx leaq -8(%r11,%r15,8), %rdx leaq -8(%r10,%r15,8), %rsi leaq -8(%r9,%r15,8), %rdi nopl (%rax,%rax) Source line: 4 L320: vmovsd (%rdi), %xmm0 ## xmm0 = mem[0],zero vaddsd (%rsi), %xmm0, %xmm0 vmovsd %xmm0, (%rdx) Source line: 3 addq $8, %rdx addq $8, %rsi addq $8, %rdi addq $-1, %rcx jne L320 Source line: 4 L350: movq %rax, -32(%rbp) Source line: 6 L354: movq -48(%rbp), %rcx movq %rcx, (%r14) addq $40, %rsp popq %rbx popq %r14 popq %r15 popq %rbp vzeroupper retq nopw (%rax,%rax)
ネイティブコードはCPUで直接実行されるので、仮想マシン(VM)を使う他のインタプリター方式の言語より高速
もうちょっと包括的な日本語のチュートリアル➥ https://github.com/bicycle1885/Julia-Tutorial
x = rand([-1, 0, 1])
if x > 0
println("positive!")
elseif x == 0
println("zero!")
else
println("negative!")
end
zero!
for i in 1:10
println(i)
end
1 2 3 4 5 6 7 8 9 10
while true
r = rand()
@show r
if r < 0.05
break
end
end
r = 0.824963896731119 r = 0.3864837619109138 r = 0.19716180707125286 r = 0.4707863823067948 r = 0.9122340342619175 r = 0.27714398262365547 r = 0.40275437809947934 r = 0.12027556668360484 r = 0.09447130478733246 r = 0.24197683192547736 r = 0.8619030597650075 r = 0.5945882942644753 r = 0.49438472096360275 r = 0.49150286529392995 r = 0.39888971732793155 r = 0.04482709506506044
# functioを使う関数定義
function hello(name)
return "hello, $(name)!"
end
hello (generic function with 1 method)
# 1行関数定義
hello(name) = "hello, $(name)!!"
hello (generic function with 1 method)
# 引数の型制約付き
function hello(name::AbstractString)
return "hello, $(name)!!!"
end
hello (generic function with 2 methods)
# 引数と返り値の型制約付き
function hello(name::AbstractString)::String
return "hello, $(name)!!!!"
end
hello (generic function with 2 methods)
# nothingの型
Void
Void
typeof(nothing)
Void
# 真偽型
Bool
Bool
typeof(true)
Bool
# 符号付き整数
Int8, Int16, Int32, Int64, Int128
(Int8, Int16, Int32, Int64, Int128)
typeof(1)
Int64
# 符号無し整数
UInt8, UInt16, UInt32, UInt64, UInt128
(UInt8, UInt16, UInt32, UInt64, UInt128)
typeof(0x0000000000000001)
UInt64
# エイリアス
Int == Int64
true
# エイリアス
UInt == UInt64
true
# 浮動小数点数
Float16, Float32, Float64
(Float16, Float32, Float64)
typeof(1.0)
Float64
# 文字
Char
Char
typeof('a')
Char
リテラルによる型の違い
typeof(1)
Int64
typeof(0x01)
UInt8
typeof(0x0001)
UInt16
typeof(1.0)
Float64
文字列
String
String
"abracadabra"
"abracadabra"
"アブラカダブラ"
"アブラカダブラ"
配列
Array
Array
# 1次元配列 (ベクトルとも言う)
[1, 2, 3]
3-element Array{Int64,1}: 1 2 3
# 2次元配列 (行列とも言う)
[1 2 3
4 5 6]
2×3 Array{Int64,2}: 1 2 3 4 5 6
JuliaにPythonでいうlist
型はないが、1次元配列がその代わり
集合
Set
Set
Set([1, 2, 3])
Set([2, 3, 1])
辞書
Dict
Dict
Dict("one" => 1, "two" => 2)
Dict{String,Int64} with 2 entries: "two" => 2 "one" => 1
Nullable
型
Nullable
Nullable
Nullable(1)
Nullable{Int64}(1)
Nullable{Int64}()
Nullable{Int64}()
Int64
など具体型をまとめる型
Int
Int64
supertype(Int)
Signed
supertype(supertype(Int))
Integer
supertype(supertype(supertype(Int)))
Real
supertype(supertype(supertype(supertype(Int))))
Number
supertype(supertype(supertype(supertype(supertype(Int)))))
Any
supertype(supertype(supertype(supertype(supertype(supertype(Int))))))
Any
抽象型AbstractMatrix
のサブタイプ
subtypes(AbstractMatrix)
21-element Array{Union{DataType, UnionAll},1}: AbstractSparseArray{Tv,Ti,2} where Ti where Tv Base.LinAlg.AbstractTriangular Base.LinAlg.HessenbergQ Base.LinAlg.LQPackedQ Base.LinAlg.QRCompactWYQ Base.LinAlg.QRPackedQ Base.LinAlg.SVDOperator Base.ReshapedArray{T,2,P,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where P<:AbstractArray where T Base.SparseArrays.CHOLMOD.FactorComponent Bidiagonal ConjArray{T,2,A} where A<:AbstractArray where T DenseArray{T,2} where T Diagonal Hermitian PermutedDimsArray{T,2,perm,iperm,AA} where AA<:AbstractArray where iperm where perm where T PyCall.PyArray{T,2} where T RowVector SubArray{T,2,P,I,L} where L where I where P where T SymTridiagonal Symmetric Tridiagonal
型パラメーターを取る型
Array
Array
Array{Int}
Array{Int64,N} where N
Array{Int,1}
Array{Int64,1}
Array{Int,2}
Array{Int64,2}
typeof([1,2,3])
Array{Int64,1}
typeof([1 2 3; 4 5 6])
Array{Int64,2}
d = Dict("one" => 1, "two" => 2)
Dict{String,Int64} with 2 entries: "two" => 2 "one" => 1
typeof(d)
Dict{String,Int64}
struct User
name::String
registerdate::Date
end
jeff = User("Jeff Bezanson", Date(2010, 3, 4))
User("Jeff Bezanson", 2010-03-04)
jeff.name
"Jeff Bezanson"
jeff.registerdate
2010-03-04
jeff.name = "Stefan Karpinski"
type User is immutable Stacktrace: [1] include_string(::String, ::String) at ./loading.jl:515
mutable struct MutableUser
name::String
registerdate::Date
end
viral = MutableUser("Viral Shah", Date(2013, 10, 2))
MutableUser("Viral Shah", 2013-10-02)
viral.name
"Viral Shah"
viral.name = "Alan Edelman"
"Alan Edelman"
viral
MutableUser("Alan Edelman", 2013-10-02)
関数+
には180を超えるメソッドが定義されている
+
+ (generic function with 185 methods)
methods(+)
methodswith(Int, +)
1 + 1
2
struct Point{T}
x::T
y::T
end
Base.:(+)(p::Point, q::Point) = Point(p.x + q.x, p.y + q.y)
+
+ (generic function with 186 methods)
p = Point(1, 2)
q = Point(-2, 4)
Point{Int64}(-2, 4)
p + q
Point{Int64}(-1, 6)
p + 1
MethodError: no method matching +(::Point{Int64}, ::Int64) Closest candidates are: +(::Any, ::Any, ::Any, ::Any...) at operators.jl:424 +(::Complex{Bool}, ::Real) at complex.jl:247 +(::Char, ::Integer) at char.jl:40 ... Stacktrace: [1] include_string(::String, ::String) at ./loading.jl:515
Base.:(+)(p::Point, d::Real) = Point(p.x + d, p.y + d)
p + 1
Point{Int64}(2, 3)
Base.:(*)(p::Point, s::Real) = Point(p.x * s, p.y * s)
p * 3
Point{Int64}(3, 6)
(p + 1) * 3 == p * 3 + 3
true
function distance(p::Point)
return sqrt(p.x^2 + p.y^2)
end
distance (generic function with 1 method)
distance(p)
2.23606797749979
function Base.:(+)(p::Point, n::Integer)
println("You called Point + Integer!")
return Point(p.x + n, p.y + n)
end
p + 1
You called Point + Integer!
Point{Int64}(2, 3)
p + 1.0
Point{Float64}(2.0, 3.0)
function move_right(p::Point)
return p + Point(1, 0)
end
move_right (generic function with 1 method)
p
Point{Int64}(1, 2)
move_right(p)
Point{Int64}(2, 2)
多重ディスパッチのポイント
ccall
でC言語の関数を呼び出せる
# https://www.gnu.org/software/libc/manual/html_node/Trig-Functions.html
ccall((:sin, "libc"), Cdouble, (Cdouble,), π/2)
1.0
# https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html
unsafe_string(ccall((:getenv, "libc"), Cstring, (Cstring,), "SHELL"))
"/usr/local/bin/fish"
JuliaとCの構造体はメモリレイアウトの互換性があるので、互いに読み書きできる。具体例は以下のパッケージを参照
スカラーや配列を同じ形に揃えて、要素毎の演算をすること。
x = [1,2,3]; y = [4,5,6]
3-element Array{Int64,1}: 4 5 6
# スカラー倍 (ブロードキャストとは言わない)
x * 3
3-element Array{Int64,1}: 3 6 9
# ベクトル和 (ブロードキャストとは言わない)
x + y
3-element Array{Int64,1}: 5 7 9
# 要素毎の積 (ブロードキャスト)
x .* 3
3-element Array{Int64,1}: 3 6 9
# 要素毎の和 (ブロードキャスト)
x .+ y
3-element Array{Int64,1}: 5 7 9
列ベクトルと列ベクトルの積は計算できない
x * y
DimensionMismatch("Cannot multiply two vectors") Stacktrace: [1] *(::Array{Int64,1}, ::Array{Int64,1}) at ./linalg/rowvector.jl:184 [2] include_string(::String, ::String) at ./loading.jl:515
ブロードキャストを使えば要素毎の積なら計算できる
x .* y
3-element Array{Int64,1}: 4 10 18
列ベクトルと行ベクトルの和は計算できない
x + y'
DimensionMismatch("dimensions must match") Stacktrace: [1] promote_shape(::Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}, ::Tuple{Base.OneTo{Int64}}) at ./indices.jl:79 [2] promote_shape(::Tuple{Base.OneTo{Int64}}, ::Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}) at ./indices.jl:75 [3] +(::Array{Int64,1}, ::RowVector{Int64,Array{Int64,1}}) at ./arraymath.jl:37 [4] include_string(::String, ::String) at ./loading.jl:515
ブロードキャストはできる
x .+ y'
3×3 Array{Int64,2}: 5 6 7 6 7 8 7 8 9
A = [
1 2
3 4
5 6
]
3×2 Array{Int64,2}: 1 2 3 4 5 6
行列と列ベクトルの和は計算できない
A + x
DimensionMismatch("dimensions must match") Stacktrace: [1] promote_shape(::Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}, ::Tuple{Base.OneTo{Int64}}) at ./indices.jl:84 [2] +(::Array{Int64,2}, ::Array{Int64,1}) at ./arraymath.jl:37 [3] include_string(::String, ::String) at ./loading.jl:515
ブロードキャストはできる
A .+ x
3×2 Array{Int64,2}: 2 3 5 6 8 9
x = -3
-3
@show x;
x = -3
@assert x > 0
AssertionError: x > 0 Stacktrace: [1] include_string(::String, ::String) at ./loading.jl:515
標準ライブラリで提供されているマクロは何種類かに分けられる。
@show
, @less
, @which
, @code_warntype
, etc.@assert
, @goto
, @label
, @async
, @parallel
, etc.@inbounds
, @inline
, @fastmath
, etc.@__DIR__
, @__FILE__
, etc.@r_str
, @ip_str
, @v_str
, etc.ismatch(r"0x[0-9A-Fa-f]{2}", "0x3F")
true
答: 速い
現時点でC/C++より遅くなりそうなケース
これ以外のケースでは、バグでないJuliaの制約でパフォーマンスが出なかったことは体験してない(個人の感想です)。
希望の光
llvmcall
命令で細かいSIMDの制御もできないことはない? (https://github.com/eschnett/SIMD.jl)具体例: Base64のデコーダー
64+1種類のASCII文字を使ってエンコードされたバイナリデータを元のデータに復元する。
ポイント
function TranscodingStreams.process(
codec :: Base64Decoder,
input :: Memory,
output :: Memory,
error :: Error)
table = codec.table
state = codec.state
buffer = codec.buffer
# Check if we can encode data.
if !is_running(state)
error[] = ArgumentError("decoding is already finished")
return 0, 0, :error
elseif output.size < 3
# Need more output space.
return 0, 0, :ok
end
# Load the frist bytes.
i = j = 0
while buffer.size < 3 && i < input.size
buffer[buffer.size+=1] = input[i+=1]
end
c1 = c2 = c3 = c4 = BASE64_CODEIGN
if buffer.size ≥ 1
c1 = decode(table, buffer[1])
end
if buffer.size ≥ 2
c2 = decode(table, buffer[2])
end
if buffer.size ≥ 3
c3 = decode(table, buffer[3])
end
empty!(buffer)
# Start decoding loop.
status = :ok
@inbounds while true
if c1 > 0x3f || c2 > 0x3f || c3 > 0x3f || c4 > 0x3f
i, j, status = decode_irregular(table, c1, c2, c3, c4, input, i, output, j, error)
else
output[j+1] = c1 << 2 | c2 >> 4
output[j+2] = c2 << 4 | c3 >> 2
output[j+3] = c3 << 6 | c4
j += 3
end
if i + 4 ≤ input.size && j + 3 ≤ output.size && status == :ok
c1 = decode(table, input[i+1])
c2 = decode(table, input[i+2])
c3 = decode(table, input[i+3])
c4 = decode(table, input[i+4])
i += 4
else
break
end
end
# Epilogue.
if status == :end || status == :error
finish!(state)
end
return i, j, status
end
# Decode irregular code (e.g. non-alphabet, padding, etc.).
function decode_irregular(table, c1, c2, c3, c4, input, i, output, j, error)
# Skip ignored chars.
while true
if c1 == BASE64_CODEIGN
c1, c2, c3 = c2, c3, c4
elseif c2 == BASE64_CODEIGN
c2, c3 = c3, c4
elseif c3 == BASE64_CODEIGN
c3 = c4
elseif c4 == BASE64_CODEIGN
# pass
else
break
end
if i + 1 ≤ input.size
c4 = decode(table, input[i+=1])
else
c4 = BASE64_CODEEND
break
end
end
# Write output.
if c1 ≤ 0x3f && c2 ≤ 0x3f && c3 ≤ 0x3f && c4 ≤ 0x3f
output[j+=1] = c1 << 2 | c2 >> 4
output[j+=1] = c2 << 4 | c3 >> 2
output[j+=1] = c3 << 6 | c4
status = :ok
elseif c1 ≤ 0x3f && c2 ≤ 0x3f && c3 ≤ 0x3f && c4 == BASE64_CODEPAD
c4 = 0x00
output[j+=1] = c1 << 2 | c2 >> 4
output[j+=1] = c2 << 4 | c3 >> 2
status = :end
elseif c1 ≤ 0x3f && c2 ≤ 0x3f && c3 == c4 == BASE64_CODEPAD
c3 = c4 = 0x00
output[j+=1] = c1 << 2 | c2 >> 4
status = :end
elseif c1 == c2 == c3 == BASE64_CODEIGN && c4 == BASE64_CODEEND
status = :end
else
error[] = ArgumentError("invalid data")
status = :error
end
return i, j, status
end
https://github.com/bicycle1885/CodecBase.jl/blob/master/src/base64/decoder.jl#L50-L159
ベンチマーク
Pythonの標準ライブラリのC言語による実装より高速
Julia 0.6.0:
julia> sizeof(data)
286180
julia> @benchmark transcode(Base64Decoder(), data)
BenchmarkTools.Trial:
memory estimate: 490.31 KiB
allocs estimate: 19
--------------
minimum time: 372.894 μs (0.00% GC)
median time: 561.580 μs (0.00% GC)
mean time: 594.615 μs (6.84% GC)
maximum time: 4.291 ms (68.99% GC)
--------------
samples: 8379
evals/sample: 1
Python 3.5.2:
In [13]: len(data)
Out[13]: 286180
In [14]: %timeit base64.decodebytes(data)
1.16 ms ± 14.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
ユーザーがREPLに打ち込んだ処理が実行されるまでの流れ
# ソースコードのパース
parse("1 + 1")
:(1 + 1)
parse("""
function foobar(x)
@assert x > 0
end
""")
:(function foobar(x) # none, line 2: @assert x > 0 end)
parse
はExpr
のオブジェクトを返す。Expr
は抽象構文木(AST)。
expr = parse("1 + 2 * 3")
:(1 + 2 * 3)
dump(expr)
Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol * 2: Int64 2 3: Int64 3 typ: Any typ: Any
# ifelseなどは複数のif-elseに展開される
quote
if x < 0
println("x is negative")
elseif x == 0
println("x is zero")
else
println("x is positive")
end
end
quote # In[141], line 3: if x < 0 # In[141], line 4: println("x is negative") else # In[141], line 5: if x == 0 # In[141], line 6: println("x is zero") else # In[141], line 8: println("x is positive") end end end
# マクロの展開する関数
macroexpand(expr)
:(1 + 2 * 3)
# @から始まるマクロはこの関数で展開される
macroexpand(:(@assert x > 0))
:(if x > 0 nothing else (Base.throw)(Base.Main.Base.AssertionError("x > 0")) end)
macroexpand(quote
function foobar(x)
@assert x > 0
end
end)
quote # In[144], line 2: function foobar(x) # In[144], line 3: if x > 0 nothing else (Base.throw)(Base.Main.Base.AssertionError("x > 0")) end end end
function foobar(x)
@assert x > 0
end
foobar (generic function with 1 method)
# Juliaの中間表現はunlessの分岐とgotoばかりのフラットなコード
code_lowered(foobar, (Int,))
1-element Array{CodeInfo,1}: CodeInfo(:(begin nothing unless x > 0 goto 4 return 4: return (Base.throw)(((Core.getfield)((Core.getfield)(Base.Main, :Base), :AssertionError))("x > 0")) end))
function foobar(n)
x = 0
for i in 1:n
x += i
end
return x
end
foobar (generic function with 1 method)
# forループもunlessとgotoへ展開
code_lowered(foobar, (Int,))
1-element Array{CodeInfo,1}: CodeInfo(:(begin nothing x = 0 # line 3: SSAValue(0) = (Main.colon)(1, n) #temp# = (Base.start)(SSAValue(0)) 6: unless !((Base.done)(SSAValue(0), #temp#)) goto 15 SSAValue(1) = (Base.next)(SSAValue(0), #temp#) i = (Core.getfield)(SSAValue(1), 1) #temp# = (Core.getfield)(SSAValue(1), 2) # line 4: x = x + i 13: goto 6 15: # line 6: return x end))
# Juliaの中間表現に型推論で型をつける
code_typed(foobar, (Int,))
1-element Array{Any,1}: CodeInfo(:(begin x = 0 # line 3: SSAValue(2) = (Base.select_value)((Base.sle_int)(1, n)::Bool, n, (Base.sub_int)(1, 1)::Int64)::Int64 #temp# = 1 5: unless (Base.not_int)((#temp# === (Base.add_int)(SSAValue(2), 1)::Int64)::Bool)::Bool goto 15 SSAValue(3) = #temp# SSAValue(4) = (Base.add_int)(#temp#, 1)::Int64 i = SSAValue(3) #temp# = SSAValue(4) # line 4: x = (Base.add_int)(x, i)::Int64 13: goto 5 15: # line 6: return x end))=>Int64
# LLVM中間表現に変換
code_llvm(foobar, (Int,))
define i64 @julia_foobar_63146(i64) #0 !dbg !5 { top: %1 = icmp slt i64 %0, 1 br i1 %1, label %L15, label %if.lr.ph if.lr.ph: ; preds = %top %2 = shl i64 %0, 1 %3 = add i64 %0, -1 %4 = zext i64 %3 to i65 %5 = add i64 %0, -2 %6 = zext i64 %5 to i65 %7 = mul i65 %4, %6 %8 = lshr i65 %7, 1 %9 = trunc i65 %8 to i64 %10 = add i64 %2, %9 %11 = add i64 %10, -1 br label %L15 L15: ; preds = %if.lr.ph, %top %x.0.lcssa = phi i64 [ %11, %if.lr.ph ], [ 0, %top ] ret i64 %x.0.lcssa }
# nativeコードに変換
code_native(foobar, (Int,))
.section __TEXT,__text,regular,pure_instructions Filename: In[147] pushq %rbp movq %rsp, %rbp xorl %eax, %eax Source line: 3 testq %rdi, %rdi jle L34 leaq -1(%rdi), %rdx leaq -2(%rdi), %rax mulxq %rax, %rax, %rcx shldq $63, %rax, %rcx leaq -1(%rcx,%rdi,2), %rax Source line: 6 L34: popq %rbp retq nopw %cs:(%rax,%rax)
REPLにコードを打ち込むと上の処理を行う
foobar(10)
55
function foobar(n)
return n * 2
end
foobar (generic function with 1 method)
@code_typed foobar(1)
CodeInfo(:(begin return (Base.mul_int)(n, 2)::Int64 end))=>Int64
@code_typed foobar(1.0)
CodeInfo(:(begin return (Base.mul_float)(n, (Base.sitofp)(Float64, 2)::Float64)::Float64 end))=>Float64
function foobar(n)
if n isa Integer
println("Integer!")
end
return n * 2
end
foobar (generic function with 1 method)
foobar(1)
Integer!
2
foobar(1.0)
2.0
@code_typed foobar(1)
CodeInfo(:(begin # line 3: $(Expr(:inbounds, false)) # meta: location coreio.jl println 5 SSAValue(0) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO # meta: pop location $(Expr(:inbounds, :pop)) (Base.print)(SSAValue(0), "Integer!", $(QuoteNode('\n')))::Void 8: # line 5: return (Base.mul_int)(n, 2)::Int64 end))=>Int64
特殊化によりprintlnが分岐ごと消えている!
@code_typed foobar(1.0)
CodeInfo(:(begin goto 3 # line 3: 3: # line 5: return (Base.mul_float)(n, (Base.sitofp)(Float64, 2)::Float64)::Float64 end))=>Float64
start_ns = time_ns()
s = 0.0
for _ in 1:1_000_000
s += rand()
end
@show Int(time_ns() - start_ns)
Int(time_ns() - start_ns) = 87514105
87514105
function loop()
s = 0.0
for _ in 1:1_000_000
s += rand()
end
return s
end
loop (generic function with 1 method)
start_ns = time_ns()
s = loop()
@show Int(time_ns() - start_ns)
Int(time_ns() - start_ns) = 14248780
14248780
使い分け:
function collatz1(n)
step = 0
while n != 1
step += 1
if mod(n, 2) == 0
n /= 2
else
n = n * 3 + 1
end
end
return step
end
collatz1 (generic function with 1 method)
collatz1(10)
6
using BenchmarkTools
@benchmark for n in 1:10000; collatz1(n); end
BenchmarkTools.Trial: memory estimate: 86.06 MiB allocs estimate: 5640009 -------------- minimum time: 110.469 ms (5.88% GC) median time: 116.169 ms (5.97% GC) mean time: 117.584 ms (5.84% GC) maximum time: 132.606 ms (5.19% GC) -------------- samples: 43 evals/sample: 1
@code_typed collatz1(10)
CodeInfo(:(begin n@_4 = n@_2 step = 0 # line 3: 4: unless (n@_4 isa Float64)::Bool goto 8 #temp#@_5 = MethodInstance for !=(::Float64, ::Int64) goto 17 8: unless (n@_4 isa Int64)::Bool goto 12 #temp#@_5 = MethodInstance for !=(::Int64, ::Int64) goto 17 12: goto 14 14: #temp#@_6 = (n@_4 != 1)::Bool goto 19 17: #temp#@_6 = $(Expr(:invoke, :(#temp#@_5), :(Main.!=), :(n@_4), 1)) 19: unless #temp#@_6 goto 108 # line 4: step = (Base.add_int)(step, 1)::Int64 # line 5: unless (n@_4 isa Float64)::Bool goto 27 #temp#@_7 = MethodInstance for mod(::Float64, ::Int64) goto 36 27: unless (n@_4 isa Int64)::Bool goto 31 #temp#@_7 = MethodInstance for mod(::Int64, ::Int64) goto 36 31: goto 33 33: #temp#@_8 = (Main.mod)(n@_4, 2)::Union{Float64, Int64} goto 38 36: #temp#@_8 = $(Expr(:invoke, :(#temp#@_7), :(Main.mod), :(n@_4), 2)) 38: unless (#temp#@_8 isa Float64)::Bool goto 42 #temp#@_9 = MethodInstance for ==(::Float64, ::Int64) goto 51 42: unless (#temp#@_8 isa Int64)::Bool goto 46 #temp#@_9 = MethodInstance for ==(::Int64, ::Int64) goto 51 46: goto 48 48: #temp#@_10 = (#temp#@_8 == 0)::Bool goto 53 51: #temp#@_10 = $(Expr(:invoke, :(#temp#@_9), :(Main.==), :(#temp#@_8), 0)) 53: unless #temp#@_10 goto 73 # line 6: unless (n@_4 isa Float64)::Bool goto 59 #temp#@_11 = MethodInstance for /(::Float64, ::Int64) goto 68 59: unless (n@_4 isa Int64)::Bool goto 63 #temp#@_11 = MethodInstance for /(::Int64, ::Int64) goto 68 63: goto 65 65: #temp#@_12 = (n@_4 / 2)::Float64 goto 70 68: #temp#@_12 = $(Expr(:invoke, :(#temp#@_11), :(Main./), :(n@_4), 2)) 70: n@_4 = #temp#@_12 goto 106 73: # line 8: unless (n@_4 isa Float64)::Bool goto 78 #temp#@_13 = MethodInstance for *(::Float64, ::Int64) goto 87 78: unless (n@_4 isa Int64)::Bool goto 82 #temp#@_13 = MethodInstance for *(::Int64, ::Int64) goto 87 82: goto 84 84: #temp#@_14 = (n@_4 * 3)::Union{Float64, Int64} goto 89 87: #temp#@_14 = $(Expr(:invoke, :(#temp#@_13), :(Main.*), :(n@_4), 3)) 89: unless (#temp#@_14 isa Float64)::Bool goto 93 #temp#@_15 = MethodInstance for +(::Float64, ::Int64) goto 102 93: unless (#temp#@_14 isa Int64)::Bool goto 97 #temp#@_15 = MethodInstance for +(::Int64, ::Int64) goto 102 97: goto 99 99: #temp#@_16 = (#temp#@_14 + 1)::Union{Float64, Int64} goto 104 102: #temp#@_16 = $(Expr(:invoke, :(#temp#@_15), :(Main.+), :(#temp#@_14), 1)) 104: n@_4 = #temp#@_16 106: goto 4 108: # line 11: return step end))=>Int64
function collatz2(n)
step = 0
while n != 1
step += 1
if mod(n, 2) == 0
n = div(n, 2)
else
n = n * 3 + 1
end
end
return step
end
collatz2 (generic function with 1 method)
collatz2(10)
6
@benchmark for n in 1:10000; collatz2(n); end
BenchmarkTools.Trial: memory estimate: 0 bytes allocs estimate: 0 -------------- minimum time: 3.080 ms (0.00% GC) median time: 3.298 ms (0.00% GC) mean time: 3.386 ms (0.00% GC) maximum time: 17.355 ms (0.00% GC) -------------- samples: 1473 evals/sample: 1
@code_warntype collatz2(10)
Variables: #self#::#collatz2 n@_2::Int64 step::Int64 n@_4::Int64 d::Int64 #temp#::Int64 Body: begin n@_4::Int64 = n@_2::Int64 step::Int64 = 0 # line 3: 4: unless (Base.not_int)((n@_4::Int64 === 1)::Bool)::Bool goto 32 # line 4: step::Int64 = (Base.add_int)(step::Int64, 1)::Int64 # line 5: $(Expr(:inbounds, false)) # meta: location int.jl mod 170 unless (2 === -1)::Bool goto 14 #temp#::Int64 = 0 goto 20 14: # line 171: # meta: location int.jl fld 191 d::Int64 = (Base.checked_sdiv_int)(n@_4::Int64, 2)::Int64 # meta: pop location #temp#::Int64 = (Base.sub_int)(n@_4::Int64, (Base.mul_int)((Base.sub_int)(d::Int64, (Base.and_int)((Base.zext_int)(Int64, (Base.and_int)((Base.slt_int)((Base.xor_int)(n@_4::Int64, 2)::Int64, 0)::Bool, (Base.not_int)(((Base.mul_int)(d::Int64, 2)::Int64 === n@_4::Int64)::Bool)::Bool)::Bool)::Int64, 1)::Int64)::Int64, 2)::Int64)::Int64 20: # meta: pop location $(Expr(:inbounds, :pop)) unless (#temp#::Int64 === 0)::Bool goto 27 # line 6: n@_4::Int64 = (Base.checked_sdiv_int)(n@_4::Int64, 2)::Int64 goto 30 27: # line 8: n@_4::Int64 = (Base.add_int)((Base.mul_int)(n@_4::Int64, 3)::Int64, 1)::Int64 30: goto 4 32: # line 11: return step::Int64 end::Int64
@show
や@assert
が標準にもあるマクロmacro myassert(ex)
msg = "$(sprint(print, ex)) is not satisfied"
quote
if !$(ex)
throw(AssertionError($(msg)))
end
end
end
@myassert (macro with 1 method)
x = 1
1
@myassert x > 0
x = 0
0
@myassert x > 0
AssertionError: x > 0 is not satisfied Stacktrace: [1] include_string(::String, ::String) at ./loading.jl:515
具体例: 正規表現をJuliaへコンパイル
Automa.jlを使って、実行時のコード生成でどういうことができるかをデモンストレーションしてみる。
import Automa
import Automa.RegExp: @re_str
const re = Automa.RegExp
oct = re"0o[0-7]+"
dec = re"[-+]?[0-9]+"
hex = re"0x[0-9A-Fa-f]+"
prefloat = re"[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)"
float = prefloat | re.cat(prefloat | re"[-+]?[0-9]+", re"[eE][-+]?[0-9]+")
number = oct | dec | hex | float
numbers = re.cat(re.opt(number), re.rep(re" +" * number), re" *");
number.actions[:enter] = [:mark]
oct.actions[:exit] = [:oct]
dec.actions[:exit] = [:dec]
hex.actions[:exit] = [:hex]
float.actions[:exit] = [:float]
1-element Array{Symbol,1}: :float
actions = Dict(
:mark => :(mark = p),
:oct => :(emit(:oct)),
:dec => :(emit(:dec)),
:hex => :(emit(:hex)),
:float => :(emit(:float)),
);
machine = Automa.compile(numbers)
context = Automa.CodeGenContext(generator=:goto, clean=true)
@eval function tokenize(data::String)
tokens = Tuple{Symbol,String}[]
mark = 0
$(Automa.generate_init_code(context, machine))
p_end = p_eof = endof(data)
emit(kind) = push!(tokens, (kind, data[mark:p-1]))
$(Automa.generate_exec_code(context, machine, actions))
return tokens, cs == 0 ? :ok : cs < 0 ? :error : :incomplete
end
tokenize (generic function with 1 method)
tokens, status = tokenize("1 0x0123BEEF 0o754 3.14 -1e4 +6.022045e23")
tokens
6-element Array{Tuple{Symbol,String},1}: (:dec, "1") (:hex, "0x0123BEEF") (:oct, "0o754") (:float, "3.14") (:float, "-1e4") (:float, "+6.022045e23")
write("numbers.dot", Automa.machine2dot(machine))
run(`dot -Tpng -o numbers.png numbers.dot`)
html"<img src=\"numbers.png\">"
tokenize("0x")
(Tuple{Symbol,String}[], :incomplete)
tokenize("0x1234")
(Tuple{Symbol,String}[(:hex, "0x1234")], :ok)
tokenize("0x1234G")
(Tuple{Symbol,String}[], :error)
Automa.generate_exec_code(context, machine, actions)
quote if p > p_end @goto exit end ##722 = (Automa.SizedMemory)(data) if cs == 1 @goto state_case_1 else if cs == 2 @goto state_case_2 else if cs == 3 @goto state_case_3 else if cs == 4 @goto state_case_4 else if cs == 5 @goto state_case_5 else if cs == 6 @goto state_case_6 else if cs == 7 @goto state_case_7 else if cs == 8 @goto state_case_8 else if cs == 9 @goto state_case_9 else if cs == 10 @goto state_case_10 else if cs == 11 @goto state_case_11 else if cs == 12 @goto state_case_12 else if cs == 13 @goto state_case_13 else @goto exit end end end end end end end end end end end end end @label state_1_action_2 emit(:float) @goto state_1 @label state_1_action_3 emit(:dec) @goto state_1 @label state_1_action_4 emit(:hex) @goto state_1 @label state_1_action_5 emit(:oct) @goto state_1 @label state_1 p += 1 if p > p_end cs = 1 @goto exit end @label state_case_1 ##723 = (getindex)(##722, p + 0) if (##723 in 0x31:0x39 || false) && true @goto state_3_action_1 else if (##723 in 0x2b:0x2b || (##723 in 0x2d:0x2d || false)) && true @goto state_4_action_1 else if (##723 in 0x20:0x20 || false) && true @goto state_1 else if (##723 in 0x30:0x30 || false) && true @goto state_2_action_1 else if (##723 in 0x2e:0x2e || false) && true @goto state_5_action_1 else cs = -1 @goto exit end end end end end @label state_5_action_1 mark = p @goto state_5 @label state_5 p += 1 if p > p_end cs = 5 @goto exit end @label state_case_5 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_6 else cs = -5 @goto exit end @label state_6 p += 1 if p > p_end cs = 6 @goto exit end @label state_case_6 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_6 else if (##723 in 0x45:0x45 || (##723 in 0x65:0x65 || false)) && true @goto state_7 else if (##723 in 0x20:0x20 || false) && true @goto state_1_action_2 else cs = -6 @goto exit end end end @label state_7 p += 1 if p > p_end cs = 7 @goto exit end @label state_case_7 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_8 else if (##723 in 0x2b:0x2b || (##723 in 0x2d:0x2d || false)) && true @goto state_9 else cs = -7 @goto exit end end @label state_9 p += 1 if p > p_end cs = 9 @goto exit end @label state_case_9 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_8 else cs = -9 @goto exit end @label state_8 p += 1 if p > p_end cs = 8 @goto exit end @label state_case_8 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_8 else if (##723 in 0x20:0x20 || false) && true @goto state_1_action_2 else cs = -8 @goto exit end end @label state_4_action_1 mark = p @goto state_4 @label state_4 p += 1 if p > p_end cs = 4 @goto exit end @label state_case_4 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_3 else if (##723 in 0x2e:0x2e || false) && true @goto state_5 else cs = -4 @goto exit end end @label state_3_action_1 mark = p @goto state_3 @label state_3 p += 1 if p > p_end cs = 3 @goto exit end @label state_case_3 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_3 else if (##723 in 0x45:0x45 || (##723 in 0x65:0x65 || false)) && true @goto state_7 else if (##723 in 0x20:0x20 || false) && true @goto state_1_action_3 else if (##723 in 0x2e:0x2e || false) && true @goto state_6 else cs = -3 @goto exit end end end end @label state_2_action_1 mark = p @goto state_2 @label state_2 p += 1 if p > p_end cs = 2 @goto exit end @label state_case_2 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || false) && true @goto state_3 else if (##723 in 0x45:0x45 || (##723 in 0x65:0x65 || false)) && true @goto state_7 else if (##723 in 0x6f:0x6f || false) && true @goto state_10 else if (##723 in 0x20:0x20 || false) && true @goto state_1_action_3 else if (##723 in 0x78:0x78 || false) && true @goto state_11 else if (##723 in 0x2e:0x2e || false) && true @goto state_6 else cs = -2 @goto exit end end end end end end @label state_11 p += 1 if p > p_end cs = 11 @goto exit end @label state_case_11 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || (##723 in 0x41:0x46 || (##723 in 0x61:0x66 || false))) && true @goto state_12 else cs = -11 @goto exit end @label state_12 p += 1 if p > p_end cs = 12 @goto exit end @label state_case_12 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x39 || (##723 in 0x41:0x46 || (##723 in 0x61:0x66 || false))) && true @goto state_12 else if (##723 in 0x20:0x20 || false) && true @goto state_1_action_4 else cs = -12 @goto exit end end @label state_10 p += 1 if p > p_end cs = 10 @goto exit end @label state_case_10 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x37 || false) && true @goto state_13 else cs = -10 @goto exit end @label state_13 p += 1 if p > p_end cs = 13 @goto exit end @label state_case_13 ##723 = (getindex)(##722, p + 0) if (##723 in 0x30:0x37 || false) && true @goto state_13 else if (##723 in 0x20:0x20 || false) && true @goto state_1_action_5 else cs = -13 @goto exit end end @label exit if p > p_eof ≥ 0 && cs ∈ Automa.StableSet{Int64}(Automa.StableDict(1=>nothing,6=>nothing,8=>nothing,3=>nothing,2=>nothing,12=>nothing,13=>nothing)) if cs == 1 else if cs == 6 emit(:float) else if cs == 8 emit(:float) else if cs == 3 emit(:dec) else if cs == 2 emit(:dec) else if cs == 12 emit(:hex) else if cs == 13 emit(:oct) else () end end end end end end end cs = 0 end end
TIOBE(August 2017)では46位 (https://www.tiobe.com/tiobe-index/)
IEEE Spectrum 2017では31位 (http://spectrum.ieee.org/static/interactive-the-top-programming-languages-2017)
入りそうな機能
NamedTuple
: 名前付きタプルTuple
は位置(1,2,...)で要素を指定NamedTuple
は名前でも要素を指定できるように(foo=1.0, bar="hoge")
のような構文も追加されるNull
値: 欠損値の扱いの向上Nullable
でnullかもしれない値を表現Nullable
を外さないといけない)Union{Float32,Void}
みたいなのが高速に扱えるようになる@threads
を使えばforループを局所的に並列実行できるが...