AtCoder に登録したら解くべき精選過去問 10 問を Julia で解いてみた

# はじめに
[AtCoder に登録したら次にやること ～ これだけ解けば十分闘える！過去問精選 10 問 ～](https://qiita.com/drken/items/fd4e5e3630d0f5859067#5-%E9%81%8E%E5%8E%BB%E5%95%8F%E7%B2%BE%E9%81%B8-10-%E5%95%8F) で紹介されていた問題を[Julia](https://julialang.org/)で解いてみました。

訂正やよりわかりやすいコードがあればぜひ教えて下さい。

なお、投稿する時点でのAtCoder上のJuliaはver1.4.0です。
アップデートにより内容に齟齬が生じる可能性があります。
（特に後述する ABC086C - Traveling のスコープ関連で言語仕様が変わる可能性が高いです）

# 対象
・Juliaの文法がわからない方。
・Juliaのコードの雰囲気を優しい問題と見比べながら掴みたい方。

# 第１問：[ABC086A - Product](https://atcoder.jp/contests/abs/tasks/abc086_a)
### 解答例１
```Julia
A,B=parse.(Int,split(readline()))
if A*B%2==0
  println("Even")
else
  println("Odd")
end
```
### 解説
`readline()`は標準入力から一行を読み込み、`split()`は文字列を空白区切りで分割します。
つまり`split(readline())`は`["a", "b"]`です。

`parse(Int,string)`は`string`を`Int`型に変換します。
関数を配列の値全てに作用させるとき、Juliaでは`関数.(配列)`という書き方ができます。
（殆どの言語では高階関数mapとして実装されているのではないでしょうか。[もちろんJuliaにもあります](https://docs.julialang.org/en/v1/base/collections/#Base.map)。）

つまり、`parse.(Int,["a", "b"])`は２つの整数を要素に持つ配列`[a, b]`となります。
`a,b=[a, b]`として、それぞれ変数に代入し`if`節で場合分けしています。

結果を計算したら、`println()`で出力します。
`print()`でも標準出力ができますが、末尾に改行が入らないため`println()`を用いるほうが無難でしょう。
### 解答例２
```Julia
println(iseven(prod(parse.(Int,split(readline())))) ? "Even" : "Odd")
```
### 解説
`prod()`は配列の要素をかけ合わせる関数で`prod([a, b])`は`a*b`を返します。
`iseven()`は偶数か否かを判定します。
`iseven(...) ? "Even" : "Odd"`は三項演算子です。
`Bool値 ? trueの場合 : falseの場合`というように評価を分岐できます。


# 第２問：[ABC081A - Placing Marbles](https://atcoder.jp/contests/abs/tasks/abc081_a)
### 解答例
```Julia
println(count(isequal('1'),readline()))
```
`count(関数,配列)`は配列の要素に関数を作用させた際の`true`を数えます。

`isequal(a,b)`は`a`と`b`が等しいか否かを判定します。
`isequal(a)`は`a`と引数が等しいか否かを判定する関数を返します。

`count(isequal('1'),readline())`は文字列`s`の要素である文字がそれぞれ`'1'`と等しい回数を数え上げます。

# 第３問：[ABC081B - Shift only](https://atcoder.jp/contests/abs/tasks/abc081_b)
### 解答例
```Julia
f(x) = iseven(x) ? 1 + f(x ÷ 2) : 0
println(minimum(f.(parse.(Int,split(readlines()[2])))))
```
### 解説
一行目では関数`ｆ`を定義しています。
`f(x)`は`x`を受け取ると、`2`で割り切れるなら`1+f(x/2)`を、そうでなければ`0`を返します。
`x = 2 ^ n * odd` のとき、`f(x)=1+1+1+1+.....+1+f(odd)`となり、`f(odd)`が`0`なので、`n`を返します。

`readlines()`は改行区切りで全ての行を読み込みます。
Juliaの配列は１番目から始まるため、`readlines()[2]`で2行目を指定できます。

`minimum()`は配列の最小の値を返します。

# 第４問：[ABC087B - Coins](https://atcoder.jp/contests/abs/tasks/abc087_b)
### 解答例
```Julia
A,B,C,x=parse.(Int,readlines())
println(count([500a+100b+50c==x for a=0:A,b=0:B,c=0:C]))
```
`[関数 for i=itr1, j=itr2... if Bool値]`は内包表記です。
`i` が `itr1` の要素それぞれ、`j` が `itr2`の要素それぞれとなる繰り返しにおいて、
`Bool値`が`true`のときの`関数`を要素に持つ配列です。
凡例の`if`以降は必要ないので省略されています。

`begin:end`は`begin`から`1`ずつ増えて`end`までを要素に持つ配列です。
`begin:step:end`は`begin`から`step`ずつ増えて`end`までを要素に持つ配列です。

`500 * a`を`500a`と表記することができます。

# 第５問：[ABC083B - Some Sums](https://atcoder.jp/contests/abs/tasks/abc083_b)
### 解答例
```Julia
n,a,b=parse.(Int,split(readline()))
println(sum([i for i=1:n if a <= sum(parse.(Int,[string(i)...])) <= b]))
```
### 解説
内包表記内の`if`が省略されず出てきました。

`if`の中にある`string()`は数字を文字に変換します。
`...`は配列をバラバラにします。
`sum()`は引数を全て足した数を返します。

つまり`sum(parse.(Int,[string(i)...]))`は`i`を文字列に変換し、要素（文字）に分け、
その後全てを整数に読み替えて足し合わせます。

不等号は`a <= X <= b`と連続的に書くことができます。

# 第６問：[ABC088B - Card Game for Two](https://atcoder.jp/contests/abs/tasks/abc088_b)

### 解答例
```Julia
n=parse(Int,readline())
a=sort(parse.(Int,split(readline())))
println(sum([a[i]*(-1)^(n-i) for i=n:-1:1]))
```

### 解説
`sort()`は配列を昇順に並べた配列を返します。
`n:-1:1`は`n`から始まって`-1`ずつ増える最後が`1`の配列です。
後手が取得した数字の分だけ有利が減るとして、後手の取る数字に`-1`を掛けて足しています。

# 第７問：[ABC085B - Kagami Mochi](https://atcoder.jp/contests/abs/tasks/abc085_b)

### 解答例
```Julia
println(length(unique(readlines()[2:end])))
```
### 解説
`unique()`は配列から重複を取り除いたものを返します。
`length()`は配列の要素数（長さ）を返します。
`配列[end]`は配列の最後の値を指します。
`配列[2:end]`のようにスライスにもできます。
標準入力を改行区切りにしたリストの2つ目以降から重複を削除したものの長さを出力しています。

# 第８問：[ABC085C - Otoshidama](https://atcoder.jp/contests/abs/tasks/abc085_c)

### 解答例
```Julia
n,y=parse.(Int,split(readline()))
ansvec=[[a,b,n-a-b] for a=0:n, b=0:n if 10a + 5b + n-a-b == y / 1e3 && b <= n-a]
ans=[ansvec; [-1,-1,-1]][1]
join(stdout,ans," ")
```
### 解説
`1e3`は`1*10^3`の正規形です。
`ansvec`は条件を満たす`[a,b,c]`の組を要素に持つ配列です。

`[配列;配列]`は配列同士を結合します。
`ansvec`に要素が存在しないとき、`[ansvec; [-1,-1,-1]][1]`は`[-1,-1,-1]`であり
それ以外の場合はもともと存在した条件を満たす`[a,b,c]`の組を指します。

`join(io,配列,区切り)`は`配列`の要素の間を`区切り`で埋めて`io`に出力します。
`stdout`(標準出力)を指定すれば`print`を省略できます。

# 第９問：[ABC049C - 白昼夢](https://atcoder.jp/contests/abs/tasks/arc065_a)
### 解答例
```Julia
println(occursin(r"^(erase(r)?|dream(er)?)*$",readline()) ? "YES" : "NO")
```
### 解説
`r"正規表現"`で正規表現のパターンを表すことができます。
`occursin(パターン、文字列)`でパターンにマッチする部分が含まれるかどうかを判定することができます。

# 第１０問：[ABC086C - Traveling](https://atcoder.jp/contests/abs/tasks/arc089_a)
### 解答例
```Julia
function main()
  t,x,y=0,0,0
  flag=true
  for i=1:parse(Int,readline())
    t,x,y=parse.(Int,split(readline())).-(t,x,y)
    flag = flag && abs(x) + abs(y) <= t && iseven(x+y-t)
  end
  println(flag ? "Yes" : "No")
end
main()
```
## 解説
`関数.()`と同じように`.演算子`で配列の要素一般に及ぶ操作を行うことができます。
`abs(x)`は`x`の絶対値を返します。

`function main()
:
end`と囲んでいるのはこうしないと`t,x,y`が`for`内のローカルスコープを指すのか、
グローバルスコープを指すのかでズレが生じてしまうためです。

ただ、この仕様に関してはバージョンアップで変わる可能性が大いにあります。

### 解答例２ 2020/09/04追記
```Julia
let
  t,x,y=0,0,0
  flag=true
  for i=1:parse(Int,readline())
    t,x,y=parse.(Int,split(readline())).-(t,x,y)
    flag = flag && abs(x) + abs(y) <= t && iseven(x+y-t)
  end
  println(flag ? "Yes" : "No")
end
```

### 解説
関数にしなくても、letによってローカルなブロックを使用できるみたいです。
無駄な定義をしない分スマートですね。
@poheさんありがとうございます。

# おわりに
なんとなくでもJuliaという言語の簡潔さが伝われば良いなと思います。
Julia、きれいですよね。