ChatGPTでElixirのプログラムも出力できたので、遊んでみました
ChatGPTのサイトはこちら
ChatGPTとは
直接ChatGPTに答えてもらいました
『ChatGPTは、OpenAIが開発した大規模な言語モデルです。このモデルは、膨大なテキストデータセットから学習させることにより、自然言語の処理や対話生成などの機能を実現します。また、ChatGPTは高い精度を誇り、人間と似たような対話ができることで知られています。』
せっかくなので、ちょっとだけ文章を入力して遊んでみました
AIの答えは
『健康であることはとても大切です』
『Elixirを使えば何でも出来るわけではありません。プログラミング言語にはそれぞれの長所と短所があり、Elixirもそうです。そのため、 どのようなものを作りたいかによって、適切な言語を選択する必要があります。 』
まぁそうですよね
これまじすごい!20数年前に草の根BBSであった人口無能や、一太郎のおまけにつていたコンピュータが返事してくれるツールと比べ物にならないぐらいの技術進化!
こんな質問も
1、4を積極的にtry
して、after
で心のもやもやリソースと傷
を開放して滅びましょう
つまり、現時点ではAIの回答内容によってはファクトチェックを検討に検討を重ねる
必要がありそうです
そんなわけで次は、AIにElixirのプログラムを作ってもらいましょう
そして、プログラムのコピペで何もしない
と、炎上することはあります
その為、プログラムを理解して正しいか検証しましょう
つまりこの記事は
・ChatGPTでElixirのソース出力するとどうなるの?
・関数テストをしてみよう
の2つの視点で書きます
お題
2つの物体の当たり判定する関数をelixirで実装して
CharGPTの結果
defmodule Rectangle do
def collided?(x1, y1, w1, h1, x2, y2, w2, h2) do
x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2
end
end
Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5)
まずはiex起動して検証
ではテストパターンを考えてみよう
ここではAとBの2つの正方形(又は長方形)の当たり判定を考えます
大きく分類して2つの結果が得られます
更に細かく座標の位置関係を考えてみよう
サイズの違いによるパターン
テストプログラムを作ろう
プロジェクトの作成
$ mix new rectangle
実行結果
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/rectangle.ex
* creating test
* creating test/test_helper.exs
* creating test/rectangle_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd rectangle
mix test
Run "mix help" for more commands.
rectangleプロジェクトに移動
$ cd rectangle
プロジェクトにテストの仕組みを組み込む
mix test.watchを使えるようにして、ファイル保存時に自動的にテストを行うようにします
参考
組み込んでみよう
mix.exsを編集
defmodule Rectangle.MixProject do
use Mix.Project
def project do
[
app: :rectangle,
version: "0.1.0",
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
+ {:mix_test_watch, "~> 1.0", only: :dev, runtime: false}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end
mix test.watchの準備と実行
$ mix deps.get
実行結果
Resolving Hex dependencies...
Dependency resolution completed:
New:
file_system 0.2.10
mix_test_watch 1.1.0
* Getting mix_test_watch (Hex package)
* Getting file_system (Hex package)
testの自動実行
$ mix test.watch
test結果
Running tests...
Compiling 1 file (.ex)
Generated rectangle app
..
Finished in 0.01 seconds (0.00s async, 0.01s sync)
1 doctest, 1 test, 0 failures
ファイルが保存する度に、自動的にテストする仕組みができました
検証対象の関数を作成
collided?関数とExamplesを追加
Examplesを追加するとDoctestが行われます
defmodule Rectangle do
@moduledoc """
Documentation for `Rectangle`.
"""
@doc """
Hello world.
## Examples
iex> Rectangle.hello()
:world
"""
def hello do
:world
end
+ @doc """
+ Collided detection.
+ ## Examples
+ iex> Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5)
+ true
+ """
+ def collided?(x1, y1, w1, h1, x2, y2, w2, h2) do
+ x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2
+ end
end
保存すると下記の結果が表示されます
Running tests...
...
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 doctests, 1 test, 0 failures
Randomized with seed 779417
2doctestsに変化しました
テストモジュールの作成
defmodule RectangleTest do
use ExUnit.Case
doctest Rectangle
test "greets the world" do
assert Rectangle.hello() == :world
end
+ test "collided" do
+ assert Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5) == true
+ end
+ test "not collided" do
+ assert Rectangle.collided?(1, 1, 10, 10, 15, 5, 5, 5) == false
+ end
end
※ assert Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5)
で良いのですが以後の改造の為にわざと
assert Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5) == true
で表現してます
保存後の結果
Running tests...
.....
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 doctests, 3 tests, 0 failures
Randomized with seed 454614
3 testsに変化しました
テストをわざと誤りにしてみると
Randomized with seed 454614
Running tests...
1) test collided (RectangleTest)
test/rectangle_test.exs:9
Assertion with == failed
code: assert Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5) == false
left: true
right: false
stacktrace:
test/rectangle_test.exs:10: (test)
....
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 doctests, 3 tests, 1 failure
1 failureと表示します
テストをパラメータ化しよう
defmodule RectangleTest do
use ExUnit.Case
doctest Rectangle
test "greets the world" do
assert Rectangle.hello() == :world
end
- test "collided" do
- assert Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5) == true
- end
- test "not collided" do
- assert Rectangle.collided?(1, 1, 10, 10, 15, 5, 5, 5) == false
- end
+ test_params = [
+ [1, 1, 10, 10, 5, 5, 5, 5, true],
+ [1, 1, 10, 10, 15, 5, 5, 5, false]
+ ]
+ for test_param <- test_params do
+ @test_param test_param
+ test "collided #{inspect(@test_param)}" do
+ [x1, y1, w1, h1, x2, y2, w2, h2, ret] = @test_param
+ assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
+ end
+ end
end
実行結果
Running tests...
.....
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 doctests, 3 tests, 0 failures
Randomized with seed 984685
テストパターンを増やそう
スプレッドシート等を使ってパターンを考えてみる
テストパターンからソースを起こす
手軽な方法はエディター等で
スプレッドシートからコピペする
・ ^
→ [
に置換(正規表現指定で先頭を[
)
・ $
→ ],
に置換(正規表現指定で行末を],
)
・ ¥t
→ ,
に置換(正規表現指定でタブを,
)
加工した結果をテストモジュールに貼り付ける
※場合によっては、特定の文字列を起こすスクリプトをElixirで作ってもいいですね(業務ではそうしてる)
defmodule RectangleTest do
use ExUnit.Case
doctest Rectangle
test "greets the world" do
assert Rectangle.hello() == :world
end
test_params = [
- [1, 1, 10, 10, 5, 5, 5, 5, true],
- [1, 1, 10, 10, 15, 5, 5, 5, false]
+ [100, 100, 10, 10, 90, 90, 10, 10, true],
+ [100, 100, 10, 10, 90, 110, 10, 10, true],
+ [100, 100, 10, 10, 110, 90, 10, 10, true],
+ [100, 100, 10, 10, 110, 110, 10, 10, true],
+ [100, 100, 10, 10, 89, 89, 10, 10, false],
+ [100, 100, 10, 10, 89, 111, 10, 10, false],
+ [100, 100, 10, 10, 111, 89, 10, 10, false],
+ [100, 100, 10, 10, 111, 111, 10, 10, false],
+ [100, 100, 10, 10, 91, 91, 10, 10, true],
+ [100, 100, 10, 10, 91, 109, 10, 10, true],
+ [100, 100, 10, 10, 109, 91, 10, 10, true],
+ [100, 100, 10, 10, 109, 109, 10, 10, true],
+ [100, 100, 10, 10, 88, 88, 10, 10, false],
+ [100, 100, 10, 10, 88, 112, 10, 10, false],
+ [100, 100, 10, 10, 112, 88, 10, 10, false],
+ [100, 100, 10, 10, 112, 112, 10, 10, false],
+ [100, 100, 10, 10, 89, 90, 10, 10, false],
+ [100, 100, 10, 10, 90, 111, 10, 10, false],
+ [100, 100, 9, 10, 110, 90, 10, 10, false],
+ [100, 100, 10, 9, 110, 110, 10, 10, false],
+ [90, 90, 10, 10, 100, 100, 10, 10, true],
+ [90, 110, 10, 10, 100, 100, 10, 10, true],
+ [110, 90, 10, 10, 100, 100, 10, 10, true],
+ [110, 110, 10, 10, 100, 100, 10, 10, true],
+ [89, 89, 10, 10, 100, 100, 10, 10, false],
+ [89, 111, 10, 10, 100, 100, 10, 10, false],
+ [111, 89, 10, 10, 100, 100, 10, 10, false],
+ [111, 111, 10, 10, 100, 100, 10, 10, false],
+ [91, 91, 10, 10, 100, 100, 10, 10, true],
+ [91, 109, 10, 10, 100, 100, 10, 10, true],
+ [109, 91, 10, 10, 100, 100, 10, 10, true],
+ [109, 109, 10, 10, 100, 100, 10, 10, true],
+ [88, 88, 10, 10, 100, 100, 10, 10, false],
+ [88, 112, 10, 10, 100, 100, 10, 10, false],
+ [112, 88, 10, 10, 100, 100, 10, 10, false],
+ [112, 112, 10, 10, 100, 100, 10, 10, false],
+ [89, 90, 10, 10, 100, 100, 10, 10, false],
+ [90, 111, 10, 10, 100, 100, 10, 10, false],
+ [110, 90, 10, 10, 100, 100, 9, 10, false],
+ [110, 110, 10, 10, 100, 100, 10, 9, false]
]
for test_param <- test_params do
@test_param test_param
test "collided #{inspect(@test_param)}" do
[x1, y1, w1, h1, x2, y2, w2, h2, ret] = @test_param
assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
end
end
end
実行結果
Randomized with seed 262009
Running tests...
Compiling 1 file (.ex)
...
1) test collided [110, 90, 10, 10, 100, 100, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
2) test collided [100, 100, 10, 10, 110, 90, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
............
3) test collided [100, 100, 10, 10, 90, 90, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
.
4) test collided [100, 100, 10, 10, 110, 110, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
..
5) test collided [90, 110, 10, 10, 100, 100, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
....
6) test collided [110, 110, 10, 10, 100, 100, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
..
7) test collided [90, 90, 10, 10, 100, 100, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
....
8) test collided [100, 100, 10, 10, 90, 110, 10, 10, true] (RectangleTest)
test/rectangle_test.exs:54
Assertion with == failed
code: assert Rectangle.collided?(x1, y1, w1, h1, x2, y2, w2, h2) == ret
left: false
right: true
stacktrace:
test/rectangle_test.exs:56: (test)
.......
Finished in 0.04 seconds (0.00s async, 0.04s sync)
2 doctests, 41 tests, 8 failures
Randomized with seed 233531
おお!失敗が8箇所
期待値と違うぞ
えっと… ぴったし重なった部分が判定が期待通りでない
プログラム修正
defmodule Rectangle do
@moduledoc """
Documentation for `Rectangle`.
"""
@doc """
Hello world.
## Examples
iex> Rectangle.hello()
:world
"""
def hello do
:world
end
@doc """
Collided detection.
## Examples
iex> Rectangle.collided?(1, 1, 10, 10, 5, 5, 5, 5)
true
"""
def collided?(x1, y1, w1, h1, x2, y2, w2, h2) do
- x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2
+ x1 <= x2 + w2 && x1 + w1 >= x2 && y1 <= y2 + h2 && y1 + h1 >= y2
end
end
実行結果
Running tests...
Compiling 1 file (.ex)
...........................................
Finished in 0.04 seconds (0.00s async, 0.04s sync)
2 doctests, 41 tests, 0 failures
Randomized with seed 251862
期待通り動くようになりました!
まとめ
・ChatGPTはすごいぞ!おおよその答えは合ってる(ホントもう少し)
・理解をしていないソースのコピペして終わらせる無責任なコーディングはやめましょう
これはAI、他人のソース、ググった結果を鵜呑みにせずに検証しましょう
・テストは作りましょう、今回の実験の結果から、望んでいないケースもあります
・AIの技術はすばらしいと思います、でも利用側のリテラシーが必要である事を認識しました