yukicoder（競プロ）でElixirの修行をする

# はじめに
最近、Elixirの文法をyukicoder（競技プログラミングサイト）の問題を解きながら学んでいます。
提出するプログラムの形式やElixirにおける標準入出力のパターンについて、まとめてみました。

# 競技プログラミングとは
競技プログラミングにもいろいろ種類はあるのですが、代表的なのはアルゴリズムに関する問題を解くプログラムを作成するというものです。
問題ごとに小規模なプログラムを独立に作成するので、新しく学ぶ言語の基本的な文法を習得するにはうってつけです。
詳しくは、以下のページが参考になります。
[競技プログラミングとは？メリットや初心者にもおすすめな理由も解説](https://tech-camp.in/note/technology/43369/)

# Elixirが使える競技プログラミングサイト
残念ながら、Elixirが使える競技プログラミングサイトは少ないです。
国内サイトだと、[yukicoder](https://yukicoder.me) 、海外サイトだと[HackerRank](https://www.hackerrank.com/)でElixirを使えます。
当然、[Project Euler](https://projecteuler.net)や[Google Code Jam](https://code.google.com/codejam/)のようにローカルで実行させた結果を提出する形式のサイトでも使えます。
今回はタイトルの通り、yukicoderの問題をElixirで解く方法について説明します。

# 提出するプログラムの形式
[ヘルプ - yukicoder](https://yukicoder.me/help) の説明の通り、 `elixir -e Main.main` コマンドにより、提出されたElixirプログラムは実行されます。
したがって、以下の形式がyukicoderにおけるテンプレートになります。

```elixir
defmodule Main do
  def main do
  # ここに問題を解くプログラムを記述する
  end
end
```

# 入力
標準入力からパラメータを受け取るので、[IO.gets/2](https://hexdocs.pm/elixir/IO.html#gets/2) を使います。
`IO.gets/2` の必須引数 `prompt` には空文字列を指定してください。
また、`IO.gets/2` で得た値は文字列なので必要に応じて型変換もしてあげます。
そして、 `IO.gets/2` で得た文字列には改行文字が含まれるので要注意です。
末尾の改行文字の除去には[String.trim/1](https://hexdocs.pm/elixir/String.html#trim/1)を使います。

## 単一の値を入力
変数 `a` に単一の値を入力するパターンです。

### 入力される値が文字列の場合
`IO.gets("")` で得た入力値を `String.trim/1` にパイプします。

```elixir
a = IO.gets("") |> String.trim
```

`IO.gets/2` の引数の括弧は省略できないことに注意してください。つまり、以下のように記述できません。

```elixir
a = IO.gets "" |> String.trim # NG!
```

### 入力される値が整数の場合
[String.to_integer/1](https://hexdocs.pm/elixir/String.html#to_integer/1) を使って、文字列として受け取った入力値を整数に変換します。

```elixir
a = IO.gets("") |> String.trim |> String.to_integer
```

パイプ演算子が気持ちよいですね。

### 入力される値が浮動小数点数の場合
[String.to_float/1](https://hexdocs.pm/elixir/String.html#to_float/1) を使って、文字列をとして受け取った入力を浮動小数点数に変換します。

```elixir
a = IO.gets("") |> String.trim |> String.to_float
```

## 定数個の値を入力
空白区切りで定個数の値が入力されるケースです。
例えば、 `A B C` のような入力形式です。
[String.split/1](https://hexdocs.pm/elixir/String.html#split/1) を使い、空白で値を分割します。

### 入力値が文字列の場合
`String.split/1` の戻り値はリストなので、パターンマッチングを利用して入力値をバインドさせます。

```elixir
[a, b, c] = IO.gets("") |> String.trim |> String.split
```

### 入力値が整数（浮動小数点数）の場合
`String.split/1` により分割した文字列のリストに対して、 [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2) の引数に`String.to_integer/1` または `String.to_float/1` を指定し、
リストの要素である各文字列を整数（浮動小数点数）に変換します。
以下はすべての入力値が整数の場合の例です。

```elixir
[a, b, c] = IO.gets("") |> String.trim |> String.split |> Enum.map(&to_integer/1)
```

## 可変個の値を入力
最初に与えられた整数 `N` のあとに、 `N` 個の値が入力されるパターンです。
例えば、

```
3
foo
bar
baz
```
のような入力形式です。
このような形式の場合、リストの内包表記を使うと便利です。

### 入力値が文字列の場合
リストの内包表記を用いて、 `IO.gets("") |> String.trim` により　値を`n` 回受け取ります。

```elixir
n = IO.gets("") |> String.trim |> String.to_integer
a = for _ <- 1..n, do: IO.gets("") |> String.trim
```

`1..n` は `1` から `n` までのrangeです。 `n` 回繰り返すために使います（`n` 個の要素を含むrangeであることがポイント。各値は興味なし）。
リストの内包表記の束縛変数が `_` となっていますが、これはrangeの各値に興味がないことを表します。

### 入力値が整数（浮動小数点数）の場合
`String.trim/1` のあとに `String.to_integer/1` または `String.to_float/1` をパイプするだけです。

```elixir
n = IO.gets("") |> String.trim |> String.to_integer
a = for _ <- 1..n, do: IO.gets("") |> String.trim |> String.to_integer
```


# 出力
標準出力に解を出力するので、[IO.puts/2](https://hexdocs.pm/elixir/IO.html#puts/2) を使います。

## 単一の値を出力
最もよくあるパターンです。例えば、変数 `ans` に格納されている値を出力するならば、

```elixir
IO.puts ans
```

と書きます。

## 定数個の値を出力
3個の値を空白区切りで出力する場合などです。 
[String interpolation](https://elixir-lang.org/getting-started/basic-types.html#strings) を使うとよさそうです。
例えば、変数 `x`, `y`, `z` に格納されている値を空白区切りで出力するならば、 

```elixir
IO.puts "#{x} #{y} #{z}"
```

と書けます。

## 可変個の値を出力
リストに格納されている値を出力するパターンです。[Enum.join/2](https://hexdocs.pm/elixir/Enum.html#join/2) が便利です。

リスト `xs` の各要素を改行区切りで出力するならば、

```elixir
IO.puts Enum.join(xs, "\n")
```

と書けます。

同様に、リスト `xs` の各要素を空白区切りで出力するならば、

```elixir
IO.puts Enum.join(xs, " ")
```

と書けます。

# 例: No.712 赤旗を解いてみる
yukicoderの[No.712 赤旗](https://yukicoder.me/problems/no/712)をElixirで解いてみます。

## プログラムの形式
まず、提出するプログラムの形式の通りにテンプレートを写経します。

```elixir
defmodule Main do
  def main do
  # ここに問題を解くプログラムを記述する
  end
end
```

## 入力
問題文のページにもある通り、入力形式は以下です。

```
N M
A_1
...
A_N
```

まず、 定数個の整数を入力する例を参考に、 `N`, `M` を以下のように受け取ります。

```elixir
[n, m] = IO.gets("") |> String.trim |> String.split |> Enum.map(&String.to_integer/1)
```

ここで、 問題文に合わせて `n` と `m` を大文字にしないように注意してください。
Elixirでは大文字から始まる識別子を変数に対して与えられないのです。

次に、可変個の文字列を入力する例を参考に、 `A_1` から `A_n` を以下のように受け取ります。

```elixir
a = for _ <- 1..n, do: IO.gets("") |> String.trim
```

入力はこれでおしまいです。ここまでをまとめると、

```elixir
defmodule Main do
  def main do
    # 入力
    [n, m] = IO.gets("") |> String.trim |> String.split |> Enum.map(&String.to_integer/1)
    a = for _ <- 1..n, do: IO.gets("") |> String.trim
    # 問題を解くプログラムを記述する
    # 解を出力するプログラムを記述する
  end
end
```

例えば、入力サンプル1である以下の入力に対して、 `n = 3`, `m = 3`, `a = ["WRW", "RWR", "WRW"]` がバインドされます。

```
3 3
WRW
RWR
WRW
```

## 出力
出力は整数1個なので、

```elixir
IO.puts ans
```

とするだけです。 ここで、 `ans` には出力する整数が格納されているものとします。
したがって、提出するプログラムは以下となります。

```elixir
defmodule Main do
  def main do
    # 入力
    [n, m] = IO.gets("") |> String.trim |> String.split |> Enum.map(&String.to_integer/1)
    a = for _ <- 1..n, do: IO.gets("") |> String.trim    

    # 問題を解くプログラムを記述する
    ans = # これは自力で書いてみましょう。

    # 出力
    IO.puts ans    
  end
end
```

この問題の `ans` の求め方については本記事では解説しないでおきます。

# おわりに
競技プログラミングの問題をたくさん解くと、いろいろな構文やライブラリの関数を知ることができて有益です。
一日一問やるだけでもだいぶ力がつくんじゃないかと。
問題を解く際には、美しく書けないかを調べながら解くと、より多くの知識が得られるのではないかと思います（何でもかんでも自力でゴリゴリ書かない）。

ちなみに、yukicoderでElixirコードを提出すると、実行時間がとても大きいので、制限時間が厳しい問題は通せないと思われます...

# リンク
* [yukicoder](https://yukicoder.me): Elixirにも対応している素晴らしい競技プログラミングサイト
* [Elixir standard library documentation](https://hexdocs.pm/elixir/Kernel.html): Elixirの標準ライブラリの関数はここで調べましょう