はじめに
PythonのコーディングでGithub copilotを使っているとかなり正確なコードが生成されます。
そんなGithub copilotにElixirのコーディングを教えてあげることにしました。
例題としてAtCoderの問題を解いていきます。
AtCoderの過去問は、Github copilotを有効にしていると、正解コードを作成してしまい、勉強にならないので、AtCoderでは、OFFにしているくらいです。Elixirの場合どうか試して見ます。
ABC242-A
ゼロショットで生成してみる
Pythonの場合、正当する場合があるので念のため確認です。
文法的にはあってるコードですが、この問題の解答にはなってませんでした。
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で指摘してみました。
この修正したコードでAC(正解)でした。
cond文を使って書きなおして
elsifの間違いを指摘した時にcond文で書きなおしてくるかな?と予想してたんですが、予想外だったので、cond文に書き直しを頼んでみました。
ちゃんとわかってますね。やればできる。
ABC242-B
ゼロショットで生成してみる
問題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は関数型言語であることを意識して、修正してください。
単に間違ってるから直してでは、うまく行きませんでした。
「関数型言語」と書いたら、正しいコードになりました。
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が関数型ならではの記述を身に着けたら、使えるようになるかもしれない。