julia> xs = [1, 2, 3, 4, 5] 5-element Vector{Int64}: 1 2 3 4 5 julia> map(x -> x * x, xs) 5-element Vector{Int64}: 1 4 9 16 25 julia> map(xs) do x x * x end 5-element Vector{Int64}: 1 4 9 16 25
julia> foldr((x, y) -> x + y, xs, init=0) 15 julia> foldr(xs, init=0) do x, y x + y end 15
julia> function map2(f, xs, ys) zs = [] for (x, y) = zip(xs, ys) push!(zs, f(x, y)) end zs end map2 (generic function with 1 method) julia> map2([1, 2, 3], [4, 5, 6]) do x, y (x, y) end 3-element Vector{Any}: (1, 4) (2, 5) (3, 6)
julia> mutable struct Foo a b c end julia> function Base.getindex(x::Foo, i::Int) if i == 1 x.a elseif i == 2 x.b elseif i == 3 x.c else throw(BoundsError(x, i)) end end julia> x = Foo(1, 2, 3) Foo(1, 2, 3) julia> x[1] 1 julia> x[2] 2 julia> x[3] 3 julia> x[4] ERROR: BoundsError: attempt to access Foo at index [4] julia> function Base.setindex!(x::Foo, v, i::Int) if i == 1 x.a = v elseif i == 2 x.b = v elseif i == 3 x.c = v else throw(BoundsError(x, i)) end end julia> x Foo(1, 2, 3) julia> x[1] = 10 10 julia> x Foo(10, 2, 3) julia> x[3] = 30 30 julia> x[3] 30 julia> x Foo(10, 2, 30) julia> Base.lastindex(x::Foo) = 3 julia> x[end] 30 julia> x[end - 1] 2
julia> function foo(c::Channel) put!(c, "foo start") for x = 1 : 4 put!(c, x) end put!(c, "foo stop") end foo (generic function with 1 method) julia> c = Channel(foo) Channel{Any}(0) (1 item available) julia> isopen(c) true julia> take!(c) "foo start" julia> take!(c) 1 julia> take!(c) 2 julia> take!(c) 3 julia> take!(c) 4 julia> take!(c) "foo stop" julia> isopen(c) false julia> take!(c) ERROR: InvalidStateException: Channel is closed. julia> for x = Channel(foo) println(x) end foo start 1 2 3 4 foo stop
julia> a = rand(8) 8-element Vector{Float64}: 0.9696171847103765 0.5789186600045584 0.13710042153456103 0.920535882493211 0.697528020292991 0.25024663580419815 0.9450074234009469 0.35114804306391556 julia> c = Channel(ch -> foreach(x -> put!(ch, x), a)) Channel{Any}(0) (1 item available) julia> take!(c) 0.9696171847103765 julia> take!(c) 0.5789186600045584 julia> take!(c) 0.13710042153456103 julia> take!(c) 0.920535882493211 julia> take!(c) 0.697528020292991 julia> take!(c) 0.25024663580419815 julia> take!(c) 0.9450074234009469 julia> take!(c) 0.35114804306391556 julia> include("list.jl") julia> d = LinkedList() () julia> for x = a insert!(d, 0, x) end julia> e = Channel(ch -> foreach(x -> put!(ch, x), d)) Channel{Any}(0) (1 item available) julia> take!(e) 0.35114804306391556 julia> take!(e) 0.9450074234009469 julia> take!(e) 0.25024663580419815 julia> take!(e) 0.697528020292991 julia> take!(e) 0.920535882493211 julia> take!(e) 0.13710042153456103 julia> take!(e) 0.5789186600045584 julia> take!(e) 0.9696171847103765
リスト : コルーチンの簡単な使用例 (co1.jl) function printcode(ch::Channel, code) while true print(code) put!(ch, true) end end function test_a(n) cs = map(x -> Channel(c -> printcode(c, x)), ["h", "e", "y", "!", " "]) for _ = 1 : n, c = cs take!(c) end end # n から始まる整数列 function integers(c, n) while true put!(c, n) n += 1 end end # フィルター function sieve_filter(f, src) Channel(c -> while true n = take!(src) if f(n) put!(c, n) end end) end # エラトステネスの篩 function sieve(x) nums = Channel(c -> integers(c, 2)) for _ = 1 : x n = take!(nums) print("$n ") nums = sieve_filter(x -> x % n != 0, nums) end end
julia> include("col.jl") sieve (generic function with 1 method) julia> test_a(5) hey! hey! hey! hey! hey! hey! julia> test_a(10) hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! julia> sieve(25) 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 julia> sieve(100) 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541
julia> function test(name, w) for _ = 1 : 10 sleep(w) println(name) yield() end end test (generic function with 1 method) julia> a = @task test("foo", 0.3) Task (runnable) @0x000000001051a850 julia> b = @task test("bar", 0.5) Task (runnable) @0x000000001051ab30 julia> c = @task test("oops", 0.7) Task (runnable) @0x000000001051b0f0 julia> istaskstarted(a) false julia> istaskstarted(b) false julia> istaskstarted(c) false julia> schedule(a); schedule(b); schedule(c) Task (runnable) @0x000000001051b0f0 julia> foo bar foo oops foo bar foo oops foo bar foo bar oops foo foo bar foo oops bar foo oops bar bar oops bar oops bar oops oops oops julia> istaskdone(a) true julia> istaskdone(b) true julia> istaskdone(c) true julia> @async test("foo", 0.5) Task (runnable) @0x000000001103e570 julia> foo foo foo foo foo foo foo foo foo foo
julia> ch = Channel(0) Channel{Any}(0) (empty) julia> a = @async foreach(x -> put!(ch, x), 1 : 5) Task (runnable) @0x0000000010aa2e10 julia> while !istaskdone(a) println(take!(ch)) end 1 2 3 4 5 julia> isopen(ch) true julia> close(ch) julia> ch = Channel(0) Channel{Any}(0) (empty) julia> a = @async foreach(x -> put!(ch, x), 1 : 5) Task (runnable) @0x0000000011081fb0 julia> bind(ch, a) Channel{Any}(0) (1 item available) julia> for x = ch println(x) end 1 2 3 4 5 julia> istaskdone(a) true julia> isopen(ch) false
julia> c = current_task() Task (runnable) @0x000000000eac5150 julia> a = Task(() -> foreach(x -> println(yieldto(c, x)), 1 : 5)) Task (runnable) @0x000000000eac45d0 julia> yieldto(a, 10) 1 julia> yieldto(a, 11) 11 2 julia> yieldto(a, 12) 12 3 julia> yieldto(a, 13) 13 4 julia> yieldto(a, 14) 14 5 julia> yieldto(a, 15) 15 フリーズする (CTRL-C で中断する)
リスト : yieldto() の簡単な使用例 (co1.jl に追加) function printcode1(next::Task, code) while true print(code) yieldto(next) end end function test_b(n) ch = current_task() a = @task printcode1(ch, " ") b = @task printcode1(a, "!") c = @task printcode1(b, "y") d = @task printcode1(c, "e") e = @task printcode1(d, "h") for _ = 1 : n yieldto(e) end end
julia> test_b(5) hey! hey! hey! hey! hey! julia> test_b(20) hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey! hey!
# # tree.jl : 二分探索木 (mutable) # # Copyright (C) 2016-2021 Makoto Hiroi # # 空の木 は Nothing (nothing) で表す # 節 mutable struct Node item left::Union{Node, Nothing} right::Union{Node, Nothing} end # 型に別名を付ける Tree = Union{Node, Nothing} # 二分木 mutable struct Tree2 root::Tree Tree2() = new(nothing) end # # 作業用 # # 挿入 function insert_node(node::Tree, x) if node === nothing return Node(x, nothing, nothing) elseif x < node.item node.left = insert_node(node.left, x) else node.right = insert_node(node.right, x) end node end # 探索 function search_node(node::Tree, x) while node !== nothing if node.item == x return true elseif x < node.item node = node.left else node = node.right end end false end # 最小値と最大値 function search_min_node(node::Tree) while node.left !== nothing node = node.left end node.item end function search_max_node(node::Tree) while node.right !== nothing node = node.right end node.item end # 最大値と最小値の削除 function delete_min_node(node::Tree) if node.left === nothing node.right else node.left = delete_min_node(node.left) node end end function delete_max_node(node::Tree) if node.right === nothing node.left else node.right = delete_max_node(node.right) node end end # 削除 function delete_node(node::Tree, x) if node === nothing return node end if node.item == x if node.left === nothing return node.right elseif node.right === nothing return node.left else node.item = search_min_node(node.right) node.right = delete_min_node(node.right) end elseif x < node.item node.left = delete_node(node.left, x) else node.right = delete_node(node.right, x) end node end # 巡回 function foreach_node(func::Function, node::Nothing) end function foreach_node(func::Function, node::Node) foreach_node(func, node.left) func(node.item) foreach_node(func, node.right) end # イテレータ用 function next_node(node::Node, buff::Vector{Node}) while node !== nothing push!(buff, node) node = node.left end end # # 公開用 # # 挿入 function Base.insert!(tree::Tree2, x) tree.root = insert_node(tree.root, x) end # 探索 function search(tree::Tree2, x) search_node(tree.root, x) end # 最大値と最小値 function search_max(tree::Tree2) if tree.root === nothing error("search_max: empty tree") else search_max_node(tree.root) end end function search_min(tree::Tree2) if tree.root === nothing error("search_min: empty tree") else search_min_node(tree.root) end end # 削除 function Base.delete!(tree::Tree2, x) tree.root = delete_node(tree.root, x) end function delete_max!(tree::Tree2) if tree.root !== nothing tree.root = delete_max_node(tree.root) end end function delete_min!(tree::Tree2) if tree.root !== nothing tree.root = delete_min_node(tree.root) end end # 巡回 function Base.foreach(func::Function, tree::Tree2) foreach_node(func, tree.root) end # 空の木か? Base.isempty(tree::Tree2) = tree.root === nothing # イテレータ function Base.iterate(tree::Tree2) buff = Node[] if tree.root === nothing nothing else next_node(tree.root, buff) (buff[end].item, buff) end end function Base.iterate(tree::Tree2, state) if isempty(state) nothing else node = pop!(state) if node.right !== nothing next_node(node.right, state) end if isempty(state) nothing else (state[end].item, state) end end end # 表示 function Base.show(io::IO, xs::Tree2) print(io, "Tree2: ") for x = xs print("$x ") end end # 簡単なテスト a = Tree2() println(a) println(isempty(a)) # 挿入 for x = [5, 3, 7, 1, 4, 2, 9, 6, 8] print("insert $x -> ") insert!(a, x) println(a) end # 巡回 foreach(println, a) # 検索 for x = 0 : 10 print("search $x -> ") println(search(a, x)) end println("min: ", search_min(a)) println("max: ", search_max(a)) # 削除 print("delete max -> ") delete_max!(a) println(a) print("delete min -> ") delete_min!(a) println(a) for x = 1 : 9 print("delete $x -> ") delete!(a, x) println(a) end println(a) println(isempty(a))
$ julia tree.jl Tree2: true insert 5 -> Tree2: 5 insert 3 -> Tree2: 3 5 insert 7 -> Tree2: 3 5 7 insert 1 -> Tree2: 1 3 5 7 insert 4 -> Tree2: 1 3 4 5 7 insert 2 -> Tree2: 1 2 3 4 5 7 insert 9 -> Tree2: 1 2 3 4 5 7 9 insert 6 -> Tree2: 1 2 3 4 5 6 7 9 insert 8 -> Tree2: 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 search 0 -> false search 1 -> true search 2 -> true search 3 -> true search 4 -> true search 5 -> true search 6 -> true search 7 -> true search 8 -> true search 9 -> true search 10 -> false min: 1 max: 9 delete max -> Tree2: 1 2 3 4 5 6 7 8 delete min -> Tree2: 2 3 4 5 6 7 8 delete 1 -> Tree2: 2 3 4 5 6 7 8 delete 2 -> Tree2: 3 4 5 6 7 8 delete 3 -> Tree2: 4 5 6 7 8 delete 4 -> Tree2: 5 6 7 8 delete 5 -> Tree2: 6 7 8 delete 6 -> Tree2: 7 8 delete 7 -> Tree2: 8 delete 8 -> Tree2: delete 9 -> Tree2: Tree2: true
# # imtree.jl : immutable な二分木 # # Copyright (C) 2016-2021 Makoto Hiroi # abstract type Tree end # 空の木 struct Nil <: Tree end # 節 struct Node <: Tree item left::Tree right::Tree end const nil = Nil() # 空の木か? Base.isempty(node::Tree) = node === nil # 探索 function search_tree(node::Tree, x) while !isempty(node) if node.item == x return true elseif x < node.item node = node.left else node = node.right end end return false end # 最小値の探索 function search_min(node::Node) while !isempty(node.left) node = node.left end node.item end # 最大値の探索 function search_max(node::Node) while !isempty(node.right) node = node.right end node.item end # 挿入 function insert_tree(node::Tree, x) if isempty(node) Node(x, nil, nil) elseif x == node.item node elseif x < node.item Node(node.item, insert_tree(node.left, x), node.right) else Node(node.item, node.left, insert_tree(node.right, x)) end end # 最小値の削除 function delete_min(node::Node) if isempty(node.left) node.right else Node(node.item, delete_min(node.left), node.right) end end # 最大値の削除 function delete_max(node::Node) if isempty(node.right) node.left else Node(node.item, node.left, delete_max(node.right)) end end # 削除 function delete_tree(node::Tree, x) if isempty(node) nil elseif node.item == x if node.left == nil; return node.right; end if node.right == nil; return node.left; end Node(search_min(node.right), node.left, delete_min(node.right)) elseif x < node.item Node(node.item, delete_tree(node.left, x), node.right) else Node(node.item, node.left, delete_tree(node.right, x)) end end # 木の巡回 function Base.foreach(f, node::Tree) if !isempty(node) foreach(f, node.left) f(node.item) foreach(f, node.right) end end # イテレータ function next_node(node::Tree, buff::Vector{Tree}) while !isempty(node) push!(buff, node) node = node.left end end function Base.iterate(node::Tree) buff = Tree[] if node == nil nothing else next_node(node, buff) (buff[end].item, buff) end end function Base.iterate(tree::Tree, state) if isempty(state) nothing else node = pop!(state) if !isempty(node.right) next_node(node.right, state) end if isempty(state) nothing else (state[end].item, state) end end end # 表示 function Base.show(io::IO, xs::Tree) print(io, "Tree: ") for x = xs print("$x ") end end # # 簡単なテスト # a = nil println(a) println(isempty(a)) # 挿入 for x = [5, 3, 7, 1, 4, 2, 9, 6, 8] print("insert $x -> ") global a = insert_tree(a, x) println(a) end # 巡回 foreach(println, a) # 検索 for x = 0 : 10 print("search $x -> ") println(search_tree(a, x)) end println("min: ", search_min(a)) println("max: ", search_max(a)) # 削除 print("delete max -> ") a = delete_max(a) println(a) print("delete min -> ") a = delete_min(a) println(a) for x = 1 : 9 print("delete $x -> ") global a = delete_tree(a, x) println(a) end println(a) println(isempty(a))
$ julia imtree.jl Tree: true insert 5 -> Tree: 5 insert 3 -> Tree: 3 5 insert 7 -> Tree: 3 5 7 insert 1 -> Tree: 1 3 5 7 insert 4 -> Tree: 1 3 4 5 7 insert 2 -> Tree: 1 2 3 4 5 7 insert 9 -> Tree: 1 2 3 4 5 7 9 insert 6 -> Tree: 1 2 3 4 5 6 7 9 insert 8 -> Tree: 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 search 0 -> false search 1 -> true search 2 -> true search 3 -> true search 4 -> true search 5 -> true search 6 -> true search 7 -> true search 8 -> true search 9 -> true search 10 -> false min: 1 max: 9 delete max -> Tree: 1 2 3 4 5 6 7 8 delete min -> Tree: 2 3 4 5 6 7 8 delete 1 -> Tree: 2 3 4 5 6 7 8 delete 2 -> Tree: 3 4 5 6 7 8 delete 3 -> Tree: 4 5 6 7 8 delete 4 -> Tree: 5 6 7 8 delete 5 -> Tree: 6 7 8 delete 6 -> Tree: 7 8 delete 7 -> Tree: 8 delete 8 -> Tree: delete 9 -> Tree: Tree: true
カッコ列は ( と ) からなる列のことで、バランスが取れているカッコ列は、右カッコで閉じることができる、つまり右カッコに対応する左カッコがある状態のことをいいます。たとえば n = 1 の場合、( ) はバランスの取れたカッコ列ですが、) ( はバランスが取れていません。今回はカッコ列を生成する高階関数 kakko() を作ります。
リスト : カッコ列 function kakko(f, n) function iter(x, y, a) if x == y == n f(a) else if x < n iter(x + 1, y, a * "(") end if y < x iter(x, y + 1, a * ")") end end end iter(0, 0, "") end kakko(println, 3) kakko(println, 4)
カッコ列の生成は簡単です。局所関数 iter() の引数 x が左カッコの個数、引数 y が右カッコの個数を表します。引数 a は累積変数でカッコ列を表す文字列です。
バランスの取れたカッコ列の場合、x, y, n には y <= x <= n の関係が成り立ちます。x == y == n の場合、カッコ列がひとつ完成しました。引数の関数 f を呼び出します。そうでなければ、iter() を再帰呼び出しします。x < n であれば左カッコを追加し、y < x であれば右カッコを追加します。これでカッコ列を生成することができます。
((())) (()()) (())() ()(()) ()()() (((()))) ((()())) ((())()) ((()))() (()(())) (()()()) (()())() (())(()) (())()() ()((())) ()(()()) ()(())() ()()(()) ()()()()
カタラン数 - Wikipedia によると、カッコ列の総数は「カタラン数 (Catalan number)」になるとのことです。カタラン数 \(\mathrm{C}_n\)は次に示す公式で求めることができます。
これをそのままプログラムしてもいいのですが、それではちょっと面白くないので別な方法でプログラムを作ってみましょう。カタラン数は次に示す経路図において、A から B までの最短距離の道順を求めるとき、対角線を超えないものの総数に一致します。
B B ┌─┬─┬─┬─┐ ┌─┬─┬─0─14 │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┤ ├─┼─0─5─14 │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┤ ├─0─2─5─9 │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┤ 0─1─2─3─4 │ │ │ │ │ │ │ │ │ │ └─┴─┴─┴─┘ 1─1─1─1─1 A A 図 : 道順の総数の求め方
A からある地点にいたる最短距離の道順の総数は、左隣と真下の地点の値を足したものになります。一番下の地点は 1 で、対角線を越えた地点は 0 になります。あとは下から順番に足し算していけば、A から B までの道順の総数を求めることができます。上図の場合はカラタン数 C4 に相当し、その値は 14 となります。
プログラムは配列を使うと簡単です。次の図を見てください。
0 : [1, 1, 1, 1, 1] 1 : [1, 1, 1, 1, 1,] 2 : [1, 1, 1+1=2, 2+1=3, 3+1=4] => [1, 1, 2, 3, 4] 3 : [1, 1, 2, 3+2=5, 5+4=9] => [1, 1, 2, 5, 9] 4 : [1, 1, 2, 5, 5+9=14] => [1, 1, 2, 5, 14]
上図は Cn (n = 4) を求める場合です。大きさが n + 1, 要素の値が 1 のベクタを用意します。n = 0, 1 の場合は n 番目の要素をそのまま返します。n が 2 よりも大きい場合、変数 i を 2 に初期化して、i - 1 番目以降の要素の累積和を求めます。
たとえば i = 2 の場合、2 番目の要素は 1 番目の要素と自分自身を加算した値 2 になります。3 番目の要素は 2 番目の要素と自分自身を足した値 3 になり、4 番目の要素は 3 + 1 = 4 になります。次に i を +1 して同じことを繰り返します。3 番目の要素は 2 + 3 = 5 になり、4 番目の要素は 5 + 4 = 9 になります。i = 4 のとき、4 番目の要素は 5 + 9 = 14 となり、C4 の値を求めることができました。
リスト : カッコ列の総数 function kakko_num(n) table = fill(big(1), n) for i = 2 : n, j = i : n table[j] += table[j - 1] end table[n] end for i = 1 : 10 println(kakko_num(i)) end println(kakko_num(50)) println(kakko_num(100))
1 2 5 14 42 132 429 1430 4862 16796 1978261657756160653623774456 896519947090131496687170070074100632420837521538745909320
バランスの取れたカッコ列と二分木は 1 対 1 に対応します。二分木を行きがけ順で巡回するとき、途中の節では左カッコ ( を出力して左右の枝をたどり、葉に到達したら右カッコ ) を出力すると、カッコ列を生成することができます。
リスト : カッコ列と二分木 # 二分木 abstract type Tree end # 節 struct Node <: Tree left::Tree right::Tree end # 葉 struct Leaf <: Tree end # 二分木をカッコ列に変換 function tree_to_kakko(tree::Tree) function iter(tree::Tree) if typeof(tree) == Leaf ")" else "(" * iter(tree.left) * iter(tree.right) end end s = iter(tree) s[1 : end - 1] end # カッコ列を二分木に変換 function kakko_to_tree(s) function iter(i) if length(s) < i (Leaf(), i) elseif s[i] == ')' (Leaf(), i + 1) elseif s[i] == '(' l, j = iter(i + 1) r, k = iter(j) (Node(l, r), k) else error("illegal char") end end iter(1)[1] end function test(s) println(s) t = kakko_to_tree(s) println(t) u = tree_to_kakko(t) println(u) end kakko(test, 3)
関数 tree_to_kakko() の実際の処理は局所関数 iter() で行います。引数が葉 (Leaf) の場合は右カッコを返します。引数が節 (Node) の場合は、iter() を再帰呼び出しして左部分木 left をたどり、それから右部分木 right をたどります。その結果と左カッコを演算子 * で連結すればいいわけです。ただし、最後に余分な右カッコが付いてくるので、最後の要素を削除します。
二分木の場合、葉 (Leaf) の個数を n とすると、節 (Node) の個数は n - 1 になります。カッコ列と違って Leaf の個数が一つ多くなることに注意してください。
関数 kakko_to_tree() も局所関数 iter() で変換処理を行います。文字列 s の i 番目の要素が右カッコの場合は (Leaf(), i + 1) を返します。左カッコの場合は、iter() を再帰呼び出しして左部分木 l を生成し、それから右部分木 r を生成します。あとは (Node(l, r), k) を返すだけです。ただし、葉に対応する右カッコがひとつ少ないので、引数 i が文字列の終端に到達した場合は Leaf() を返すようにします。
((())) Node(Node(Node(Leaf(),Leaf()),Leaf()),Leaf()) ((())) (()()) Node(Node(Leaf(),Node(Leaf(),Leaf())),Leaf()) (()()) (())() Node(Node(Leaf(),Leaf()),Node(Leaf(),Leaf())) (())() ()(()) Node(Leaf(),Node(Node(Leaf(),Leaf()),Leaf())) ()(()) ()()() Node(Leaf(),Node(Leaf(),Node(Leaf(),Leaf()))) ()()()