メインコンテンツへスキップ

プロでも陥る無限ループを回避!アルゴリズムのバグをなくす「頭の中の証明」とは?

·4 分
2025/07 プログラミング アルゴリズム バグ 思考法 ソフトウェア開発

プロでも陥る無限ループを回避!アルゴリズムのバグをなくす「頭の中の証明」とは?

引用元:https://news.ycombinator.com/item?id=44573409

hiAndrewQuinn 2025/07/15 17:49:25

バイナリサーチはシンプルに見えて実装が難しいんだ。Jon Bentleyがプログラマの9割がバグありのバイナリサーチを書いたって話、知ってる?無限ループが多いみたい。ループ不変条件を意識しないと正しく書けないよ。僕の記事で詳しく解説してるから見てみてね。
[1]: https://hiandrewquinn.github.io/til-site/posts/binary-search

mdnahas 2025/07/16 09:50:16

僕もバイナリサーチを面接問題にしてるけど、2/3の優秀な候補者が20分で書けないんだ。ほとんど無限ループで失敗するよ!原因はたぶん、教えられてるインターフェースが悪いんだよね。inclusiveかexclusiveかでバグの発生率が変わるか、実験してみたいなぁ。

godelski 2025/07/16 07:07:18

面白いテストになるね。Claude SonnetにPythonでバグのないバイナリサーチを書いてもらったんだ。結果は見ての通り、コードとテストケースが提示されてるよ。AIもちゃんと書けるんだね。

hiAndrewQuinn 2025/07/16 07:16:41

Claudeのコードにある ’mid = left + (right - left) // 2’ って式、Pythonだと整数のオーバーフローを気にする必要ないから不要なんだ。Cみたいな言語なら大事だけどね。Pythonではちょっと非イディオム的かな。自分でアルゴリズム書くなら、最適化を意識すべきだね。

sedatk 2025/07/15 19:07:59

僕はバイナリサーチのバグが有名だって知ってたから、自分の本では超慎重に書いたんだ。それでも結局バグがあったよ。Manningの早期アクセスプログラムのおかげで、印刷前に直せて本当に良かったよ。

edanm 2025/07/16 05:20:09

ああ、それ面白いね!そのバグがどんなものだったか、どこで読めるかな?

fragmede 2025/07/15 18:59:48

2006年のGoogleの論文によると、バイナリサーチの「ほぼ全ての」実装にバグがあったらしいよ。そう考えると、Jon Bentleyの90%がバグって話、むしろすごいと思わない?
https://research.google/blog/extra-extra-read-all-about-it-n

Etheryte 2025/07/16 11:36:01

君のその説明だと、バイナリサーチは悪い面接問題に聞こえるよ。優秀な候補者が解けないなら、それはただLeetCodeを練習した人だけを選んでるってことじゃないの?

qsort 2025/07/15 18:20:21

正直、あなたはインデックスにめちゃくちゃ神経を使う例を挙げてるよね。エラーなしで書くのが多分一番難しい基本的なアルゴリズムだわ。Hoare partitionと同じくらい難しいやつ。

hiAndrewQuinn 2025/07/16 12:13:11

マジでそう。Pythonの閉じた左端、開いた右端の記法が好きな理由の一つはこれなんだよね。inclusive boundの問題を完全に回避できるから。だって、PythonのリストLで0 ≤ l ≤ r ≤ len(L)なら、L==L[0:len(L)]==L[0:l]+L[l:len(L)]==L[0:l]+L[l:r]+L[r:len(L)]ってなるじゃん。この記法、最初は嫌いだったんだけど、自分で動かしてみてからはもうこれしか考えられないわ。他の全部変に見える。

monkeyelite 2025/07/15 22:02:58

C++の標準ライブラリだと、binary searchをパーティショニング問題として表現してるんだよね。述語がfalseからtrueに変わるインデックスを見つけるって感じで、俺にはそれがすごく分かりやすい。

scuol 2025/07/16 06:11:57

C++のlower_boundやupper_boundみたいな最左・最右binary searchを俺がいつも覚えてる方法は、常に「prior best」を持って、アルゴリズムに合わせて境界を動かすってやり方なんだ。while (l < r){auto mp = l + (l-r)/2;if (nums[mp] == target){prior = target;#ifdef upper_boundl = target + 1;#else r = target - 1;#endif}}
ひどいフォーマットでごめんね、レイオフされてからLeetCodeやりまくってて、もう一日中コード書いてるからさ…。

andyjohnson0 2025/07/16 11:09:02

これについて読んだんだけど、binary searchを面接の質問に使い始めたってのは、なんでそれが有用だと思うのか興味あるな。有名なアルゴリズムをストレスのある面接の状況で実装させるのって、候補者についてあまり示さない気がするんだけど。俺なら、むしろコードの印刷物を見せてもらって、その実装について話したいな。例えば、抽象アルゴリズムとコードがどう対応してるかとか、実装のトレードオフとか、可読性とかね。

naniwaduni 2025/07/16 11:09:28

C言語みたいな言語でも、実際に使う場合って、サイズが小さいインデックスを使ってない限り、ほとんどおまじないみたいなもんでしょ。だってSIZE_MAX/2なんてサイズの要素なんて、まずないからね。根本的な問題は、インデックスにintを雑に使うことだよ。回りくどい計算しても、二倍のサイズでオーバーフローしちゃうし。

sedatk 2025/07/16 20:35:56

あれは全くもってバカなミスだったよ。binary searchで一番よくあるバグは、中間点を(start + end) / 2って計算するところなんだ。一見簡単そうだけど、「start + end」が大きすぎて整数オーバーフローを起こす可能性があるんだよね。そうすると関数が壊れちゃう。俺のそのケースの処理も、結局間違ってて、読者の一人がそれを見つけてくれたんだ。他にもバグがあったかは覚えてないけど、あれは一番痛かったね。

JohnKemeny 2025/07/15 22:15:37

昔、C言語でmicrocontroller用にbinary searchを書く必要があったんだ(ライブラリなしでね)。そのルーティンはだいたい1時間ごとに実行されてて、約4Mのデータポイントを扱ってたよ。

rockostrich 2025/07/16 15:37:28

ほとんどのスライス系メソッドって、Pythonと同じような動作をするんじゃない?JavaScriptのArray.prototype.sliceとか、JavaのArrays.copyOfRange、Golangのスライス構文もPythonに似てるけど、3番目の値がステップ値じゃなくて結果のスライスの最大サイズってだけで、全部同じような振る舞いをするよね。

jansan 2025/07/16 05:11:10

JavaScriptの場合、Googleのその「バグ」って、配列が4503599627370495個以上のアイテムを持ってる時にしか発生しないんだよ。そのバグにぶつかる前に、きっとメモリの問題にぶつかるんじゃないかな。

hiAndrewQuinn 2025/07/15 19:03:33

記事のバグって、俺が言ってたバッファオーバーフローのことだよ。Jonですら”決定版”の実装でミスってるんだから、彼が正しいとした10%の実装で見抜くのは無理だったろうね。Googleの基準だと、検索の実装を完璧にできた奴は一人もいないんじゃないかな。

mettamage 2025/07/16 11:41:07

これはLeetCode経験があるか、誰かが見てるとコードが書けるってことだね。こういうスキルが必要な企業文化もあるし、そうじゃない企業は勘違いしてるよ。俺はこうするね:1. CSVからデータを読む税金計算機とかでプログラミング能力を見てもらう。2. 小規模な有料プロジェクト。良ければ雇う。ダメならフィードバックして、1週間後にまたやらせて、学びの俊敏性を見る。3. 学びの俊敏性が高ければ雇う。

namibj 2025/07/16 11:57:42

スカラーコードなら公平だけど、gatherでベクトル化できるってことを忘れないでね。その場合、整数幅が広くなるとレーンが減るから心配だよ。
正直、B+ツリー構造をバルク/キャッシュの「ライン/ページ」サイズにチャンク化して、各ノード内で2進数より高次のベクトル化された検索を使うべきだろうね。

inhumantsar 2025/07/17 03:09:57

記憶が正しければ君の言う通りだけど、そのコメントは紛らわしいね。Pythonでは、シングルスラッシュだとデフォルトでfloatになるから、ダブルスラッシュ(または型キャスト)が必要なんだ。ダブルスラッシュはintを返すんだよ。

hiAndrewQuinn 2025/07/16 12:29:24

正確には、Zozは「非常に資格のある応募者の約2/3」って言ってたよ。
「資格がある」≠「能力がある」。

hiAndrewQuinn 2025/07/16 06:27:19

頑張れ、LeetCoderの仲間よ!今は熱心にやってないけど、Ankiでいくつか練習問題は回してるよ。俺の最も右のコードはミニシリーズのパートIIIにあるよ、[1]。すごく似てるけど、俺は-1を最後のreturnに回してるんだ。以下にコードがあるよ。
def rightmost_bsearch(L, T):
l, r = 0, len(L)

while l < r:
# Loop invariants. Always true! Comment out in production!
for somewhat_smol in L[0:l]:
assert somewhat_smol <= T # Note: Weak inequality now.
for lorg in L[r:len(L)]:
assert lorg > T

mid = (l + r) // 2

if L[mid] > T:
r = mid
else:
l = mid + 1

return r - 1 # return the first element AFTER L[r:len(L)].

(技術的にはBEFOREであるべきだと思う。全ての右端バイナリサーチは逆配列の左端バイナリサーチでもあるから、AFTERでも実は間違ってない)
[1]: https://hiandrewquinn.github.io/til-site/posts/binary-search

zozbot234 2025/07/16 11:43:22

すぐ解けるかどうかじゃなくて、実行可能な解法にたどり着くまでの問題解決プロセスが重要なんだ。「このコードが本当に正しいことを証明してみろ」ってのがそのプロセスの一部なら、それは実務環境でも広く適用できる“豆知識”だよ。ランダムなLeetCodeアルゴリズムとは違ってね。アルゴリズム中心の面接では、「どうやってこれが正しいと証明するか考える?」って質問を入れるべきだと俺は思うね。

hoseja 2025/07/16 13:40:38

std::lower_boundstd::upper_boundの微妙な違いと、それがどこで重要になるかを思い出すのは、筋肉の記憶になるまではちょっと面倒だよね。

edanm 2025/07/16 21:40:04

ああ、そうそう、これはJavaの標準ライブラリに20年間も発見されずにあった“古典的な”バグだよ、記憶が正しければね。この話題は本当に興味深いし、共有してくれてありがとう。俺はバイナリサーチを最初からバグなしで書ける自信ないな :)

noosphr 2025/07/16 03:20:19

君の言う通りだね。でも、不変条件は、コンピュータ支援証明用のDSLを使わずに形式手法から得られる最大の費用対効果だよ。それらがバイナリサーチで機能するってことは、形式手法に馴染みのない人にとって、ほとんど全てのものに機能する(実際に機能するんだ)ってことの非常に強力なシグナルなんだ。

kamaal 2025/07/16 03:28:17

C、マイクロコントローラー、組み込みシステムでの経験からすると、アルゴリズム的に難しい課題でも1、2日で解決できるってさ。LeetCodeみたいな競技プログラミングは関係ないって。普通のプログラマーならできることだし、競技プログラマーが言うような「オフラインでコード書け」みたいな極端な状況はありえないって言ってるね。面接でそのことを突っ込んだら嫌がられたらしいよ。

powersnail 2025/07/16 03:26:39

質問の角度がすごく意外だな。なんか、コンパイル時専用のプログラムでも書いてんの?

もっとコメントを表示(1)
JohnMakin 2025/07/15 20:49:20

昔の大学院の授業で、記事と似た考え方に出くわしたよ。数学のテストでペンを使い始めたら、頭の中でしっかり考えてから書き出すようになって、高得点になったんだ。コード書くときも、どう動くか、どう動かせばいいか、頭の中でハッキリさせてから書くと、間違いが減るって言ってるね。

irchans 2025/07/17 11:35:32

8年生の時、代数の先生が「宿題をペンでやったら、間違い減るんじゃない?」って言ったらしい。それから45年、数学者になった今も95%はペンで数学してるって。どれだけ役立ったかは不明だけど、書き直しを嫌うから、書く前にもっと考えるようになったのかもね。

nayuki 2025/07/15 18:11:06

大学で理論コンピューターサイエンスを学んだんだ。記事の考えには同意するけど、実践は難しいって。事前条件と事後条件に加えて、ループ不変式と構造的帰納法がCSの証明に役立つって強調してるね。UofTのCSC236Hのノートも関連してるってさ。
https://en.wikipedia.org/wiki/Loop_invariant
https://en.wikipedia.org/wiki/Structural_induction
https://www.cs.toronto.edu/~david/course-notes/csc236.pdf#pa

sitkack 2025/07/15 18:57:25

あのノートは最高だね。David Liuって人がめちゃくちゃ良い人みたいだぞ。
https://www.cs.toronto.edu/~david/research.html

w3news 2025/07/16 07:26:02

良いプログラマーになるには、コードに小さな証明を書くべきだって。それってつまりテストと型(Types)のこと。特にテストを最初に書いて、次に型、最後にコードを書くのがいい。OpenAPIやGraphQLでAPIを定義して、その仕様で実行時にデータ検証するのも良い証明になる。コードより仕様の方が大事なんだってさ。

bmn__ 2025/07/16 08:19:18

記事の見出しにあった5つの特性は、良い型システムで表現できるって。そうすれば、仕様のほとんどがコードになって、コンパイラがその特性を保証してくれるようになる。OpenAPIとGraphQLは型が貧弱だから、今すぐにはこれに到達できないだろうってさ。

bluetomcat 2025/07/15 18:08:29

記事にある「頭の中で証明しろ」の裏には、「証明しやすいコードを書け」って意味もあるんだってさ。言うのは簡単だけど、実際にやるのは難しいと。自分で書いた新しいプロジェクトならまだしも、他の開発者が書いたfoo()やbar()、baz()がグローバルステートを変更するような既存のコードだと、証明するのはめちゃくちゃ難しいって言ってるね。

akavi 2025/07/15 18:17:15

良い開発者であるってことは、システムをそういう方向に進化させられるってことだよ。実際のシステムって結構ごちゃごちゃしてるけど、不変条件の穴を少しずつ減らしたり、後続のエンジニアが不変条件を意識して、それを守るようなコードを書きたくなるような「成功の落とし穴」を作ったりするのって、ちゃんと考えるべきなんだ。ドキュメントも役立つけど、コードの構造の方がもっと重要だと俺は思うね。

mprast 2025/07/15 18:23:20

うん、俺が「証明親和性」って言ってたのがまさにこれだよ。ちゃんと構造化されたコードベースって、自分が書いてないコードでも簡単に『これって正しい』って確信できるようなものなんだよね。

msteffen 2025/07/15 19:00:37

君のイライラ、よくわかるよ。可変なグローバル状態がダメなのは、それを読むコードが正しいって証明するために、プログラム全体の動きを知らなきゃいけないからなんだ。もしグローバル変数を不変にしたり、状態変数を関数の引数にしたり、あるいは可変なグローバル状態をヘルパークラスで囲んだりすれば、特定の関数の呼び出し元だけ知ればよくなる。関数の可視性を制限したり、アサーションで呼び出し元の振る舞いをさらに制約したりもできる。これら全部で、読み取り側のコードが正しいって証明しやすくなるんだ。ほとんどのプログラマーは無意識にやってると思うけどね。

bluetomcat 2025/07/15 19:27:32

この方向性は良いんだけど、できることには限りがあるんだよね。一部のコードは自然と関数型になるけど、他のものは本質的に命令型なんだ。たいてい、プログラムのトップレベル(イベントループとか)は命令型で、I/Oとか画面、ストレージみたいな状態を持つ『デバイス』を扱う。呼び出しグラフの『葉』の部分の関数は関数型にできるけど、トップが命令型だと、プログラム全体について推論することはまだ難しいよ。

thfuran 2025/07/15 22:44:46

そうだね、でも、そういう命令型の部分を全部トップレベルに追い出して、他の全てをクリーンに保てるなら、多くのプログラムの構造よりはるかに大きな改善になるよ。

jimbokun 2025/07/15 20:25:20

>foo()、bar()、baz()をユニット境界を越えて呼び出して、それらがグローバル状態を変更し、異なる開発者が書いた場合、この方法で何かを証明するのははるかに難しい。これって、記事のポイントをさらに強くしてると思うな。こういうコードはバグを含みやすく、さらにバグを追加せずにメンテナンスするのが、最初から『証明可能性』を目標に書かれたプログラムよりもはるかに大変なんだ。

monocasa 2025/07/15 22:00:52

実際、形式的に証明されたコードを見てみると、その80%は変更の複雑さを扱いやすいものに減らすことなんだ。これは、実際に証明するプロセスを踏まなくても価値のあることだよね。

hinkley 2025/07/15 20:50:12

俺、すごく古いチケットでコードベースのめちゃくちゃな部分を整理する作業を終えたばかりなんだ。最初は3つのチケットを直すつもりで入ったんだけど、作業中にさらに6つも見つけちゃって、自分で2つも追加で作ったよ。楽に済んだのか、それとも見落としがあったのか、正直わからないな。今やってる別のプロジェクトも似たような感じだよ。『ねぇ、みんな、大規模なメモリリークがあるの知ってた?』って感じ。

hinkley 2025/07/15 20:48:45

>彼らがグローバル状態を変更し、異なる開発者によって書かれている場合。癌が転移しちゃったら、治療計画はもっと攻撃的で、楽しくないものになるよね。多くの場合、患者はまだ救えるけど、それはたくさんの外部要因に左右されるんだ。

ericmcer 2025/07/16 04:14:28

これ読んでて、俺も常に自分のコーディングアプローチを見直して、『正しく』やる方法を何度も学び直してるなって思ったよ。John Carmackみたいな人でも、『ああ、俺は理解した』って感じでいるのかな、それとも彼も『5年前の俺はダメだった、今はもっと『うまく』やってる』って常に感じてるんだろうか。

saghm 2025/07/16 06:16:41

昔のコードを見直して改善案が浮かぶのは成長の証しだよ。成長が止まるとそういう発想も無くなるから気をつけな。最高のプログラマーでも完璧じゃないんだから、常に向上心を持つべきだね。学びを止めないって考えはマジで大事だよ。

xg15 2025/07/15 18:33:15

コードベースで「安全な場所」と「危険な場所」をマップ化できるんだよ。どの関数がグローバルな状態を変えるか、変えないか、頭の中で整理できるってことだね。

tristramb 2025/07/15 20:30:40

プログラムを証明ありきで設計する考え方は、1959年のT. J. Dekkerが相互排他問題を解決した時にまで遡るんだ。Edgser DijkstraがEWD1303 (https://www.cs.utexas.edu/~EWD/transcriptions/EWD13xx/EWD130…)でこの話をしていて、彼の後の研究もこの洞察から来てるんだよ。

gregorygoc 2025/07/15 18:03:26

正しい証明を書くのもプログラムの検証もすごく難しいよ。手動でやるなら意味ないと思うな。もし言語やコードベースに合ったイディオマティックなコードを書けば、不変条件や事前事後条件なんて考えなくて済むか、必要性がグッと減るよ。R. PikeとB. W. Kernighanの「The Practice of Programming」って本を読んでみて。彼らのモットーは「シンプルさ、明瞭さ、一般性」で、これは日々の仕事で超役立つよ。ちなみに競技プログラミングもコードを正しく書くテクニックを学ぶのに意外と良いんだよね。

xg15 2025/07/15 18:11:45

強く反対するよ。筆者は完全な形式証明を求めているわけじゃないと思うな。でも、自分のコードがどんな論理的性質(例えば、どんな不変条件が成り立つべきか)を満たすか理解しようとすれば、コードはうんと分かりやすくなるし、怖さもなくなるはずだよ。

Nevermark 2025/07/15 18:27:39

うん、「もっともらしい証明可能性の維持」って呼んでもいいかもね。想像上の証明すらできないようなコードは、もっと良いコードと切り離して考えるべきだね。

Sharlin 2025/07/15 18:41:49

型も一種の部分的な証明になるんだよね。強力な依存型システム以外では、ほとんどの実行時不変条件の証明まではできないけど、型で証明できる範囲だけでもすごく役立つよ。

gregorygoc 2025/07/15 19:08:04

そうそう、イディオマティックなコードなら、必要な不変条件は自然と備わってるってことだよ。

wahern 2025/07/16 06:29:40

「The Practice of Programming」は僕も昔買って、今でも関連性が高くて良い本だと思うよ。でも君の主張には同意できないな。君の提案は大事なアドバイスだけど、証明的な考え方をなくすんじゃなくて、むしろ補完するものでしょ。イディオマティックなコードは複雑なアルゴリズムやアーキテクチャの問題を直接解決しないけど、ノイズを減らして本質的な複雑さを際立たせる役には立つよね。

gregorygoc 2025/07/16 10:19:02

ある程度は補完し合うってのには同意するよ。僕が言いたいのは、良いコードを書けば、頭の中で複雑な論理をこねくり回す必要は最小限に抑えられるべきってこと。心の中でエッジケースとハッピーパスをテストするだけで十分なくらいにね。例えば、最近見た複雑なロジックは、早期リターンを使えばすごくシンプルになったはずなんだ。作者は分かっていても、そのテクニックに慣れてなかったんだろうな。そしたらもっと効率的に考えられたはずだよ。

bmn__ 2025/07/16 08:03:18

みんなが低評価したのは、議論を汚さずに自分で調べられたからだよ。答えはイエスで、両方の単語は「idios」、「own」、「self」、「private」と関係があるんだ。

auggierose 2025/07/16 10:24:58

コメントの投票についてコメントするのはやめてね。何の役にも立たないし、読んでてつまんないよ。https://news.ycombinator.com/newsguidelines.html

mprast 2025/07/15 18:21:04

うん、僕が考えてたのは、証明じゃなくて「証明のスケッチ」みたいなものだったんだ。

もっとコメントを表示(2)
wizzwizz4 2025/07/15 18:08:35

最初の段落を逆にして考えてみてよ。適切な抽象化(つまり、言語やコードベースのidiomaticなコード)を使えば、プログラムの検証は簡単になるんだ。
適切に抽象化されたプログラムを手作業で書いてるなら、ループ不変条件とか事前条件・事後条件を考える意味はあんまりないよ。だって、そのレベルの汎用性には存在しないからね。正しい証明は正しいコードから直接導き出されるんだ。

gregorygoc 2025/07/15 19:02:12

いや、適切な抽象化だけじゃ僕の議論には不十分なんだ。例えばC言語でidiomaticなループを書く方法が一つあるんだけど、それって構築によって必要な不変条件を引き継ぐんだ。
この本を読むことを強く勧めるよ。idiomaticなコードを書く概念について、僕が説明するよりずっと分かりやすく解説してるから。

chowells 2025/07/15 20:04:13

C言語のidiomaticなループがエッジケースで間違ってるせいで、どれだけバグが見つかってきたかを考えると、それってマジで面白い例だよね。
iからj(含む)まで昇順にsigned intをイテレートするループって、どうやってidiomaticに書くの?
jがINT_MAXのとき、そのループはどうなる?

wk_end 2025/07/15 20:30:25

頭の中で、あるいは紙にでも、小さい証明をスケッチする人でも、そのケースは見落としちゃうだろうね。C言語で算術演算するとき、普通の算術演算じゃないってことを忘れがちだから!

chowells 2025/07/15 21:16:29

C言語のループはマジで難しいよな。特に終了条件のチェックとインクリメントの順番が厄介だ。do/whileじゃダメで、結局break使うしかない。C言語(や似たような構文の言語)には、この正しい書き方ってのがないんだよな。

wizzwizz4 2025/07/16 00:30:46

俺様が偉そうに言うけどさ、int i = start; do thing_with(i) while (i++ <= end);って書けばいいんじゃね?

chowells 2025/07/16 01:04:53

それも考えたけど、「一般的に」って書いたのには理由があるんだよ。単純な+1とか–1以外じゃ無理だし、ポインタを追ったりする複雑な処理には向かない。余計な副作用も困るし、break使うのが一番だね。C言語の構文が悪いんだよな。他の言語だとこんな問題起きないんだけど。

trealira 2025/07/16 05:22:23

「一般的に」って言うなら、『Modern Compiler Design』からの汎用的なforループの実装を見てくれよ。goto使ってて複雑だけど、あらゆるケースに対応できるぜ。Dick GruneさんのコードをC言語に書き直したんだ。
// enumerate: print out numbers from “from” to “to”, inclusive, with step size “by”
void enumerate(intN_t from, intN_t to, intN_t by) {
uintN_t loop_count;
intN_t i;
if (by > 0) {
if (from > to) return;
loop_count = (to - from) / by + 1;
} else if (by < 0) {
if (from < to) return;
loop_count = (from - to) / -by + 1;
} else /* (by == 0) */ {
// Maybe some kind of error
return;
}
for (i = from; ; i += by) {
printf(“%d\n”, i);
if (--loop_count == 0) break;
}
}
慣用的なC言語のforループより複雑だけど、これが汎用解だよ。

Izkata 2025/07/16 14:25:53

あのi++ <= endだと、iが最後のループでendより1つ多くなるんじゃない?++i <= endi++ < endが良いと思うんだけどな。終了条件に<=を使うのは、だいたい間違いが多いから注意が必要だぜ。

chowells 2025/07/16 15:46:01

やっぱi++ != endが良いと思ったけど、INT_MAXを超えるsigned intの未定義動作(UB)に気づいたわ。C言語の専門家に聞いたら、インクリメントでUBが出ちゃうってさ。だから更新と条件チェックは完全に分けるべきだな。

trealira 2025/07/16 17:15:51

うん、符号付き整数のオーバーフローを避けるなら、こんな感じに書くしかないだろ。
assert(i <= j);
for (;;) {
do_something_with(i);
if (i == j)
break;
i++;
}

maxbond 2025/07/16 03:40:49

コードがきれいだと正しくなるってのは因果関係が逆だろ。問題を丁寧に考えれば、結果としてコードがきれいになるんだよ。きれいなコードが正しいって信じるのは「Cargo cult programming」だよ。もちろん、きれいなコードは大事だし、コードレビューと合わせれば正確性も上がるだろうけどさ。形は機能に従うんだ、逆じゃない。

monkeyelite 2025/07/15 22:04:23

「証明」ってのは、何か正しいってことを論証するための基本的な考え方だろ。細かいミスを避けるためじゃなくて、方向性が正しいことを示すのが目的だよ。

pipes 2025/07/15 18:14:55

競技プログラミングのテクニックについて、もっと詳しく教えてくれない?マジで興味あるんだけど!

kevinventullo 2025/07/16 00:19:43

競技プログラミングでいつも論理はわかるのに、バグが多すぎて時間内に終われない壁にぶち当たってるんだよね。本当にこれってあるあるだわ。

mathgradthrow 2025/07/15 19:29:57

プログラムを正しく書くこと以上に大事なことはないよ。もし正しいプログラムが欲しいなら、正しく書くしかないんだから。当然だよね。

wrs 2025/07/15 19:32:43

80年代にCarnegie Mellon大学の学部生だった頃、最初のプログラミングの授業で、記事にあるようなこと全部を教わったよ。再帰と帰納の同等性を知ってから、再帰アルゴリズムを作る時の「イライラする試行錯誤」がマジでなくなったのを覚えてるね。

TheShermanTank 2025/07/15 20:47:29

そのコース、去年取ったんだけどさ、関数型プログラミングを学んでたら、マジで良さが分かってきたんだよね。

xg15 2025/07/15 18:14:57

プロパティのリストに、mutabilityとimmutabilityも加えるべきだと思うな。状態をなるべくimmutableにしておくと、multithreadingでの問題が減るだけじゃなく、プログラムの取りうる状態を把握する時の頭痛の種もかなり減るからね。

jimbokun 2025/07/15 20:28:37

記事にはちゃんとそれらも含まれてるよ。

GMoromisato 2025/07/15 18:49:50

バグ修正のpost-mortemsを読むのがマジで好きでさ。Raymond Chenがよくやってるやつね。最近あったバグはC++でgarbage-collectedなオブジェクトを使うthread-poolでのcrash。最初はconcurrencyの問題だと思ってたけど、結局はlistenerがオブジェクトをちゃんとmarkしてなくて、GCで消されちゃったのが原因だったんだ。ChatGPTはconcurrencyの問題を推してたけど、違ったんだよね。

TacticalCoder 2025/07/15 19:13:44

1991年頃作ったゲームで、時々crashするバグに悩まされたんだ。再現性が低くて困ったから、ゲームの入力とロジックを全部deterministicに書き直してみたの。そしたら、player inputを記録してreplayできるようになって、ついにバグを再現!結局、dangling pointerのせいで、二発目のshotがメモリを壊してたんだよね。deterministicエンジンは当時珍しかったけど、今じゃ普通だよね。

GMoromisato 2025/07/17 21:32:42

入力全部保存するってのは極端だけど、結果的にはすごい効果あったんだね。いい話だわ。

agnishom 2025/07/16 06:31:55

頭の中で小さな証明を書くべきって、当たり前だよね?コード書くならそうすべきだし、コードの各部分が何をするのか、簡単な目標を持つべきなんだからさ。

記事一覧へ

海外テックの反応まとめ
著者
海外テックの反応まとめ
暇つぶしがてらに読むだけで海外のテックニュースに詳しくなれるまとめサイトです。