はじめに
AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~ で紹介されていた問題をJuliaで解いてみました。
訂正やよりわかりやすいコードがあればぜひ教えて下さい。
なお、投稿する時点でのAtCoder上のJuliaはver1.4.0です。
アップデートにより内容に齟齬が生じる可能性があります。
(特に後述する ABC086C - Traveling のスコープ関連で言語仕様が変わる可能性が高いです)
対象
・Juliaの文法がわからない方。
・Juliaのコードの雰囲気を優しい問題と見比べながら掴みたい方。
第1問:ABC086A - Product
解答例1
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にもあります。)
つまり、parse.(Int,["a", "b"])
は2つの整数を要素に持つ配列[a, b]
となります。
a,b=[a, b]
として、それぞれ変数に代入しif
節で場合分けしています。
結果を計算したら、println()
で出力します。
print()
でも標準出力ができますが、末尾に改行が入らないためprintln()
を用いるほうが無難でしょう。
解答例2
println(iseven(prod(parse.(Int,split(readline())))) ? "Even" : "Odd")
解説
prod()
は配列の要素をかけ合わせる関数でprod([a, b])
はa*b
を返します。
iseven()
は偶数か否かを判定します。
iseven(...) ? "Even" : "Odd"
は三項演算子です。
Bool値 ? trueの場合 : falseの場合
というように評価を分岐できます。
第2問:ABC081A - Placing Marbles
解答例
println(count(isequal('1'),readline()))
count(関数,配列)
は配列の要素に関数を作用させた際のtrue
を数えます。
isequal(a,b)
はa
とb
が等しいか否かを判定します。
isequal(a)
はa
と引数が等しいか否かを判定する関数を返します。
count(isequal('1'),readline())
は文字列s
の要素である文字がそれぞれ'1'
と等しい回数を数え上げます。
第3問:ABC081B - Shift only
解答例
f(x) = iseven(x) ? 1 + f(x ÷ 2) : 0
println(minimum(f.(parse.(Int,split(readlines()[2])))))
解説
一行目では関数f
を定義しています。
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の配列は1番目から始まるため、readlines()[2]
で2行目を指定できます。
minimum()
は配列の最小の値を返します。
第4問:ABC087B - Coins
解答例
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
と表記することができます。
第5問:ABC083B - Some Sums
解答例
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
と連続的に書くことができます。
第6問:ABC088B - Card Game for Two
解答例
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
を掛けて足しています。
第7問:ABC085B - Kagami Mochi
解答例
println(length(unique(readlines()[2:end])))
解説
unique()
は配列から重複を取り除いたものを返します。
length()
は配列の要素数(長さ)を返します。
配列[end]
は配列の最後の値を指します。
配列[2:end]
のようにスライスにもできます。
標準入力を改行区切りにしたリストの2つ目以降から重複を削除したものの長さを出力しています。
第8問:ABC085C - Otoshidama
解答例
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
を省略できます。
第9問:ABC049C - 白昼夢
解答例
println(occursin(r"^(erase(r)?|dream(er)?)*$",readline()) ? "YES" : "NO")
解説
r"正規表現"
で正規表現のパターンを表すことができます。
occursin(パターン、文字列)
でパターンにマッチする部分が含まれるかどうかを判定することができます。
第10問:ABC086C - Traveling
解答例
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
内のローカルスコープを指すのか、
グローバルスコープを指すのかでズレが生じてしまうためです。
ただ、この仕様に関してはバージョンアップで変わる可能性が大いにあります。
解答例2 2020/09/04追記
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、きれいですよね。