Github CopilotにElixirを教えてみる

# はじめに

PythonのコーディングでGithub copilotを使っているとかなり正確なコードが生成されます。
そんなGithub copilotにElixirのコーディングを教えてあげることにしました。

例題としてAtCoderの問題を解いていきます。
AtCoderの過去問は、Github copilotを有効にしていると、正解コードを作成してしまい、勉強にならないので、AtCoderでは、OFFにしているくらいです。Elixirの場合どうか試して見ます。

# ABC242-A

## ゼロショットで生成してみる

Pythonの場合、正当する場合があるので念のため確認です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/04427be9-64e9-f60d-a5f2-c9fb539ada1f.png)

文法的にはあってるコードですが、この問題の解答にはなってませんでした。
500円、100円、10円の硬貨があります適な問題の解答っぽいですね。

## Pythonのコードを示して書き換えてもらう

```Elixir
    def solve(a, b, c, x) do
        # 次のPythonのコードをElixirに書き換える
        # A,B,C,X = TII()
        # if X <= A:
        #     ans = 1
        # elif X > B:
        #     ans = 0
        # else:
        #     ans = C/(B-A)
        # print(ans)
        if x <= a do
            IO.puts(1)
        elsif x > b do
            IO.puts(0)
        else
            IO.puts(c / (b - a))
        end
        end
     end
```

おお～それっぽいの書いてきたなぁ。とおもって実行してみました。
elsifはありません。
コンパイルエラーでした。

## elsif文はElixirにはありません。使わない方法で書き直して

問題点をcopilot chatで指摘してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/7e2e10b7-37f3-ce69-72ff-1bbcad5e5a2a.png)

この修正したコードでAC(正解)でした。

## cond文を使って書きなおして

elsifの間違いを指摘した時にcond文で書きなおしてくるかな？と予想してたんですが、予想外だったので、cond文に書き直しを頼んでみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/9acd7fb5-13a2-7ffc-7e8e-a9049ccff82f.png)

ちゃんとわかってますね。やればできる。

# ABC242-B

## ゼロショットで生成してみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/1a9ae398-ff4b-23e7-2262-f274d084d3d4.png)

問題Aと同様、正解にはなりませんでした。
しかし、問題Aの時とは違って、Pythonのコードをコメントとして書いて、Elixirのコードを書いてきました。ABC242-Aの真似してきてます。
この適用力は恐ろしいですね。

## Pythonのコードを示して書き換えてもらう

```Elixir
    def solve(s) do
        # 次のPythonのコードをElixirに変換する
        # S = I()
        # s = [s for s in S]
        # s.sort()
        # ans = "".join(s)
        # print(ans)
        s = String.graphemes(s)
        Enum.sort(s)
        ans = Enum.join(s)
        IO.puts(ans)
    end

    def main() do
        s = input()
        solve(s)
    end
```

それらしいコードが作成されました。
おしいですね。
コンパイルは通るんですが、不正解です。
このコード、Elixirに慣れてないとついつい書いてしまうコードだと思います。特にPythonからの移植だったりすると。
Copilot chatを使って、間違った個所を指摘してみます。

## Elixirは関数型言語であることを意識して、修正してください。

単に間違ってるから直してでは、うまく行きませんでした。
「関数型言語」と書いたら、正しいコードになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/26efb631-02d1-30ac-a48c-b5eef6a0cd46.png)

# ABC242-C

最後は、C問題です。
`lixirは関数型言語であることを考慮すること。`もコメントに書いて、どんなコードになるか？

```Elixir
    def solve(n) do

        # 次のPythonのコードをElixirに変換する
        # Elixirは関数型言語であることを考慮すること。
        # N = II()
        # dp = [[0]*9 for _ in range(N)]
        # for i in range(9):
        #     dp[0][i] = 1
        # for i in range(0,N-1):
        #     for j in range(9):
        #         if j > 0:
        #             dp[i+1][j-1] += dp[i][j]
        #         if j < 8:
        #             dp[i+1][j+1] += dp[i][j]
        #         dp[i+1][j] += dp[i][j]
        #         dp[i+1][j] %= MOD
        # # print(dp)
        # ans = sum(dp[N-1]) % MOD
        # print(ans)
        n = ii()
        dp = Enum.map(0..(n-1), fn _ -> Enum.map(0..8, fn _ -> 0 end) end)
        for i <- 0..8 do
            dp[0][i] = 1
        end
        for i <- 0..(n-2) do
            for j <- 0..8 do
                if j > 0 do
                    dp[i+1][j-1] += dp[i][j]
                end
                if j < 8 do
                    dp[i+1][j+1] += dp[i][j]
                end
                dp[i+1][j] += dp[i][j]
                dp[i+1][j] = rem(dp[i+1][j], MOD)
            end
        end

    end

```

これは、難しかったらしく、コメントだけでは、コードがでてきませんでした。
n = ii()を入力したら、
それに続いて、コードがでてはきました。
表面的にPythonのコードを書き換えただけで、動かないですね。

## 関数定義とコメントを書いて助けてみる

コード部分はCopilotが生成したものです。

```Elixir
    def solve(n) do

        # 次のPythonのコードをElixirに変換する
        # Elixirは関数型言語であることを考慮すること。
        # N = II()
        # dp = [[0]*9 for _ in range(N)]
        # for i in range(9):
        #     dp[0][i] = 1
        # for i in range(0,N-1):
        #     for j in range(9):
        #         if j > 0:
        #             dp[i+1][j-1] += dp[i][j]
        #         if j < 8:
        #             dp[i+1][j+1] += dp[i][j]
        #         dp[i+1][j] += dp[i][j]
        #         dp[i+1][j] %= MOD
        # # print(dp)
        # ans = sum(dp[N-1]) % MOD
        # print(ans)

        t = create_all_one_list(9)
        #  tに対して、add_list_with_shiftをn-1回適用する
        for i <- 1..n-1, do: t = add_list_with_shift(t)
        # tの要素の合計をMODで割った余りを出力する
        IO.puts(Enum.sum(t) |> rem(MOD))
    end

    def create_all_one_list(size) do
        #   長さがsize、要素の値が1のリストを作成する
        #   例: size=3の場合、[1,1,1]を返す
        for i <- 1..size, do: 1
    end


    def add_list_with_shift(l) do
        #   リストlの要素を以下のようにシフトして加算したリストを返す。
        # ret[i] = l[i-1] + l[i] + l[i+1]
        shift_left1 = Enum.drop(l, 1)
        shift_right1 = Enum.take(l, length(l) - 1)
        shift_right1 = [0 | shift_right1]
        Enum.zip3(shift_left1, l, shift_right1) |> Enum.map(fn {x, y, z} -> x + y + z end)
    end


    def main() do
        n = ii()
        solve(n)
    end
```

ret[i] = l[i-1] + l[i] + l[i+1]この演算実現するのが難しい。
shift_left1=とか入力したら、その後は書いてくれました。
残念ながら、zip3はありませんけど。

いろいろダメなところがあって、このコードは動きませんでした。

Copilotでの生成は、ここまでで諦めて、あとは自分で修正しました。

```Elixir
    def solve(n) do

        t = create_all_one_list(9)
        # tに対して、add_list_with_shiftをn-1回適用する

        t = for _ <- 1..n-1, reduce: t do
            acc -> add_list_with_shift(acc)
        end
        # tの要素の合計をMODで割った余りを出力する
        IO.puts(Enum.sum(t) |> rem(@mod))
    end

    def create_all_one_list(size) do
        #   長さがsize、要素の値が1のリストを作成する
        #   例: size=3の場合、[1,1,1]を返す
        for i <- 1..size, do: 1
    end


    def add_list_with_shift(l) do
        #   リストlの要素を以下のようにシフトして加算したリストを返す。
        # ret[i] = l[i-1] + l[i] + l[i+1]
        shift_left1 = Enum.drop(l++[0], 1)
        shift_right1 = Enum.take(l, length(l) - 1)
        shift_right1 = [0 | shift_right1]
        Enum.zip([shift_left1, l, shift_right1]) |> Enum.map(fn {x, y, z} -> rem(x + y + z, @mod) end)
    end
```

このコードで正解できました。

# まとめ

- Copilotは関数型についての知識もあるが、まだ慣れてない感じ。
- ループを含むものをCopilotでゼロから作るのは難しい。
- 細かい指示をすればコードは作ってくれるが、大変だった。
- Copilotが関数型ならではの記述を身に着けたら、使えるようになるかもしれない。