LoginSignup
1
2

Julia独習メモ <2>

Posted at

Juliaを独習していて、その内容や気になったことをメモ書きとして残しています。

前回まで:

3日目 マニュアル読みの続き

算術演算子と初等関数

算術演算子、論理演算子、ビット演算子

概ね普通なので目についたものだけ触れる。

  • 整数での除算はx ÷ y
    • ゼロ方向に丸められる整数除算
    • fld, cldなど、丸め方が異なる整数除算もある
    • %は、÷に対応する剰余演算子。一方、丸め方の異なる剰余演算子modもある
  • x \ yというのがあり、y / xと同じ意味である - ただし、試したところ引数の評価順序は、前者がx, yの順であるのに対し、後者はy, xの順であった
  • xy乗はx^y
  • は単項演算子のように振る舞う。\sqrt-tabで入力
  • bitwise xorはで、\xor-tabで入力できる
  • bitwise nand , bitwise nor もそれぞれ、\nand, \norとtabで入力できる
  • 右シフトは算術シフト。論理右シフトは>>>
  • += などの演算子も用意されている
  • Pythonのような比較演算子の連続にも対応している
    • 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
    • 値の評価順序はundefinedであることがドキュメントに明記されており、副作用を持った項を使うことは推奨されない

これら演算子は、(+)のようにすることで、関数の形で呼び出すこともできる。

julia> (-)(9, 4)
5

julia> (-)(3)
-3

vectrized dot

各演算には、 .+.^ などのドット演算が自動的に定義され、配列の各要素に対し、演算が行われる。

julia> [1,2,3] .+ 10
3-element Vector{Int64}:
 11
 12
 13

演算子を関数形式で呼び出す記法では、以下のように書く。 (+).([1, 2, 3], 10)

関数についても、ドット演算を行える。

julia> sin.([0, π/2, π, 3π/2, 2π])
5-element Vector{Float64}:
  0.0
  1.0
  1.2246467991473532e-16
 -1.0
 -2.4492935982947064e-16

1.+1は、1. + 1なのか1 .+ 1なのか分からないため、このような記法は禁止されている。

演算子の優先順位や結合順序は以下で見られる。

julia> Base.operator_precedence(:+), Base.operator_precedence(:*)
(11, 12)

julia> Base.operator_associativity(:+), Base.operator_associativity(:*)
(:none, :none)

+*の結合順序がnoneなのは、1 + 2 + 3(+)((+)(1, 2), 3)(+)(1, (+)(2, 3))ではなく、(+)(1, 2, 3)と解釈されるかららしい。

複素数と有理数

虚数単位はimで、定数倍や足し算により、複素数を作ることができる。

julia> im
im

julia> typeof(im)
Complex{Bool}

julia> 1im
0 + 1im

julia> typeof(ans)
Complex{Int64}

julia> 1.0 + im
1.0 + 1.0im

julia> typeof(ans)
ComplexF64 (alias for Complex{Float64})

complex(1, 2)のようにしても作ることができる。

1 // 2 で、有理数 $\frac{1}{2}$が作れる。

文字と文字列

Charは32ビット整数であり、Unicodeを扱うこともできる。ただし、1コードポイントに対応しないものは扱えない。

julia> 'A'
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)

julia> 'あ'
'あ': Unicode U+3042 (category Lo: Letter, other)

julia> '𠮟'
'𠮟': Unicode U+20B9F (category Lo: Letter, other)

julia> '😊'
'😊': Unicode U+1F60A (category So: Symbol, other)

julia> '🇫🇮ERROR: syntax: character literal contains multiple characters
Stacktrace:
 [1] top-level scope
   @ none:1

文字列リテラルはダブルクオートや、三重のダブルクオートで書く。
インデックスや、レンジを与えることで、文字や文字列を取り出せる。

julia> "hello"[1]
'h': ASCII/Unicode U+0068 (category Ll: Letter, lowercase)

julia> "hello"[2:4]
"ell"

長い文字列の部分をコピーせずに取り出すには、@views "hello"[2:4]のようにするといいらしい。

文字列のインデックスは、何文字目、ではなく、UTF-8で書いたときに何バイト目、を表していて、中途半端な参照をするとエラーが発生する。

julia> "aあb"[1:1]
"a"

julia> "aあb"[1:2]
"aあ"

julia> "aあb"[1:3]
ERROR: StringIndexError: invalid index [3], valid nearby indices [2]=>'あ', [5]=>'b'
Stacktrace:
 [1] string_index_err(s::String, i::Int64)
   @ Base ./strings/string.jl:12
 [2] getindex(s::String, r::UnitRange{Int64})
   @ Base ./strings/string.jl:278
 [3] top-level scope
   @ REPL[124]:1

julia> "aあb"[1:5]
"aあb"

nextind/previndを用いて、次/前のインデックスを求めることができる。

julia> nextind("aあb", 1)
2

julia> nextind("aあb", 2)
5

julia> nextind("aあb", 3)
5

julia> nextind("aあb", 5)
6

1文字ずつ取り出すには、こういう方法がある

julia> s = "aあb"
"aあb"

julia> for i = eachindex(s)
         println(s[i])
       end
a

b

julia> for c in s
         println(c)
       end
a

b

文字列リテラル中の"$x"は、変数xに置き換えられる。'$'そのものを記述したい場合は"$"のようにエスケープする。

r"..." によって、正規表現を書くことができる。(Pythonのraw文字列にあたるものはraw"...")

julia> typeof(r"")
Regex

julia> match(r"a.c", "abc")
RegexMatch("abc")

replaceは以下のように行う。

julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"

b"..." はバイト配列リテラル、v"0.1"はバージョン番号リテラル(semver)

4〜6日目 関数とタプル

Functionsを読む。

関数

関数は以下のように定義できる。最後に評価された式の値が返される。(明示的にreturnで値を返すこともできる)
値を返したくない場合は、nothingを返すとよい(return nothing)

julia> function f(x,y)
           x + y
       end
f (generic function with 1 method)

julia> f(x,y) = x + y
f (generic function with 1 method)

無名関数は、以下のように定義できる。

julia> x -> x^2 + 2x - 1
#1 (generic function with 1 method)

julia> function (x)
           x^2 + 2x - 1
       end
#5 (generic function with 1 method)

引数の型や返り値の型は、::TypeNameでつけることができる。

f(x::Int64)::Int64 = x

定義した関数のvectorize版はドット記法により使うことができる。

julia> inc(x::Int)::Int = x + 1
inc (generic function with 1 method)

julia> inc.([1, 2, 3])
3-element Vector{Int64}:
 2
 3
 4

hcat, vcat, hvcatなど

[1, 2, 3] のように書くと、Vectorになる。

julia> [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

[1 2 3] のように、カンマを入れずに書くと、hcat(1, 2, 3)と同じ意味で扱われ、横方向にそれらを並べたMatrixになる。

julia> [1 2 3]
1×3 Matrix{Int64}:
 1  2  3

julia> hcat(1, 2, 3)
1×3 Matrix{Int64}:
 1  2  3

[[1, 2] [3, 4] [5, 6]] とすることで、2x3のMatrixとなる。

julia> [[1, 2] [3, 4] [5, 6]]
2×3 Matrix{Int64}:
 1  3  5
 2  4  6

[[1 2] [3 4] [5 6]] は、全てを横に並べたような形になる。[[1 2], [3 4], [5 6]]は、MatrixVectorである。

julia> [[1 2] [3 4] [5 6]]
1×6 Matrix{Int64}:
 1  2  3  4  5  6

julia> [[1 2], [3 4], [5 6]]
3-element Vector{Matrix{Int64}}:
 [1 2]
 [3 4]
 [5 6]

[1; 2; 3] のように書くと、vcat(1, 2, 3) と同じ意味で扱われ、縦方向にそれらを並べる。

julia> [1; 2; 3]
3-element Vector{Int64}:
 1
 2
 3

julia> [[1; 2]; 3]
3-element Vector{Int64}:
 1
 2
 3

Matrixvcatで並べると、MatrixVectorではなくMatrixとなる。

julia> [[1 2]; [3 4]; [5 6]]
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6

[1 2; 3 4; 5 6]も、Matrixとなり、これはhvcat((2, 2, 2), 1, 2, 3, 4, 5, 6)の意味である。

julia> [[1 2]; [3 4]; [5 6]]
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6

julia> hvcat((2, 2, 2), 1, 2, 3, 4, 5, 6)
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6

'を後置することで、ArrayMatrixの転置複素共役(エルミート共役。実数においては単なる転置)を返す。複素数においては複素共役を返し、実数においては変化しない。好みや慣れの問題だと思うが、この記法はどうなのか?

julia> A = [1.0+1im 2+2im; 3+3im 4+4im]
2×2 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im
 3.0+3.0im  4.0+4.0im

julia> A'
2×2 adjoint(::Matrix{ComplexF64}) with eltype ComplexF64:
 1.0-1.0im  3.0-3.0im
 2.0-2.0im  4.0-4.0im

julia> A''
2×2 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im
 3.0+3.0im  4.0+4.0im

julia> A = [1.0+1im 2+2im 3+3im]
1×3 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im  3.0+3.0im

julia> A'
3×1 adjoint(::Matrix{ComplexF64}) with eltype ComplexF64:
 1.0 - 1.0im
 2.0 - 2.0im
 3.0 - 3.0im

julia> A''
1×3 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im  3.0+3.0im

julia> x = 1.0 + 2.0im
1.0 + 2.0im

julia> x'
1.0 - 2.0im

julia> x''
1.0 + 2.0im

A[i]getindex(A, i)に等しく、A[i] = xsetindex!(A, x, i)に等しい。stylistic conventionsから、末尾に!がつくものは、引数を変更していることを表す。

タプル

(1, "x", 3.0)(1,)()はタプルである。

julia> typeof((1, "x", 3.0))
Tuple{Int64, String, Float64}

タプルは、getindexで参照ができる。setindex!で変更はできない。

julia> t = (1, "x", 3)
(1, "x", 3)

julia> t[1]
1

julia> t[2]
"x"

julia> t[3]
3

julia> t[1] = 2
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, String, Int64}, ::Int64, ::Int64)
Stacktrace:
 [1] top-level scope
   @ REPL[58]:1

複数の値を返す関数は、タプルを返す関数として作ることができる。このあたりはPythonと同じ。

以下のように名前付きタプル(named tuple)を作ることができる。名前付きタプルは、インデックスでも名前でも参照することができる。

julia> x = (a=10, b=20)
(a = 10, b = 20)

julia> x[1]
10

julia> x.a
10

分割代入も行える。

左辺の数が足りない場合は、エラーなどにはならない。右辺の数が足りない場合は、BoundsErrorが出される。

julia> a, b = (1, 2, 3)
(1, 2, 3)

julia> b
2

julia> a, b, c = (1, 2)
ERROR: BoundsError: attempt to access Tuple{Int64, Int64} at index [3]
Stacktrace:
 [1] indexed_iterate(t::Tuple{Int64, Int64}, i::Int64, state::Int64)
   @ Base ./tuple.jl:88
 [2] top-level scope
   @ REPL[71]:1

行列を右辺に取ったときの挙動は少し思っていたのと違った。

julia> a, b = [1 2; 3 4]
2×2 Matrix{Int64}:
 1  2
 3  4

julia> a
1

julia> b
3

Julia 1.6以降は、左辺の最後の要素に...をつけると、残りの要素が全て代入される。

julia> a, b... = (1, 2, 3, 4)
(1, 2, 3, 4)

julia> a
1

julia> b
(2, 3, 4)

julia> a, b... = [1, 2, 3, 4]
4-element Vector{Int64}:
 1
 2
 3
 4

julia> a
1

julia> b
3-element Vector{Int64}:
 2
 3
 4

julia> a, b... = 1:4
1:4

julia> a
1

julia> b
3-element Vector{Int64}:
 2
 3
 4

julia> a, b, c... = (1, 2)
(1, 2)

julia> c
()

julia> a, b, c... = [1, 2]
2-element Vector{Int64}:
 1
 2

julia> c
Int64[]

julia> a, b... = [1 2 3; 4 5 6; 7 8 9]
3×3 Matrix{Int64}:
 1  2  3
 4  5  6
 7  8  9

julia> a
1

julia> b
8-element Vector{Int64}:
 4
 7
 2
 5
 8
 3
 6
 9

Julia 1.9からは、...をどの位置でも使えるようになった。

julia> a, b..., c = 1:4
1:4

julia> a
1

julia> b
2-element Vector{Int64}:
 2
 3

julia> c
4

julia> a..., b, c = 1:4
1:4

julia> a
2-element Vector{Int64}:
 1
 2

julia> b
3

julia> c
4

f((x..., y)) = x という記法は面白い。

julia> f((x..., y)) = x
f (generic function with 1 method)

julia> f(1:4)
3-element Vector{Int64}:
 1
 2
 3

# これは以下と同じ
julia> function f(xs)
         x..., y = xs
         x
       end
f (generic function with 1 method)

julia> f(1:4)
3-element Vector{Int64}:
 1
 2
 3

無名関数で、このような記法をするためには、タプルの後ろにカンマが必要。 ((x..., y), ) -> x

名前付きタプルの分割代入は (; )によって行える。

julia> (; b, a) = (a=1, b=2, c=3)
(a = 1, b = 2, c = 3)

julia> a
1

julia> b
2

これらは、関数の第一引数を分割しているが、それとは別で、可変長引数の関数を f(x, y, z...) = x のように定義できる。可変長引数では、...は最後の引数にしか付けられない。

f(x, y=1) = x + yのように、デフォルト値付き引数の関数を定義することができる。

キーワード引数は f(x; y=1)f(x; options...) のように定義できる。

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2