サンプルコード
このページでは「Juliaプログラミング大全」中のサンプルコードを掲載しています。
ファイルに特別の記載がない限りはパブリック・ドメインです。
サンプルコードのファイルは次のリンクで一括ダウンロードできます。
1章
insertionsort.jl
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
2章
hello.jl
helloargs.jl
3章
isprime.jl
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
7章
lastitem.jl
fib.jl
min.jl
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
9章
circle.jl
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
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
check.jl
repeat.jl
elapsed.jl
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
polynomial.jl
11章
mysparse.jl
12章
tarai.jl
producer-consumer.jl
counter-bad.jl
mb/mandelbrot.jl
mb/plot-seq.jl
mb/plot-par1.jl
counter-lock.jl
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
atomic.jl
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
titlecase.jl
json.jl
logging.jl
14章
toplogger.jl
15章
voyager.c
memcmp.jl
sort.c
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
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/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
isin.jl
sum-unstable.jl
loop.jl
sum-stable.jl
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
sleeper.jl
allocator.jl
vec4.jl
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
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
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
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