サンプルコード

このページでは「Juliaプログラミング大全」中のサンプルコードを掲載しています。

ファイルに特別の記載がない限りはパブリック・ドメインです。

サンプルコードのファイルは次のリンクで一括ダウンロードできます。

1章

insertionsort.jl

# 挿入ソート
function insertionsort(xs)
    xs = copy(xs)
    for i in 2:lastindex(xs)
        x = xs[i]
        while i > 1 && xs[i-1] > x
            xs[i] = xs[i-1]
            i -= 1
        end
        xs[i] = x
    end
    return xs
end

nq/nqueens.jl

#!/usr/bin/env julia

# 列fileにクイーンを置き、再帰的に解を数え上げる
function solve(ranks, file)
    n = length(ranks)
    nsols = 0  # 解の数
    for rank in 1:n
        for i in 1:file-1
            @inbounds r = rank - ranks[i]
            if r == 0 || file - i == abs(r)
                # 横か斜めにクイーンがあるので次の行に移る
                @goto next
            end
        end
        # (file, rank)にクイーンを置く
        ranks[file] = rank
        # 次の列に移る(既に最後の列なら解を1つ発見)
        nsols += file < n ? solve(ranks, file + 1) : 1
        @label next
    end
    return nsols
end

# nクイーン問題の解を数え上げる
nqueens(n) = n  0 ? 0 : solve(zeros(Int, n), 1)

# コマンドラインから呼び出されたときのエントリーポイント
if abspath(PROGRAM_FILE) == @__FILE__
    let
        n = parse(Int, ARGS[1])
        println(n, ": ", nqueens(n))
    end
end

pca.jl

# 標準ライブラリの読込み
using LinearAlgebra
using Statistics

# 主成分分析(データXは各列が観測値の行列)
function pca(X)
    # 中心化(各行の平均値を差し引く)
    X = X .- mean(X, dims = 2)
    # 特異値分解
    U, = svd(X)
    # 射影(i行目が第i主成分に対応)
    return U'X
end

2章

hello.jl

#!/usr/bin/env julia
println("hello, world")
println("こんにちは、世界")

helloargs.jl

#!/usr/bin/env julia
name1 = ARGS[1]
name2 = ARGS[2]
println("こんにちは、$(name1)さんと$(name2)さん")

3章

isprime.jl

# nが素数かを判定
function isprime(n)
    for i in 2:isqrt(n)
        if n % i == 0
            # 約数が見つかったので素数ではない
            return false
        end
    end
    # 約数がなかったので素数である
    return true
end

primes.jl

# n以下の素数を返す
function primes(n)
    # 素数の候補を配列で保持
    list = trues(n)
    # 1は素数ではないので削除
    list[1] = false
    # 最初の素数
    p = 2
    while p  isqrt(n)
        # 2p, 3p, … は素数ではないので削除
        for i in 2p:p:n
            list[i] = false
        end
        #@show p bitstring(list)  # 変数の内容を表示する
        # 候補から最小の自然数(素数)を選ぶ
        p = findnext(list, p + 1)
    end
    # 残った素数の候補を返す
    return findall(list)
end

Othello.jl

# Othelloモジュールの定義開始
module Othello

# インターフェース(モジュール外から使う型や関数)
export User, RandomAI, play

# オセロ石
# -------

# 石は文字で表現
const Disk = Char
const DISK_EMPTY = '\u22c5'  # 空き '⋅'
const DISK_BLACK = '\u25cf'  # 黒石 '●'
const DISK_WHITE = '\u25cb'  # 白石 '○'

# 石を判定する便利関数
isblack(disk::Disk) = disk == DISK_BLACK
iswhite(disk::Disk) = disk == DISK_WHITE

# 石をひっくり返す関数
flip(disk::Disk) =
    isblack(disk) ? DISK_WHITE :
    iswhite(disk) ? DISK_BLACK :
    throw(ArgumentError("not flippable"))


# 盤上の位置
# --------

const Position = Tuple{Char, Int}  # 位置をタプルで表現
const N = 8                        # 盤の1辺のサイズ
const ROWS = 1:N                   # 行の範囲
const COLS = 'a':'a'+N-1           # 列の範囲

# 位置posを行列のインデックスに変換
pos2idx(pos::Position) = (pos[2], pos[1] - 'a' + 1)

# 位置posが盤上にあるかを判定
isonboard(pos::Position) = pos[2] in ROWS && pos[1] in COLS


# オセロ盤
# -------

# オセロ盤のデータ型
struct Board
    data::Matrix{Disk}  # 石の行列(2次元配列)でオセロ盤を表現
end

# オセロ盤を作るコンストラクタ
function Board()
    board = Board(fill(DISK_EMPTY, (N, N)))
    # 石の初期配置
    board[('d', 5)] = board[('e', 4)] = DISK_BLACK
    board[('d', 4)] = board[('e', 5)] = DISK_WHITE
    return board
end

# オセロ盤の石を見る操作
Base.getindex(board::Board, pos::Position) =
    board.data[pos2idx(pos)...]

# オセロ盤に石を置く操作
function Base.setindex!(board::Board, disk::Disk, pos::Position)
    board.data[pos2idx(pos)...] = disk
    return board
end

# オセロ盤の表示
function Base.show(output::IO, board::Board)
    disks = countdisks(board)
    print(output, "Board with $(disks.black) blacks")
    print(output, " and $(disks.white) whites:\n")
    print(output, "  ")
    for c in COLS
        print(output, ' ', c)  # 列の英字表示
    end
    for r in ROWS
        print(output, "\n ", r)  # 行の数字表示
        for c in COLS
            print(output, ' ', board[(c, r)])
        end
    end
end

# 盤上にある石のカウント
function countdisks(board::Board)
    black = white = 0
    for c in COLS, r in ROWS
        disk = board[(c, r)]
        if isblack(disk)
            black += 1
        elseif iswhite(disk)
            white += 1
        end
    end
    # 名前付きタプルを返す
    return (black = black, white = white)
end


# 石の打ち方
# --------

# 方向dirに対してひっくり返せる石を探索
function flips(board::Board, disk::Disk, pos::Position, dir::NTuple{2, Int})
    dir == (0, 0) && return nothing
    nflips = 0  # ひっくり返せる石の数
    # 自分の石を探索
    next = pos .+ dir
    while isonboard(next) && board[next] == flip(disk)
        nflips += 1
        next = next .+ dir
    end
    # 相手の石を挟めているかをチェック
    if nflips > 0 && isonboard(next) && board[next] == disk
        # ペアになる石の位置を返す
        return next
    else
        return nothing
    end
end

# 有効手か判定
function isvalidmove(board::Board, disk::Disk, pos::Position)
    isonboard(pos) &&
    !isblack(board[pos]) &&
    !iswhite(board[pos]) || return false
    dirs = ((u, v) for u in -1:1 for v in -1:1 if !(u == v == 0))
    return !all(isnothing, flips(board, disk, pos, dir) for dir in dirs)
end

# 石diskを位置posに置いたときの盤面boardの更新
function move!(board::Board, disk::Disk, pos::Position)
    isvalidmove(board, disk, pos) || throw(ArgumentError("invalid move"))
    board[pos] = disk
    for u in -1:1, v in -1:1
        dir = (u, v)  # ひっくり返す方向
        pair = flips(board, disk, pos, dir)
        isnothing(pair) && continue  # ひっくり返す石がなければスキップ
        next = pos .+ dir
        while next != pair
            board[next] = disk
            next = next .+ dir
        end
    end
    return board
end


# プレイヤー
# ---------

# 抽象的なプレイヤーの型
abstract type Player end

# ユーザの型
"""
An interactive human player.
"""
struct User <: Player
    name::String
end

# プレイヤー名を取得
name(user::User) = user.name

# ユーザに次の手を入力させる
function move(::User, ::Disk, ::Board)
    while true
        input = prompt("move> ")
        if isnothing(input) || lowercase(input) == "resign"
            return nothing
        elseif contains(input, r"^[a-h][1-8]$"i)
            c = lowercase(input[1])
            r = input[2] - '0'
            return (c, r)
        end
        println("Invalid input; try again (e.g. f5).")
    end
end

# プロンプトを表示して入力させる
function prompt(msg::String)
    print(msg)
    input = readline(stdin)
    return isopen(stdin) ? strip(input) : nothing
end

# 人工知能の型
"""
A foolish AI player playing random moves.
"""
struct RandomAI <: Player
    name::String
end

# プレイヤー名を取得
name(ai::RandomAI) = ai.name

# 人工知能に次の手を決めさせる
function move(::RandomAI, disk::Disk, board::Board)
    moves = Position[]
    for c in COLS, r in ROWS
        pos = (c, r)
        if isvalidmove(board, disk, pos)
            push!(moves, pos)
        end
    end
    return rand(moves)
end


# ゲーム
# -----

# ゲームの結果
struct Result
    board::Board                    # 終局盤面
    black::Player                   # 黒石を持ったプレイヤー 
    white::Player                   # 白石を持ったプレイヤー
    resigned::Union{Disk, Nothing}  # 投了した石の色
end

# 出力に使うマクロの読込み
using Printf: @printf

# 結果の表示
function Base.show(output::IO, result::Result)
    report(disk, count, player) =
        @printf output "%s×%2d %s\n" disk count name(player)
    disks = countdisks(result.board)
    report(DISK_BLACK, disks.black, result.black)
    report(DISK_WHITE, disks.white, result.white)
    resigned = result.resigned
    if isnothing(resigned)
        x = cmp(disks.black, disks.white)
        msg = x < 0 ? "white winned" :
              x > 0 ? "black winned" : "draw"
    else
        @assert isblack(resigned) || iswhite(resigned)
        color = isblack(resigned) ? "black" : "white"
        msg = "$(color) resigned"
    end
    print(output, msg)
end

# ゲームを開始する関数
"""
    play(; black, white)

Start playing a game.

# Arguments
- `black`:
    a player making the first move.
    `User` with a random name is the default.
- `white`:
    a player making the second move.
    `RandomAI` with a random name is the default.
"""
function play(;
    black::Player = User(randname(DISK_BLACK)),
    white::Player = RandomAI(randname(DISK_WHITE)),
)
    print("$(DISK_BLACK) $(name(black)) vs ")
    print("$(DISK_WHITE) $(name(white))\n")
    board = Board()  # 初期盤面の作成
    nextdisk = alternatedisks(board)  # 次に打つ石を決める関数
    while true
        println('\n', board)
        disk = nextdisk()  # 次の石を取得
        if isnothing(disk)
            # 双方次手なし
            return Result(board, black, white, nothing)
        end
        player = isblack(disk) ? black : white
        playername = name(player)
        println("$(disk) $(playername)'s turn")
        pos = getmove(player, disk, board)
        if isnothing(pos)
            # 投了
            println(playername, " has resigned.")
            return Result(board, black, white, disk)
        else
            @assert isvalidmove(board, disk, pos)
            move!(board, disk, pos)  # 石を設置
            println("$(playername) placed at $(pos[1])$(pos[2]).")
        end
    end
end

# プレイヤーの手を決定
function getmove(player::Player, disk::Disk, board::Board)
    while true
        pos = move(player, disk, board)
        if isnothing(pos) || isvalidmove(board, disk, pos)
            # 投了または有効手
            return pos
        end
        # 無効手(選び直し)
        println("You cannot move that position.")
    end
end

# プレイヤー名を生成
const BLACK_NAMES = ("Panther", "Hawk", "Stingray")
const WHITE_NAMES = ("Tiger", "Parrot", "Shark")
randname(disk::Disk) =
    isblack(disk) ? "Black $(rand(BLACK_NAMES))" :
    iswhite(disk) ? "White $(rand(WHITE_NAMES))" :
    throw(ArgumentError("invalid disk"))

# 「次の石を選ぶ関数(クロージャ)」を返す関数
function alternatedisks(board::Board)
    next = DISK_BLACK  # 初手は黒石
    canmove() =  # nextに打つ手があるかを判定
        any(isvalidmove(board, next, (c, r))
            for c in COLS, r in ROWS)
    return function ()  # クロージャを返す
        if !canmove()
            next = flip(next)
            canmove() || return nothing
        end
        disk = next
        next = flip(next)  # 次の石に状態を更新
        return disk
    end
end

end  # Othelloモジュールの定義終了

6章

printchars.jl

function printchars(s)
    fi, li = firstindex(s), lastindex(s)
    i = fi
    while i  li
        i > fi && print('/')
        print(s[i])
        i = nextind(s, i)
    end
end

7章

lastitem.jl

function lastitem(xs)
    local last
    for x in xs
        last = x
    end
    return @isdefined(last) ? last : nothing
end

fib.jl

# 通常の関数定義
function fib(n)
    if n  2
        return 1
    else
        return fib(n - 1) + fib(n - 2)
    end
end

# 1行関数定義
fib(n) = n  2 ? 1 : fib(n - 1) + fib(n - 2)

min.jl

# 引数の最小値を返す関数
function min(x1, xs...)
    ret = x1
    for x in xs
        if isless(x, ret)
            ret = x
        end
    end
    return ret
end

8章

periodictable.jl

# 元素(chemical element)
struct ChemElem
    number::Int
    symbol::String
end

# 周期表(periodic table)
struct PeriodicTable
    elements::Vector{ChemElem}  # 元素
    groups::Vector{Int}         # 族
    periods::Vector{Int}        # 周期
end

# 簡単のため原子番号1−6のみ
table = PeriodicTable(
    # elements
    [ChemElem(1, "H"), ChemElem(2, "He"), ChemElem(3, "Li"),
     ChemElem(4, "Be"), ChemElem(5, "B"), ChemElem(6, "C")],
    # groups
    [1, 18, 3, 4, 13, 14],
    # periods
    [1, 1, 2, 2, 2, 2]
)

reportfields.jl

# 構造体型の構造を出力
function reportfields(T)
    println(T)
    for i in 1:fieldcount(T)
        name = fieldname(T, i)
        type = fieldtype(T, i)
        println("  [$(i)] $(name) :: $(type)")
    end
end

9章

circle.jl

struct Circle
    r::Float64   # 半径
    cx::Float64  # 中心のx座標
    cy::Float64  # 中心のy座標

    # 内部コンストラクタ
    function Circle(r::Real, cx::Real, cy::Real)
        r  0 || throw(ArgumentError("r must be non-negative"))
        return new(r, cx, cy)
    end
end

rgb.jl

# RGB色モデル(true color)
struct RGB
    r::UInt8  # 赤(red)
    g::UInt8  # 緑(green)
    b::UInt8  # 青(blue)

    # 内部コンストラクタ
    function RGB(r::Integer, g::Integer, b::Integer)
        0  r  255 && 0  g  255 && 0  b  255 ||
        throw(ArgumentError("value must be between 0 and 255"))
        return new(r, g, b)
    end
end

# 無彩色を作る外部コンストラクタ
RGB(x::Integer) = RGB(x, x, x)

normal.jl

# 正規分布(normal distribution)
struct Normal{T <: Real}
    μ::T  # 平均
    σ::T  # 標準偏差

    # 内部コンストラクタ
    function Normal{T}::Real = 0, σ::Real = 1) where T <: Real
        σ > 0 || throw(ArgumentError("σ must be positive"))
        return new{T}(μ, σ)
    end
end

# 外部コンストラクタ
function Normal::Real = 0, σ::Real = 1)
    μ, σ = promote(float(μ), float(σ))
    return Normal{typeof(μ)}(μ, σ)
end

# 確率密度関数
function pdf(dist::Normal, x::Real)
    # Julia 1.7以降では (; μ, σ) = dist も可
    μ, σ = dist.μ, dist.σ
    return* oftype(σ, (2π))) \ exp(-2 \ ((x - μ) / σ)^2)
end

node.jl

# 木構造のノード
mutable struct Node
    index::Int
    parent::Node

    # 内部コンストラクタの定義
    Node(index::Integer) = new(index)
    Node(index::Integer, parent::Node) = new(index, parent)
end

stack.jl

mutable struct Stack{T}
    data::Vector{T}  # 要素
    size::Int        # 要素数

    function Stack{T}(; capacity::Integer = 4) where T
        data = Vector{T}(undef, capacity)
        return new{T}(data, 0)
    end
end

Stack(; capacity::Integer = 4) = Stack{Any}(; capacity)

Base.eltype(::Type{Stack{T}}) where T = T
Base.length(stack::Stack) = stack.size
Base.isempty(stack::Stack) = stack.size == 0

function Base.push!(stack::Stack, item)
    item = convert(eltype(stack), item)
    if stack.size == length(stack.data)
        # stack.dataがいっぱいなら、サイズを2倍に拡大
        resize!(stack.data, max(2 * stack.size, 4))
    end
    newsize = stack.size + 1
    stack.data[newsize] = item
    stack.size = newsize
    return stack
end

function Base.pop!(stack::Stack)
    isempty(stack) && throw(ArgumentError("cannot pop! an empty stack"))
    item = stack.data[stack.size]
    stack.size -= 1
    return item
end

shortstring.jl

# 短い(7バイト以下)ASCII文字列のデータ型
struct ShortString # <: AbstractString
    data::UInt64
end

# コンストラクタ
ShortString(s::String) = convert(ShortString, s)

# String → ShortStringの変換
function Base.convert(::Type{ShortString}, s::String)
    n = ncodeunits(s)
    if n > 7 || !isascii(s) || '\0' in s
        throw(InexactError(:ShortString, ShortString, s))
    end
    data = zero(UInt64)
    for i in n:-1:1
        data = (data << 8) | codeunit(s, i)
    end
    return ShortString(data)
end

# ShortString → Stringの変換
Base.convert(::Type{String}, s::ShortString) = sprint(print, s)

# show関数の拡張
Base.show(out::IO, s::ShortString) = show(out, convert(String, s))

# print関数の拡張
function Base.print(out::IO, s::ShortString)
    data = s.data
    while data & 0xff != 0
        write(out, data % UInt8)
        data >>= 8
    end
end

bitrotator.jl

# ビットを左回転するイテレータ
struct BitRotator{T <: Base.BitInteger}
    bits::T
end

# 初期状態を返す
Base.iterate(rot::BitRotator) = (rot.bits, 1)

# 次の反復を準備する(状態を表すk引数は初期位置からの距離)
function Base.iterate(rot::BitRotator, k::Int)
    x = rot.bits
    nbits = sizeof(x) * 8
    if k  nbits
        return nothing
    end
    return bitrotate(x, k), k + 1
end

# イテレータの長さ(要素数)
Base.length(rot::BitRotator) = sizeof(rot.bits) * 8

composition.jl

# コンポジション型
struct Composition{T <: Real} <: AbstractVector{T}
    data::Vector{T}

    function Composition(x)
        c = x ./ sum(x)
        return new{eltype(c)}(c)
    end
end

# スカラー倍
Base.:*::Real, x::Composition) = Composition(x .^ α)
Base.:*(x::Composition, α::Real) = Composition(x .^ α)

# ベクトル和
Base.:+(x::Composition, y::Composition) = Composition(x .* y)

# ベクトル和の逆元とベクトル差
Base.:-(x::Composition) = Composition(inv.(x))
Base.:-(x::Composition, y::Composition) = x + (-y)

Base.size(x::Composition) = size(x.data)
Base.getindex(x::Composition, i::Integer) = x.data[i]

trait.jl

# トレイトの定義
abstract type Trait end
struct TraitA <: Trait end
struct TraitB <: Trait end

# トレイトを利用する関数
f(x) = _f(Trait(typeof(x)), x)
_f(::TraitA, x) = "Trait A"
_f(::TraitB, x) = "Trait B"

# トレイトを実装する型
struct Foo end
Trait(::Type{Foo}) = TraitA()
struct Bar end
Trait(::Type{Bar}) = TraitB()
struct Baz end
Trait(::Type{Baz}) = TraitB()

10章

msb.jl

# 最上位ビット(most significant bit)を取得する関数
for T in [UInt8, UInt16, UInt32, UInt64, UInt128]
    @eval msb(x::$T) = x >> (sizeof($T) * 8 - 1)
end

check.jl

# 注:この定義は問題あり
macro check(ex)
    code = string(ex)  # 式を文字列化
    quote
        print($code, " → ")
        if $ex
            println("GOOD")
        else
            println("BAD")
        end
    end
end

repeat.jl

macro repeat(n, ex)
    quote
        for _ in 1:$(esc(n))
            $(esc(ex))
        end
    end
end

elapsed.jl

# base/timing.jl (MIT license: https://julialang.org/license)
macro elapsed(ex)
    quote
        local t0 = time_ns()
        $(esc(ex))
        (time_ns() - t0) / 1e9
    end
end

fstring.jl

# f文字列の解析処理
function parse_fstring(str)
    parts = Union{String, Symbol}[]
    pos = firstindex(str)
    # '{'を探す
    while (open = findnext('{', str, pos)) !== nothing
        # '}'を探す
        close = findnext('}', str, open)
        isnothing(close) && break  # ペアになる'}'がない
        # '{'より前までを詰める
        push!(parts, str[pos:prevind(str, open)])
        # '{'と'}'の間の名前を詰める(空文字列ならそのまま)
        name = str[nextind(str, open):prevind(str, close)]
        push!(parts, isempty(name) ? "{}" : Symbol(name))
        # '}'の直後の位置を取得する
        pos = nextind(str, close)
    end
    # 残りの文字列を詰める
    push!(parts, str[pos:end])
    return parts
end

# マクロの定義
macro f_str(str)
    parts = parse_fstring(str)
    # 変数(シンボル)はエスケープする
    parts = map(x -> x isa Symbol ? esc(x) : x, parts)
    return :(string($(parts...)))
end

genfun.jl

@generated function genfun(x)
    if x <: Real
        return :(println("$(repr(x)) is Real"))
    else
        return :(println("$(repr(x)) is not Real"))
    end
end

polynomial.jl

function genpoly(n)
    @assert n  1
    ex = :(a[1])
    for k in 2:n
        ex = :(fma($(ex), x, a[$(k)]))
    end
    return ex
end

@generated polynomial(
    x::Number,
    a::NTuple{n, Number},
) where n = genpoly(n)

11章

mysparse.jl

# sparse関数のように振る舞う関数
function mysparse(I, J, V, m = maximum(I), n = maximum(J))
    M = spzeros(m, n)
    for (i, j, v) in zip(I, J, V)
        M[i,j] = v
    end
    return M
end

12章

tarai.jl

# CPUを使い切る処理
function tarai(x, y, z)
    if x > y
        return tarai(
            tarai(x - 1, y, z),
            tarai(y - 1, z, x),
            tarai(z - 1, x, y))
    else
        return y
    end
end

producer-consumer.jl

# チャネルに値を詰める生産者
chan = Channel{Int}() do chan
    for i in 1:10
        put!(chan, i)
    end
end

# チャネルから値を取り出す消費者
@sync for label in 'A':'E'
    @async for data in chan
        println(label, ": ", data)
    end
end

counter-bad.jl

# 誤った実装!
counter = 0
Threads.@threads for i in 1:1_000_000
    global counter
    counter += 1
end
@show counter

mb/mandelbrot.jl

const N = 1000

# マンデルブロ集合に含まれるか計算
function mandelbrot(c::Complex; maxiters = N)
    k = 0
    z = zero(c)
    while k < maxiters && abs2(z)  4
        z = z*z + c
        k += 1
    end
    return maxiters - k  # 0なら収束と判定
end

mb/plot-seq.jl

# 逐次実行版
function plot_seq(x, y; maxiters = 1000)
    m, n = length.((y, x))
    img = zeros(Int16, m, n)
    for j in 1:n, i in 1:m
        c = complex(x[j], y[i])
        img[i,j] = mandelbrot(c; maxiters)
    end
    return img
end

mb/plot-par1.jl

# 並列実行版1
function plot_par1(x, y; maxiters = N)
    m, n = length.((y, x))
    img = zeros(Int16, m, n)
    Threads.@threads :static for j in 1:n
        for i in 1:m
            c = complex(x[j], y[i])
            img[i,j] = mandelbrot(c; maxiters)
        end
    end
    return img
end

counter-lock.jl

# ロックを使った排他制御
counter = 0
mutex = ReentrantLock()
Threads.@threads for i in 1:1_000_000
    global counter
    lock(mutex)
    # ←ここからクリティカルセクション
    counter += 1
    # ←ここまでクリティカルセクション
    unlock(mutex)
end
@show counter

notification.jl

using .Threads: Condition, @spawn

@sync begin
    cond = Condition()
    ready = false

    # イベント通知をするタスク
    @spawn begin
        sleep(1)
        @lock cond begin
            ready = true
            println("Ready!")
            # イベント通知
            notify(cond)
        end
    end

    # イベント通知を受けるタスク
    for i in 1:8
        @spawn begin
            @lock cond while !ready
                wait(cond)
            end
            # イベント通知後の処理
            print("Go!")
        end
    end
end
println()

counter-atomicadd.jl

# アトミック操作によるカウンター
mutable struct Counter
    @atomic count::Int
end

counter = Counter(0)
Threads.@threads for i in 1:1_000_000
    @atomic counter.count += 1
end
@show counter.count

atomic.jl

# アトミック型
mutable struct Atomic{T}
    @atomic data::T
end

dist/addworkers.jl

using Distributed

let
    workers = [
        split(line, '\t')[1]
        for line in eachline("workers.txt")]
    exename = "/usr/local/julia/bin/julia"
    sshflags = `
        -q
        -i workerkey
        -o StrictHostKeyChecking=no
        -o UserKnownHostsFile=/dev/null`
    addprocs(workers; dir = "/", exename, sshflags)
    @info "Connected to $(length(workers)) workers"
end

13章

walkpwd.jl

# pwd()以下のすべてのディレクトリとファイルを再帰的に探索
for (root, dirs, files) in walkdir(pwd())
    # root直下のディレクトリを列挙
    for dir in dirs
        println("d:", joinpath(root, dir))
    end
    # root直下のファイルを列挙
    for file in files
        println("f:", joinpath(root, file))
    end
end

titlecase.jl

#!/usr/bin/env julia
line = readline(stdin)
println(stdout, titlecase(line))

json.jl

using JSON

# JSON形式の文字列のパース
langs = JSON.parse("""
{
    "julia": {
        "first-release": 2012,
        "creators": [
            "Jeff Bezanson",
            "Stefen Karpinski",
            "Viral Shah"
        ]
    },
    "python": {
        "first-release": 1991,
        "creators": ["Guido van Rossum"]
    }
}
""")

# JuliaのオブジェクトからJSON形式の文字列への変換
JSON.json(langs)

logging.jl

using Logging

# ファイルに出力するロガー
file = open("messages.log", "w")
logger = ConsoleLogger(file, Logging.Debug)
with_logger(logger) do
    @info "Info message"
    @debug "Debug message"
end
close(file)

14章

toplogger.jl

# topコマンドの1行目をlog.txtファイルに追記
toplogger = pipeline(
    pipeline(`top -b`, stdout = `head -1`),
    stdout = "log.txt",
    append = true,
)

# 1秒間隔で10回実行
for _ in 1:10
    run(toplogger)
    sleep(1)
end

15章

voyager.c

#include <stdio.h>

const char* message =
    "Hello from the children of planet Earth.";

void say_hello(void)
{
    printf("%s\n", message);
}

memcmp.jl

# s1とs2のメモリ比較(o1とo2はオフセット)
function memcmp(x1, o1, x2, o2)
    n = min(sizeof(x1) - o1, sizeof(x2) - o2)
    GC.@preserve x1 x2 begin
        s1 = pointer(x1) + o1
        s2 = pointer(x2) + o2
        # int memcmp(void *s1, void *s2, size_t n);
        return ccall(
            :memcmp,
            Cint,
            (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t),
            s1, s2, n)
    end
end

sort.c

#include <stddef.h>

void sort(double *xs, size_t n)
{
    for (size_t i = 0; i < n; i++)
        for (size_t j = 0; j < n; j++)
            if (xs[i] < xs[j]) {
                // swap x[i] and x[j]
                double tmp = xs[i];
                xs[i] = xs[j];
                xs[j] = tmp;
            }
}

textwrap.jl

using PyCall

# モジュールの読込みと関数定義
py"""
import textwrap

# wraptextはテキストを適当なところで折り返す
def wraptext(text, width=50):
    return "\n".join(textwrap.wrap(text, width=width))
"""

# 関数の取得
wraptest = py"wraptext"

# 実行例
longtext = "Julia was designed from the beginning for high performance. Julia programs compile to efficient native code for multiple platforms via LLVM."
println(wraptest(longtext))
## Julia was designed from the beginning for high
## performance. Julia programs compile to efficient
## native code for multiple platforms via LLVM.

16章

myproject/hello.jl

using Example: hello

println(hello(ARGS[1]))

pkgtemplate.jl

using PkgTemplates

template = Template(
    # ~/workspace以下にパッケージを生成
    dir = "~/workspace",
    # Julia 1.6以降をサポート
    julia = v"1.6",
    # プラグインの設定
    plugins = [
        # テスト固有の環境を有効化
        Tests(project = true),
        # 自動化にGitHub Actionsを使用
        GitHubActions(),
        # カバレッジ報告にCodecovを使用
        Codecov(),
        # ドキュメント生成にDocumenter.jlを使用
        Documenter{GitHubActions}(),
    ]
)

Othello/src/Othello.jl

# Othelloモジュールの定義開始
module Othello

# インターフェース(モジュール外から使う型や関数)
export User, RandomAI, play

include("disk.jl")
include("position.jl")
include("board.jl")
include("player.jl")
include("game.jl")

end  # Othelloモジュールの定義終了

Othello/test/runtests.jl

using Othello
using Test

# 何度も使う変数は取り込んでおく
using Othello: DISK_EMPTY, DISK_WHITE, DISK_BLACK

# テストケースのグループ化
@testset "Othello.jl" begin
    # flip関数のテスト
    @test Othello.flip(DISK_WHITE) == DISK_BLACK
    @test Othello.flip(DISK_BLACK) == DISK_WHITE
    # ArgumentErrorが発生することをテスト
    @test_throws ArgumentError Othello.flip(DISK_EMPTY)
end

message.jl

"""
A data type to represent a message.
"""
struct Message
    from::String
    content::String
end

"""
    send(msg, to[; anonymous = false])

Send a message `msg` to a person `to`.

It sends the message and returns the number of written bytes.

# Examples
```jldoctest
julia> msg = Message("Alice", "Hi, how have you been?");

julia> send(msg, "Bob")
30
```
"""
function send(
    msg::Message,
    to::AbstractString;
    anonymous::Bool = false,
)
    from = anonymous ? "?" : msg.from
    data = "[$from] $(msg.content)"
    return write(devnull, data)
end

17章

PrimeTools.jl

module PrimeTools

function isprime(n::Integer)
    if n  1
        return false
    end
    i = 2
    while i  isqrt(n)
        if n % i == 0
            return false
        end
        i += 1
    end
    return true
end

end  # module

isin.jl

# xがcolに含まれるか判定
function isin(x::T, col::Vector{T}) where T
    for y in col
        y == x && return true
    end
    return false
end

sum-unstable.jl

# 型が安定していないsum関数の実装
function sum_unstable(xs)
    s = 0
    for x in xs
        s += x
    end
    return s
end

loop.jl

function loop(n)
    s = 0.0
    for i in 1:n
        s += sin(i) / i
    end
    return 2s + 1
end

sum-stable.jl

# 型が安定しているsum関数の実装
function sum_stable(xs)
    s = zero(eltype(xs))
    for x in xs
        s += x
    end
    return s
end

18章

calccircle.jl

# 円の直径・円周・面積の計算関数
diameter(r) = 2r
circumference(r) = diameter(r) * π
area(r) = π * r^2

# メイン関数
function main(args)
    r = parse(Float64, args[1])
    println("       radius = $(r)")
    println("     diameter = $(diameter(r))")
    println("circumference = $(circumference(r))")
    println("         area = $(area(r))")
end

# プログラムのエントリーポイント
(abspath(PROGRAM_FILE) == @__FILE__) && main(ARGS)

calccircle-test.jl

using Test

include("circle.jl")

@testset "circle" begin
    r = 2.1
    @test diameter(r)  4.2
    @test circumference(r)  13.194689145077131
    @test area(r)  13.854423602330987
end

sleeper.jl

# sleep関数を呼び出す関数
function sleeper()
    sleep(1)
    for i in 1:15
        sleep(0.1)
    end
    deepsleeper(3)
end

# 再帰関数
function deepsleeper(n)
    if n  1
        sleep(1)
    else
        deepsleeper(n - 1)
    end
end

using Profile
sleeper()           # コンパイル
@profile sleeper()  # プロファイル
Profile.print()     # プロファイル結果の出力

allocator.jl

# メモリを割り当てる関数
function allocator(n)
    zeros(n)
    zeros(n, n)
    zeros(n, n, n)
end

using Profile
allocator(1)                 # コンパイル
Profile.clear_malloc_data()  # リセット
allocator(100)               # プロファイル

vec4.jl

# 長さ4のベクトル
struct Vec4{T}
    x::T
    y::T
    z::T
    w::T
end

# Vec4型のnorm
norm(v::Vec4) =
    sqrt(abs2(v.x) + abs2(v.y) + abs2(v.z) + abs2(v.w))

# Vector型のnorm
function norm(v::AbstractVector)
    s = zero(eltype(v))
    for x in v
        s += abs2(x)
    end
    return sqrt(s)
end

rank1matrix.jl

using LinearAlgebra: norm, normalize, normalize!

# 階数が1以下の行列
struct Rank1Matrix{T} <: AbstractMatrix{T}
    # A = u * v'
    u::Vector{T}
    v::Vector{T}
end

# 基本操作
Base.size(A::Rank1Matrix) = (length(A.u), length(A.v))
Base.getindex(A::Rank1Matrix, i::Integer, j::Integer) =
    A.u[i] * A.v[j]

# Matrix型への変換
Matrix(A::Rank1Matrix) = A.u * A.v'

# 行列ベクトル積
Base.:*(A::Rank1Matrix, x::AbstractVector) = A.u * A.v'x

# 行列積
Base.:*(A::Rank1Matrix, B::Rank1Matrix) =
    Rank1Matrix(A.u * A.v'B.u, B.v)
Base.:*(A::Rank1Matrix, B::AbstractMatrix) =
    Rank1Matrix(A.u, B'A.v)
Base.:*(A::AbstractMatrix, B::Rank1Matrix) =
    Rank1Matrix(A * B.u, B.v)

database.jl

# データベースのレコード
struct Record{T}
    serial::Int
    data::T
end

# データベース
struct Database{T}
    records::Vector{Record{T}}
end

# 空のデータベースのコンストラクタ
Database{T}() where T = Database{T}(Record{T}[])

# データベースに新しいレコードを追加
function Base.push!(db::Database{T}, item) where T
    serial = length(db.records)
    record = Record(serial, convert(T, item))
    push!(db.records, record)
    return db
end

split.jl

# SubString版
function split_sub(isprefix, s)
    pos = findfirst(!isprefix, s)
    pos === nothing && return SubString(s), SubString("")
    return @view(s[begin:prevind(s, pos)]), @view(s[pos:end])
end

# String版
function split_str(isprefix, s)
    pos = findfirst(!isprefix, s)
    pos === nothing && return s, ""
    return s[begin:prevind(s, pos)], s[pos:end]
end

rowmins.jl

# 各行の最小値を計算する関数(行優先)
function rowmins_row(A)
    a = A[:,begin]
    for i in axes(A, 1)
        for j in firstindex(A, 2)+1:lastindex(A, 2)
            @inbounds a[i] = min(A[i,j], a[i])
        end
    end
    return a
end

# 各行の最小値を計算する関数(列優先)
function rowmins_col(A)
    a = A[:,begin]
    for j in firstindex(A, 2)+1:lastindex(A, 2)
        for i in axes(A, 1)
            @inbounds a[i] = min(A[i,j], a[i])
        end
    end
    return a
end

sortmerge.jl

# ソート済の配列A,Bの要素を配列Cに昇順にコピー
function sortmerge!(C, A, B)
    @assert length(A) + length(B) == length(C)
    i, j, k = firstindex.((A, B, C))
    @inbounds while i  lastindex(A) && j  lastindex(B)
        a = A[i]
        b = B[j]
        if isless(a, b)
            C[k] = a
            i += 1
        else
            C[k] = b
            j += 1
        end
        k += 1
    end
    if i  lastindex(A)
        copyto!(C, k, A, i, lastindex(A) - i + 1)
    else
        copyto!(C, k, B, j, lastindex(B) - j + 1)
    end
    return C
end

b64/hex2char.jl

# 6ビットから1文字への変換関数
hex2char(h::UInt8) =
    h < 0x1a ? UInt8('A') + h        :
    h < 0x34 ? UInt8('a') + h - 0x1a :
    h < 0x3e ? UInt8('0') + h - 0x34 :
    h  0x3e ? UInt8('+') : UInt8('/')

b64/encode1.jl

# Base64符号化関数(バージョン1)
function encode1(data::AbstractVector{UInt8})
    str = zeros(UInt8, cld(sizeof(data), 3) * 4)
    i = firstindex(data)
    j = firstindex(str)
    k = 0     # 持ち越されたビットの長さ
    h = 0x00  # 持ち越されたビット
    @inbounds while i  lastindex(data)
        x = data[i]
        str[j] = hex2char(h | x >> (k + 2))
        j += 1
        k += 2; k %= 8
        h = 0x3f & x << (6 - k)
        k  4 && (i += 1)
    end
    # 余ったビットの処理
    k == 0 || (str[j] = hex2char(h); j += 1)
    # パディング処理
    j  lastindex(str) && (str[j] = UInt('='); j += 1)
    j  lastindex(str) && (str[j] = UInt('='); j += 1)
    return str
end

b64/encode2.jl

# Base64符号化関数(バージョン2)
function encode2(data::AbstractVector{UInt8})
    str = zeros(UInt8, cld(sizeof(data), 3) * 4)
    i = firstindex(data)
    j = firstindex(str)
    @inbounds while i + 2  lastindex(data)
        x1, x2, x3 = data[i], data[i+1], data[i+2]
        i += 3
        h1 =                  x1 >> 2
        h2 = x1 << 4 & 0x3f | x2 >> 4
        h3 = x2 << 2 & 0x3f | x3 >> 6
        h4 = x3      & 0x3f
        str[j  ] = hex2char(h1)
        str[j+1] = hex2char(h2)
        str[j+2] = hex2char(h3)
        str[j+3] = hex2char(h4)
        j += 4
    end
    finish!(str, data, lastindex(data) - i + 1)
    return str
end

b64/encode3.jl

# ルックアップテーブル
const ENCODE_TABLE = UInt8['A':'Z'; 'a':'z'; '0':'9'; '+'; '/']

# Base64符号化関数(バージョン3)
function encode3(data::AbstractVector{UInt8})
    str = zeros(UInt8, cld(sizeof(data), 3) * 4)
    i = firstindex(data)
    j = firstindex(str)
    @inbounds while i + 2  lastindex(data)
        x1, x2, x3 = data[i], data[i+1], data[i+2]
        i += 3
        h1 =                  x1 >> 2
        h2 = x1 << 4 & 0x3f | x2 >> 4
        h3 = x2 << 2 & 0x3f | x3 >> 6
        h4 = x3      & 0x3f
        str[j  ] = ENCODE_TABLE[h1+1]
        str[j+1] = ENCODE_TABLE[h2+1]
        str[j+2] = ENCODE_TABLE[h3+1]
        str[j+3] = ENCODE_TABLE[h4+1]
        j += 4
    end
    finish!(str, data, lastindex(data) - i + 1)
    return str
end

mb/plot-par2.jl

# 並列実行版2
function plot_par2(x, y; maxiters = N, blocksize = nothing)
    m, n = length.((y, x))
    if isnothing(blocksize)
        # スレッド数x32個の区画に分割
        p = Threads.nthreads()
        blocksize = cld(n, 32p)
    end
    chan = Channel(spawn = true) do chan
        # y軸と平行に分割
        for J in Iterators.partition(1:n, blocksize)
            put!(chan, J)
        end
    end
    img = zeros(Int16, m, n)
    Threads.foreach(chan) do J
        for j in J, i in 1:m
            c = complex(x[j], y[i])
            img[i,j] = mandelbrot(c; maxiters)
        end
    end
    return img
end

fs/parsum.jl

using .Threads: nthreads, @threads, threadid

function parsum(A, k)
    s = zeros(eltype(A), (nthreads() - 1) << k + 1)
    @threads :static for j in axes(A, 2)
        offset = (threadid() - 1) << k + 1
        # 行列Aのj列目の総和を計算
        @inbounds for i in axes(A, 1)
            s[offset] += A[i,j]
        end
    end
    return sum(s)
end

hist/histogram-serial.jl

using Images

const Img = Array{RGB{N0f8}}

# 逐次実行
function histogram_serial(img::Img)
    hist_r = zeros(Int, 256)
    hist_g = zeros(Int, 256)
    hist_b = zeros(Int, 256)
    @inbounds for i in eachindex(img)
        x = img[i]  # 画素(pixel)
        hist_r[reinterpret(UInt8, x.r)+1] += 1
        hist_g[reinterpret(UInt8, x.g)+1] += 1
        hist_b[reinterpret(UInt8, x.b)+1] += 1
    end
    return hist_r, hist_g, hist_b
end

hist/histogram-lock.jl

using .Threads: @threads, SpinLock

# ロックを使った排他制御
function histogram_lock(img::Img)
    hist_r = zeros(Int, 256)
    hist_g = zeros(Int, 256)
    hist_b = zeros(Int, 256)
    mutex = SpinLock()
    @inbounds @threads for i in eachindex(img)
        x = img[i]  # 画素(pixel)
        lock(mutex)  # クリティカルセクション開始
        hist_r[reinterpret(UInt8, x.r)+1] += 1
        hist_g[reinterpret(UInt8, x.g)+1] += 1
        hist_b[reinterpret(UInt8, x.b)+1] += 1
        unlock(mutex)  # クリティカルセクション終了
    end
    return hist_r, hist_g, hist_b
end

hist/histogram-mapreduce.jl

using .Threads: @spawn, nthreads

# MapReduceパターン
function histogram_mapreduce(img::Img; p = nthreads())
    # Map
    n = cld(length(img), p)
    parts = Iterators.partition(eachindex(img), n)
    tasks = map(parts) do part
        @spawn begin
            hist_r = zeros(Int, 256)
            hist_g = zeros(Int, 256)
            hist_b = zeros(Int, 256)
            @inbounds for i in part
                x = img[i]  # 画素(pixel)
                hist_r[reinterpret(UInt8, x.r)+1] += 1
                hist_g[reinterpret(UInt8, x.g)+1] += 1
                hist_b[reinterpret(UInt8, x.b)+1] += 1
            end
            hist_r, hist_g, hist_b
        end
    end

    # Reduce
    function add!((r1, g1, b1), (r2, g2, b2))
        r1 .+= r2
        g1 .+= g2
        b1 .+= b2
        return r1, g1, b1
    end
    return foldl(add!, fetch(task)::NTuple{3, Vector{Int}} for task in tasks)
end

nq/nqueens-flat.jl

# フラット並列(MapReduce)
function nqueens_flat(n)
    n  1 && return Int(n == 1)
    tasks = map(1:n) do rank
        # (1, j)に最初のクイーンがあるタスクを作成
        @spawn begin
            ranks = zeros(Int, n)
            ranks[1] = rank
            return solve(ranks, 2)
        end
    end
    return sum(fetch(task)::Int for task in tasks)
end

nq/nqueens-nested.jl

# ネスト並列
function nqueens_nested(n)
    n  0 && return 0
    return parsolve(zeros(Int, n), 1)
end

# solve関数の並列版
function parsolve(ranks, file)
    n = length(ranks)
    n - file < 12 && return solve(ranks, file)
    tasks = map(1:n) do rank
        @spawn begin
            for i in 1:file-1
                r = rank - ranks[i]
                if r == 0 || file - i == abs(r)
                    return 0
                end
            end
            let ranks = copy(ranks)
                ranks[file] = rank
                return parsolve(ranks, file + 1)
            end
        end
    end
    return sum(fetch(task)::Int for task in tasks)
end