LoginSignup
11
0

Github CopilotにElixirを教えてみる

Last updated at Posted at 2023-12-13

はじめに

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

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

ABC242-A

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

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

image.png

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

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

    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

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

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

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

image.png

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

ABC242-B

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

image.png

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

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

    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

ABC242-C

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

    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が生成したものです。

    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での生成は、ここまでで諦めて、あとは自分で修正しました。

    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が関数型ならではの記述を身に着けたら、使えるようになるかもしれない。
11
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
0