Help us understand the problem. What is going on with this article?

# 「FizzBuzzクイズ」クイズ with #julialang

More than 1 year has passed since last update.

## 初めに

「FizzBuzzクイズ」クイズ－Ruby編 を見て、これ Julia でやるにはどうすれば良いだろう？ と考えてやってみてできたので晒します。

## できたもの

Julia v0.6.0 で開発、v0.6.0/v0.5.2 で動作確認済。
Aho にも対応済。

FizzBuzzQuiz.jl
```"""文字列連結（多重再定義）"""
function Base.:*(n::Int, s2::AbstractString)::AbstractString
str = String(UInt8[reinterpret(UInt8, [n]);Vector{UInt8}(s2)])
SubString(str, sizeof(Int)+1, endof(str))
end
function Base.:*(s1::SubString{String}, s2::AbstractString)::AbstractString
str = String(UInt8[Vector{UInt8}(s1.string);Vector{UInt8}(s2)])
SubString(str, s1.offset+1, endof(str))
end

"""文字列に埋め込んだ整数値を抽出（多重定義）"""
function choose_n(str::SubString{String})::Int
reinterpret(Int, Vector{UInt8}(str.string)[1:str.offset])[1]
end
choose_n(n::Int)::Int = n

"""fizz()"""
function fizz(val::Union{Int,SubString{String}})::Union{Int,AbstractString}
choose_n(val) % 3 == 0 ? val * "Fizz" : val
end

"""buzz()"""
function buzz(val::Union{Int,SubString{String}})::Union{Int,AbstractString}
choose_n(val) % 5 == 0 ? val * "Buzz" : val
end

# @show fizz(1) # => 1
@show 1 |> fizz # => 1
@show 3 |> fizz # => "Fizz"
@show 1 |> buzz # => 1
@show 5 |> buzz # => "Buzz"
# @show buzz(fizz(1)) # => 1
@show 1 |> fizz |> buzz # => 1
@show 3 |> fizz |> buzz # => "Fizz"
@show 5 |> fizz |> buzz # => "Buzz"
@show 15 |> fizz |> buzz # => "FizzBuzz"
@show 15 |> buzz |> fizz # => "BuzzFizz"

"""pezz()"""
function pezz(val::Union{Int,SubString{String}})::Union{Int,AbstractString}
choose_n(val) % 7 == 0 ? val * "Pezz" : val
end

# @show pezz(buzz(fizz(7))) # => "Pezz"
@show 7 |> fizz |> buzz |> pezz # => "Pezz"
@show 21 |> fizz |> buzz |> pezz # => "FizzPezz"
@show 35 |> fizz |> buzz |> pezz # => "BuzzPezz"
@show 105 |> fizz |> buzz |> pezz # => "FizzBuzzPezz"
@show 105 |> fizz |> pezz |> buzz # => "FizzPezzBuzz"
@show 105 |> pezz |> buzz |> fizz # => "PezzBuzzFizz"

# 以下も念のため
@show 1 |> fizz |> buzz |> pezz # => 1
@show 3 |> fizz |> buzz |> pezz # => "Fizz"
@show 5 |> fizz |> buzz |> pezz # => "Buzz"
@show 15 |> fizz |> buzz |> pezz # => "FizzBuzz"
@show 15 |> buzz |> fizz |> pezz # => "BuzzFizz"
@show 104 |> fizz |> buzz |> pezz # => 104

"""hozz()"""
function hozz(val::Union{Int,SubString{String}})::Union{Int,AbstractString}
3 ∈ digits(choose_n(val)) ? val * "Aho" : val
end

@show 13 |> fizz |> buzz |> hozz # => "Aho"
@show 3 |> fizz |> buzz |> hozz # => "FizzAho"
@show 35 |> fizz |> buzz |> hozz # => "BuzzAho"
@show 30 |> fizz |> buzz |> hozz # => "FizzBuzzAho"
@show 30 |> hozz |> buzz |> fizz # => "AhoBuzzFizz"
```

### 解説1：基本方針

Julia では、Ruby のオープンクラス（モンキーパッチ）みたいな、既存の型にフィールドを追加するようなことはできません（はず）。なので「オブジェクト（文字列）にインスタンス変数を持たせて整数値を保持」という手は使えません。

ところでJuliaの文字列は`String`型なのですが、supertypeとして`AbstractString`が存在して、状況によってこれを継承した別の型を文字列として扱うことによって効率化を図っています。
その代表的な例が`SubString{<:AbstractString}`型で、内部に`AbstractString`型の値とオフセット値（とバイト長）を保持して、既存の文字列の一部だけを新しい文字列として扱うための型。新しい文字列オブジェクトを生成（する際にバイト列のコピーを発生）しないようにするために用意された型で、部分文字列を返す関数（例：`match()`）でよく利用されています。見た目上・基本動作は普通の文字列です。

ということで、これを利用することにしました。

`fizz()` `buzz()` の第1引数が `SubString` 型だったら、その先頭（8バイト）に埋め込まれた整数値を抽出して処理をする。
あとは文字列連結するときなどに、せっかく埋め込んだ整数値の情報が消されないように注意すればOK。

### 解説2：Julia コードの概説

• 1〜9行目。Julia での文字列連結演算子は `*`。ただし `«整数値» * «文字列»` は定義されていないし、`«AbstractString» * «AbstractString»`しか定義されていないので、そのことを逆に利用して `«Int» * «AbstractString»``«SubString{String}» * «AbstractString»` を多重定義することで今回の目的を達成できるよう調整。
Julia はこんな感じで演算子のオーバーロードができます。
• 11〜15行目。`SubString{String}` が保持するバイト列に埋め込んだ整数値を抽出。こちらも関数を多重定義して、整数値が引数として渡ってきたらそのままその値を返しています。
15行目の `choose_n(n::Int)::Int = n` は関数のワンライナー定義の表記です。
• 18, 23, 40, 61 行目。引数やその戻り値の型アノテーションとして `Union{A,B}` と指定することで、「`A`または`B`の型を受け取る（返す）」という指定ができます。
動的型付けであり型アノテーション付けなくても動くんですけれど、型推論に全部お任せするのではなくなるべく型情報を付けるようにしています。適切に指定すればパフォーマンス向上するので。
• 62行目、`e ∈ S` は数学の「eはSの要素である」という記号ですが、Julia ではそれがそのまま演算子てとして定義されています。あと `digits()` は整数値を（第2引数が指定されなければ10進数の）各桁の数値を配列（`Vector{Int}`型）で返す関数。
• 28行目他多数：`x |> f1 |> f2` という記述は、関数適用チェーン。文法ではなく `|>` が値と関数を受け取る演算子（関数）として定義されているだけ。この場合 `f2(f1(x))` と同じ意味になります。
Why not register and get more from Qiita?
1. We will deliver articles that match you
By following users and tags, you can catch up information on technical fields that you are interested in as a whole
2. you can read useful information later efficiently
By "stocking" the articles you like, you can search right away