雑記帳

Copilot は非大卒が頑張って考えだした激ムズ問題に正しく答えられるのか?

Copilot さんのポテンシャルに期待しながら「ぼくのかんがえたさいきょうにわかりにくい Haskell コード」を作り、それをクイズとして出題してみました。
当然大学を出ていない残念な頭の僕が考えた問題なので、 Copilot さんにとってそんなのは超絶カンタンな問題に過ぎず、パーフェクトな説明とともにスパッとエレガントに正答を導いてくれることでしょう。
問題のコードは以下となります。
((!!)(iterate(curry((foldl((flip$foldr id).(((>>=id).(flip$curry(uncurry id.(fmap$uncurry id).(uncurry$flip(,)).(uncurry$flip(,)).(fmap$(fmap$snd).(uncurry$flip(,)).(fmap$fst.fst).(uncurry$flip(,)).((,)>>=id)).(uncurry$flip(,)).(fmap$snd.fst).(uncurry$flip(,)).((,)>>=id)).(fmap$(:).fst).(uncurry$flip(,)).(fmap$(:).snd).(uncurry$flip(,)).((>>=id)(,)))mempty).((,)>>=id).(flip replicate$succ))) 0.((uncurry(((foldr($)(fst.fst)).repeat).(curry$curry$((uncurry$maybe fst(const snd).([Just (),mempty]!!).fromEnum).(uncurry$(<*>).fmap(,))(uncurry(>=).(uncurry$(<*>).fmap(,))(snd.snd,const 0),(uncurry$(<*>).fmap(,))((uncurry id).(uncurry$(<*>).fmap(,))(snd.fst,(uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))(uncurry(:).(uncurry$(<*>).fmap(,))((fromIntegral.(flip mod$2).length.uncurry(>>=).(uncurry$(<*>).fmap(,))((fmap$maybe[](const[[]])). fst.fst, const id)),fst.snd),(uncurry$maybe fst(const$snd.fst.(uncurry$(<*>).fmap(,))(id,uncurry id.((uncurry$(<*>).fmap(,)).const(snd,snd.fst)$(fromRational,fromIntegral)).(uncurry$(<*>).fmap(,))(snd,const const cos.fst.snd).((,)>>=id))).(maybe Nothing (pure.head)).snd.((fmap((uncurry(>>)).(uncurry((flip$(,).foldr const Nothing . fmap Just).return).((,)>>=id)))).((,)>>=id)).(flip drop$return ()).fromEnum).(uncurry$(<*>).fmap(,))(uncurry(<=).(uncurry$(<*>).fmap(,))(foldr id 0.(fmap$maybe succ(const id)).fst.fst,const 1),(uncurry$(<*>).fmap(,))(mconcat.fst.fst,const Nothing))),pred.snd.snd)),fst.snd))).(uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))((uncurry id).(uncurry$(<*>).fmap(,))((:).snd.fst.snd,zipWith id [fst,snd].(replicate 2).uncurry(!!).(uncurry$(<*>).fmap(,))(fst.fst.fst,snd.snd)),snd.fst),(uncurry$(<*>).fmap(,))(fst.fst.snd,snd.snd))))).(uncurry$(<*>).fmap(,))(id, (uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))(const mempty, const mempty),pred.snd)).(uncurry$(<*>).fmap(,))(id, length).((uncurry zip). (uncurry$(<*>).fmap(,))(((flip$fst.(uncurry$(<*>).fmap(,)).((uncurry$flip(,)).(fmap(uncurry$flip(,))).(uncurry$flip(,)).fmap (uncurry$flip(,))).(uncurry$(<*>).fmap(,))(fmap$const.fst,(fmap$const.fst).(uncurry$flip(,))).(uncurry$(<*>).fmap(,))(((,)>>=id).snd,((,)>>=id).fst))(uncurry(/))).(uncurry$(<*>).fmap(,))(uncurry replicate.(uncurry$(<*>).fmap(,))(length.snd,const Nothing),fst),((>>=id).(flip$curry(uncurry id.(fmap$uncurry id).(uncurry$flip(,)).(uncurry$flip(,)).(fmap$(fmap$snd).(uncurry$flip(,)).(fmap$fst.fst).(uncurry$flip(,)).((,)>>=id)).(uncurry$flip(,)).(fmap$snd.fst).(uncurry$flip(,)).((,)>>=id)).(fmap$(:).fst).(uncurry$flip(,)).(fmap$(:).snd).(uncurry$flip(,)).((>>=id)(,)))mempty).(uncurry$(<*>).fmap(,))(uncurry replicate.(uncurry$(<*>).fmap(,))(length.fst,const Nothing),snd)).((uncurry$(<*>).fmap(,))((!!0),(!!1)).(fmap$(snd.uncurry(>>=).(uncurry$(<*>).fmap(,))(const$pure$sequence_ mempty >>= pure,(curry$(uncurry$(<*>).fmap(,))(snd,(uncurry id).(uncurry$(<*>).fmap(,))(((((foldr($)snd).repeat).(curry$curry$(uncurry$maybe fst(const snd).([Just (),mempty]!!).fromEnum).(uncurry$(<*>).fmap(,))(uncurry(<=).(uncurry$(<*>).fmap(,))(id,const 0).fst.snd,(uncurry$(<*>).fmap(,))(snd.snd, (uncurry$maybe fst(const snd).([Just (),mempty]!!).fromEnum).(uncurry$(<*>).fmap(,))(uncurry(/=).(uncurry$(<*>).fmap(,))(flip mod$2,const 0).fst.snd,(uncurry$(<*>).fmap(,))((uncurry id).(fmap$(uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))(pred.fst.snd,snd.snd),const$Just mempty),snd.fst)),(uncurry id).(fmap$(uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))((uncurry$(<*>).fmap(,))(fst.snd,snd.snd),const Nothing),snd.fst))).(uncurry$(<*>).fmap(,))(const$(uncurry id).(uncurry$(<*>).fmap(,))(snd,(uncurry$(<*>).fmap(,))((flip div$2).fst.fst.fst,uncurry(:).(uncurry$(<*>).fmap(,))(snd.fst, snd.fst.fst))),id))))))),fmap$(take 0).repeat)))))).(uncurry$flip$(flip(:)).(flip(:)[])))))).(((<*>).fmap (,))(uncurry id)snd)))(const 0))) 789 210
スクリーンショット
もちろん自分の頭で意味を考えるなんてタイパの悪いムダなことはせずに、さっさと Copilot さんをはじめとする生成 AI にわかりやすく意味を噛み砕いていただきましょう。
きっと恐ろしくわかりやすく書かれた説明が待っているはずです。
一応ネタバレをしておくと、何か壮大なことが行われているようで、やっていることは単なる「(正の整数同士の) 掛け算」となります。
とはいえその掛け算であるという「結果」それ自体は重要ではなくて、入力として与える「789」と「210」が具体的にどういった処理を通じて最終出力となるそれらの積のカタチにまで持っていかれているのかという部分が肝であり、その点をどれだけわかりやすく噛み砕いて説明してくれるのかが今回のクイズの1番の見どころとなります。
以下、Copilot さんのわかりやすい説明。
スクリーンショット
スクリーンショット
スクリーンショット
スクリーンショット
てっきり僕は掛け算を実行するコードを書いたつもりだったのだけど、実はそれは僕の単なる思い違いで、無意識のうちにそれら2つの整数から 0 を誘導するコードを書いてしまっていたようです。
参りました。
非大卒と生成AI、どちらの意見を鵜呑みにすればいいのかなんて、あえて言うまでもありませんよね。
【結論】
非大卒のご身分では、Copilot さんには到底敵わなかった…
【おまけ】 Copilot さんから珍回答をいただく遊びの続き
しょーもない茶番はさておき、わざわざここまでわかりにくいコードを書かなくても、Copilot から珍回答を引き出すことはいくらでもできるようです。
特に Copilot さんには、
  • 近似値を使用するとまずい場面であっても、自然な流れで使用してくださる。
  • 定石通りでない (学習漏れ状態に置かれているであろう) ヘンテコな記述へのご対応がまだまだ柔軟ではない。
といった Features があるっぽいので、最後におまけとしてそれら Features を活用した「Copilot に珍回答を出力していただく遊び」の番外編をやっていこう。
※ちなみに本編のコードの構造が具体的にどうなっているのかについては自分の頭で考えるか、身近にいる圏論オタクに聞いてみよう。
「定石にとらわれてコードの意味を改変してしまう Copilot さん」の巻
負の平方根やゼロ除算の実行結果を文字列化させて、その文字列「"NaN"」, 「"Infinity"」に対する処理を進めていくアホみたいなコードを作ってみた。
(sum.(fmap $ fromEnum).(>>=id).((fmap $ drop 1).((((>>=id).const ((:).(>>=id)))) [0..5].(fmap $ (flip (>>=)$(++" ").(:[])).(take 2).(drop 1).show.(1/).sqrt)))) (take 32[5,4..])
スクリーンショット
以下 Copilot さんの出力。
スクリーンショット
スクリーンショット
スクリーンショット
スクリーンショット
見ての通り、コードの意味を考えればやるはずのないことを自然な流れで展開していく技が見事に発揮されています。
あとなんか全然意図していなかったところでの求めていないミスも数多くありました。
まず、浮動小数点数の「1」を文字列に変換した値 "1.0" を "1" と置いてしまっているようで、このレベルの操作でも正答からは遠ざかってしまうみたいですね。
続いて7番、いっていることとやっていることが違うと思います。
あと7番と8番の間、一体どんな最適化が施されてしまったのでしょうか。
ちなみにそもそも置かれている文脈で意味が全然変わってくる関数 (>>=id) について、その意味で使っている部分と別の意味で使っている部分が混在しているこの状況でその説明文を提供してしまうのもあまり適当ではないような。
とはいえやはり一番悪質に思えるのは、この不適当な出力の最後に引用先を追加してそれっぽくみせかけつつ、巧妙にユーザーを欺いている点なのかな。
しかしながら、11694525 は近似的に等しいものという見方の存在も否定できないので、 Copilot さんが間違っているとは一概にいえないのかもしれません。
「使ったらマズイ近似値を使いたがる Copilot さん」の巻
近似値をとらずに普通通り倍精度浮動小数点数型で処理して得られる最終的な計算結果に対し、小数点以下の特定の位置に現れる3桁の数字と対応する ASCII テーブル上の文字を並べて得られる文字列を求めるクイズを出してみた。
"Which do you prefer, green apples or regular apples? I prefer " ++ (fmap $ toEnum.fromIntegral.read.take 3. drop 11.show.log.(+3.433447261).(/1000000000))[0.8663336714676007,0.9590372940238012,0.9624701036159422,0.9590372940238012,0.9727707528384144,0.6877942659855307,0.8663336714676007,0.935002741897506,0.9247025367642436,0.9590372940238012,0.9693370550678537,0.9933720512833588] ++ "."
スクリーンショット
以下 Copilot さんの出力。
スクリーンショット
スクリーンショット
スクリーンショット
こうなることがわかっていてやったことだけど、このテキトーさ。(ひょっとするとこれは決してテキトーなのではなく、実はこちらが真の正答なのかもしれませんが)
あと Copilot さんが「11文字」と「2文字」が同じであることを仄めかしていますが、AI がそうおっしゃるのだからきっとそういうことなのでしょう。
余談
どうでもいいけど、これからどのように「考えているような雰囲気を演出するトリック」を LLM に実装していくのかな。
まとめ
Copilot はある意味最強です。