もうやらなくていい昔のコーディングテクニックあれこれ 496
縁側で茶飲み話 部門より
あるAnonymous Coward 曰く、
本家「Old-School Coding Techniques You May Not Miss」に、無くなって嬉しい昔のコーディングテクニックについてのストーリーが掲載されている。
ソフトウェア開発は複雑なものだが、年月とともにその開発プロセスは改善されてきたと言えるだろう。「熟練の」プログラマーであればマニュアルチューニングなどを行ったことも記憶に残っているだろう。しかし今日の開発ツールは、昔であれば手で書かなければならなかったような複雑な機能を自動的に行ってくれたりする。多くの開発者はこれを歓迎している。すでに若いナマイキな奴は、我々のような時代遅れの人間がこれらのことを手で行っていたと気付かないかもしれない。
Esther Schindler氏は古株プログラマーらに「頭痛の種だった昔のプログラミングテクニック」について尋ね、自身の経験も交えた記事をComputerWorldに掲載している。パンチカードとか、ハンガリアン記法とか、覚えているだろうか?
元記事に挙げられている「頭痛の種」には(バブルソートなどの)ソートアルゴリズム、リンクリストやハッシュテーブルの実装、GUIデザイン、「Go To」(そしてスパゲティコード)、マルチスレッドやマルチタスクのマニュアル実装、自己書き換えコード、ユリウス暦の変換等々が含まれている。
/.J諸兄方が昔を振り返ってみて「これは大変だった」と記憶に残っているものや、「今だったらもっと楽なのに」と思われるような懐かしい(?)思い出にはどんなものがあるだろうか? ちなみに本家では、ソートやサーチアルゴリズムなどの基本的な仕組みを理解していることがプログラマーとして重要かどうかといった話に発展してしまっているようだ。
てっきり (スコア:5, おもしろおかしい)
「もうやらなくてもいい昔のコーディング規約」かと。
ええ、もちろん大昔の話ですよ?
Re:てっきり (スコア:3, 興味深い)
このお題を見て、てっきりFortranの桁揃えのことかと。メインフレームメーカーは7桁目に縦線の入ったコーディング用紙を供給していましたね。フローチャートのテンプレートも供給していた。当時は富士通を使っていたので、FACOMってロゴ入りだった。
FACOMのFortranのテキストにFortranの歌も載っていたな。おおブレネリの替え歌。
ヤーッフォーッフォートランランランてやつ。
Re:てっきり (スコア:2, すばらしい洞察)
でも、「コーディング規約⊂コーディングテクニック」の様な気もします。
それなりの合理性が有るノウハウや、バグの作り込みの原因を分析結果を元に、決められたのが、コーディング規約な訳で……ま、「規約」なるモノの常として、大概の場合、決められた、その時から、形骸化が始まる訳ですが。
Re:てっきり (スコア:3, 興味深い)
これは今でもあっちこっちで見受けますよねー。
で、変更した人に詳細を聞きに行こうとしたら8割の確率で
「あ。その人とっくに辞めてますよ」 orz
だから、あんまり役に立たないんだよねー。
# ちなみに私も今は「とっくに辞めてますよ」の側だから、これ以上、つっこまない(笑)
clausemitz
異教徒どもめ (スコア:3, 興味深い)
リーナスおじさんの「タブ幅は8桁で決まり!」主張 [linux.or.jp]を誰かフォローしとくべきでは?
#この話題はおじさんホイホイなんだし
わかりやすい(無理やり短縮されていない)識別子 (スコア:3, 参考になる)
昔のコンパイラには識別子の長さについて制限のやたらきついものがあったので、無理やり短縮した名前にせざるを得なくなって自分でも何が何やらわけのわからない名前になってしまうことがありました。まぁアセンブラのニーモニックだってそのアーキテクチャに精通してない人にはたとえ規則性があったとしてもなんじゃこりゃ~なわけで、まぁあんな感じです。MASMだって識別子は確か31文字くらいに制限されていた時代があったはず。もっと古いアセンブラのラベルなんて8文字に制限されているものもあったり。
というわけで、「無理やり短縮した識別子」は「もうやらなくていい昔のコーディングテクニック」です。
屍体メモ [windy.cx]
Re:わかりやすい(無理やり短縮されていない)識別子 (スコア:2, 興味深い)
AppleSoft BASICは識別子の最初の二文字までしか有効でなかったので、気づかずに変数を書き換えてデバッグに苦労した思い出が蘇りました。
アセンブラが買えなかった子供の頃にはPeek/Pokeでハンドアセンブルとかやったなー(遠い目)
署名スパムがウザい?アカウント作って非表示に設定すればスッキリさ。
Re:わかりやすい(無理やり短縮されていない)識別子 (スコア:2, 参考になる)
FORTH [rubyist.net]というのも.
冗長でいいのでは?と言うか冗長でないと後工程が困る(Re:わかりやすい (スコア:3, すばらしい洞察)
関数の中身を書く場合には短い関数名でもいいのでしょうが、実際にその関数を使ってロジックを組む場合は冗長な位の関数名の方が組みやすいしメンテやバグ潰しもしやすい。
一つのロジックを見直すときに呼び出す関数自体のソースコードやコメント・ドキュメントを毎度毎度見ないといけないのは、煩雑な分見落としミスの温床になりやすい。
どっちかというと関数同士の引数の順序や法則性での統一性の維持に労力を割いた方が余程メンテを考えることになる。
関数内部のドキュメントの充実以前の問題です。ロジックの記述ミスや手順ミスを見つけるデバッグ側の立場からすると短い関数名でアレコレやっちゃう人は逆に困るんですが。
# つまりはメンテやる側がソースコードにアクセスする時の工数増やさないで欲しい。って事。
# 呼び出し先の関数については一回コメント読んだら後は関数内部のロジックの妥当性を見る必要が出るまで
# 一切見ないで済む位を目標にすべき。
# 余計な工数自体がメンテやバグ潰しの時のチョンボを誘発する大要因になる。
関数の命名規則として変な略語や日本語記述とか頭に機能種別をつけたり。というのは一人二人でやって作りきりで後にコード引き継がせる必要がないときにはいいのでしょうが引き継ぎとか考えるとParanoidなハンガリアン規則などでロジック自体の可読性を高める程度がちょうどいい場合も少なくないですよ。
# しかし、そういうプロジェクトに限って関数名16文字以内とか頭にアレコレつけろだつけないだと
# と言う所ばかりに拘ったコーディングを要求してくる_| ̄|○汎用機とか十五年前のアセンブラかと_| ̄|○
## それよりも引数の統一性と中身の可読性の方が問題だろうと。
時々思うんだが (スコア:3, 興味深い)
malloc/freeの処理コストってどれくらいかかるんだろう。メモリがバカ高かった時代はmalloc/freeで使用量を厳密に、というのはわかるんだが、ンGB当たり前の昨今、malloc/freeの処理コストの方が高くなったりしないのかな?と。「だいたい、こんくらいとっといてぇー」というようなプログラミングスタイルは無いのだろうか、と思う。
・・・malloc/free叩いとらんな、最近。
-- gonta --
"May Macintosh be with you"
Re:時々思うんだが (スコア:2, 興味深い)
>malloc/freeの処理コストの方が高くなったりしないのかな?
>と。「だいたい、こんくらいとっといてぇー」というようなプログラミングスタイルは無いのだろうか、と思う。
チマチマ何回かに分けて取るより一回にまとめて取った方が相対的に安くなる
ので、「だいたい、このくらい」でまとめて取ってから、それをアプリ内部で
チマチマ切り刻んで使うというテクニックは昔からあったと思います。
そしてJVMなんかだと、それを自動でやってくれるので、Javaな人にとっては
これも「もうやらなくていい昔のコーディングテクニック」の一つですね。
それから、OSが仮想記憶をサポートして以降は、mallocは実メモリを確保する
わけではなくOSの管理テーブルに予約するだけなので、ちょっとくらい大きい
領域を予約しても影響はすごく小さくなってるはずです。
Re:時々思うんだが (スコア:2, 参考になる)
>>そうか、Javaだと(よほど変なことをしない限り)メモリリーク心配しなくていいのか(本当?)
>いえいえ、お馬鹿なコードを書くとリークしていきますよ。
「メモリリーク」という単語を、
「ポインタ(或いは参照)を全て手放したにもかかわらず、領域の解放(free)を
忘れたために、メモリを少しずつ食い尽くしていく現象」
と定義するなら、Javaだと「メモリリーク」の心配はほぼありません。
#まさに「よほど変なことをしない限り」、且つ「VMやフレームワークにバグがない限り」。
#C言語で長期間動作するアプリを作るのが難しいのは、このような「狭義の
#メモリリーク」の検出とデバッグが難しいからで、Javaではそれが発生しない
#ことがサーバー向けアプリに適している理由でもあります。
少なくとも昔は「メモリリーク」というのはそういう意味で使われているものだと
思っていましたが、最近では参照を掴んだまま離さない
「到達可能なオブジェクト」=「(GC的に)生きているオブジェクト」
が生き残っているのも「メモリリーク」だと呼ぶ人はいます。
そういう「広義のメモリリーク」ならばJavaでも起こりえます。
#これはGCの基本的な特性なので、おそらく他のGCを持つ言語でも同じになる。
でもこれは言うまでもなくC言語でも他の言語でも起こることですね。そういう
コードしか書けない人は、いずれにせよC言語で長期間安定動作するようなコードは
書けないので、Javaにおける広義のメモリリークの心配をするには十年早いかと。
Re:時々思うんだが (スコア:3, 参考になる)
言葉の定義がどうであろうと起きている現象がなんであろうと
問題ですね。
このメモリリークの広義/狭義(あるいはメモリリーク/メモリリテンション)って
のは良く聞く話なのだけど運用者にとっては区別する意味が全くない。
この話を聞いて私が思い出す言葉は「五十歩百歩」です。
Re:時々思うんだが (スコア:3, 参考になる)
>昔ならいざ知らず、統合開発環境が主流な現在においてはどちらも容易。
>(ブレークポイント張りまくれるでしょ。スタックの中身ですら見えるでしょ。)
ブレークポイントはどこで何が発生するか分かってないと張っても無駄です。
(狭義の)メモリリークの難しい点は、それを発生させる条件を特定することや、
或いはそもそもメモリリークの有無を知ることそのものなんですよ。どういう
条件の時にメモリリークが発生するのかまで特定できていれば、その部分のバグに
ついては8割方片付いたも同じです。
ブレークポイントはその残りの2割のさらに一部を楽にできる程度の効果しかありません。
#そもそも統合開発環境がある前からデバッガなんてありましたよ。
#ブレークポイントをはったりもやってた。でも(狭義の)メモリリークは
#最も面倒なバグの一つとして有名だったのです。
>出来ない君集団にやらせるなら、何使わせても一緒じゃね!?
この部分についてだけは同意。
Re:時々思うんだが (スコア:2, 興味深い)
・言語としてのシェアがトップクラス
・Javaをクライアントサイドで使うことは少ない(携帯Javaはそこそこ多いけど)
ということを考えると、かなり多いのでは?
> 動的メモリを確保できる言語は、メモリリークの問題を常に持っている。
一見、動的メモリ確保をしていないように見えても、メモリリークすることがあります。
g++2.9.5(だったかな)で例外をcatchするとスタックが本来の位置より数bytesせり上がるというバグがあって、長期間動かすと徐々にプロセスのメモリ使用量が増えていくという現象に出くわしました。あれは怖かった。
Re:時々思うんだが (スコア:2)
malloc/freeはシステムコールなので呼ぶとコンテキストスイッチが発生します。
特にCPUのクロック数よりもL2キャッシュのヒット率とかのほうが性能的影響が
大きいようなメモリレイテンシにシビアなシステム、例えば、
マルチスレッドでサイズの大きいヒープを頻繁に生成/開放しながら
沢山のアクセスを受け付けるサーバーアプリみたいな処理系では、
コンテキストスイッチのオーバヘッドが馬鹿にならないので、
JVMや.Netのようにヒープの大枠確保しておいて、プロセス内部で
cpu user timeでやりくりするメモリ管理があったほうが性能的には有利。
というもんだと理解しとります
Re:時々思うんだが (スコア:2)
malloc/free はシステムコールではありません。必要であれば mmap や brk などで
ヒープの大枠を確保し、確保したヒープを切り分けること自体はユーザモードで
行います。
というわけで、
> JVMや.Netのようにヒープの大枠確保しておいて、プロセス内部で
> cpu user timeでやりくりするメモリ管理があったほうが性能的には有利。
> というもんだと理解しとります
JVMや.Net のメモリ管理方式には C に対してあなたが述べたような性能的な
アドバンテージはありません。
Re:時々思うんだが (スコア:2)
ページフォルトの影響を書き忘れてました。
そうだな。。。
ページフォルトが後からじんわり効いてくるのがいやな場合には
プログラムの初期化部分で大きめに malloc して memset してから
free しとく必要がありますね。
でも JVM でもこれを避けようとしたら JVM 自体の初期化処理で
同じことをやる必要があるはず。ですんでこれは性能的な
アドバンテージというよりは、プログラムを数~十数ステップ程度、
短くできるかどうかという問題ですね。
この意味でのアドバンテージなら確かにありますね。
昔のテクニック (スコア:3, 興味深い)
紙メディアにカッターナイフで穴をあけたり、ハサミとセロファンテープで切り貼りしたりなど、
物理的なテクニックは駆使してましたよ。さん孔機の順番待ちをするより早いですから。
あとはパンチャーのおばさんにお菓子を持って行くのも、ひとつのテクニックです。
〜◍
Re:昔のテクニック (スコア:3, 参考になる)
紙テープを繋ぐツールが色々とありましたね。でも、こんどは高速のテープリーダにかけると、繋いだところでテープがひっかかってちぎれたりするんですよ。
さん孔タイプライタで打ち込みに集中していたら、テープが引っかかって、横一列に穴が空いていただけっていうトラブルも見かけました。集中していても、たまには脇見をしましょう。
あと、忘れてならないテクニックが、メインフレームのラインプリンタは筐体が大きくて余裕があるので、ものを隠すには最適ってことです。オヤツとか隠しやすい ^-^)
Re:昔のテクニック (スコア:2)
>あと、忘れてならないテクニックが、メインフレームのラインプリンタは筐体が大きくて余裕があるので、
>ものを隠すには最適ってことです。オヤツとか隠しやすい ^-^)
あの頃のプリンタは五月蝿かったので防音ケースに入れるか隔離されてましたね。
#ぎりぎり紙テープを見たことのある世代
Re:昔のテクニック (スコア:2, おもしろおかしい)
パンチカードの束をうっかり落としてバラバラになっても、すぐ順番に並べられるように、束の側面に斜めの線をマジックで引いておく。
シフト演算 (スコア:3, 興味深い)
以前はコンパイラーが賢くなかったし乗算が遅かったので、 C 言語の入門書には「y = x * 8; でなく y = x << 3; を使いましょう」などと書かれていたものです。 z = x * 8 + 1; と等価のつもりで z = x << 3 + 1; と書いてバグったのも良い思い出です。
Re:シフト演算 (スコア:4, 参考になる)
除算も同じシフト演算で置き換えしてましたですよね。ただ、組み込み系のような、いつまで経っても進歩しないアホなコンパイラを使わざるを得ないケースとかですと、未だ現役の知識なのですw
それはさておき、
私の場合は、演算子の優先順位というのを頭に入れるのが面倒でしたので、四則演算の組み合わせでも括弧をつける癖をつけてました。10年前はアホだの無駄だのひどい扱いを受けていましたが、今は「コーディングの鏡」扱いされるのって、複雑な気分です。
余談ですが、CPUの命令によっては高級言語を意識した命令があって、例に挙げられたような n * { 2,4,8 } + { 0-7 } のような演算を1命令でこなせるモノもあり、コンパイラによってはちゃんと置き換えしてくれるものもありますね。昔はインラインアセンブラ使って人力コンパイルしたりしてました。
ほえほえ
68系は (スコア:2)
80系は裏レジを使いこなす。
のがコツなんて古すぎて、誰もわからないか。
Re:68系は (スコア:2, おもしろおかしい)
アマチュアは今でも癖のあるアーキテクチャのPICマイコン(8bit品)一辺倒で,姑息なコーディング・テクニックを競い合っている
Re:68系は (スコア:3, おもしろおかしい)
そんなごたくはいいから (スコア:2)
無敵のコーディング規約でバグの無いソフトウェアを最初から納品してくださいよぉぉぉぉぉぉ!!!
いや、まあ、昔に比べたらバグの原因が特定しやすくなってるみたいでありますが
これがコーディング規約の力なのか?
Re:そんなごたくはいいから (スコア:2, すばらしい洞察)
>これがコーディング規約の力なのか?
違います。
コーディング規約こそ、なんの役にも立たない御託そのものだと思います。
私の場合 (スコア:2, 興味深い)
586以降では命令キャッシュを破壊したりと不利益のほうが大きいので使う事はなくなりましたね、さすがに。
特殊なC++コンパイラでの話だけど (スコア:2, 興味深い)
for文の初期化項での変数スコープがfor文外でも有効だった件。
可搬性のために
#define for if(0); else for
とかやってたよね。
Re:リアルタイムグラフィック関連はずいぶん変わりました (スコア:4, すばらしい洞察)
その頃とは難しさの「質」が違いますよ。
今のシェーダプログラミングの「難しい」は、ライティングなどのモデルにおける数学や物理の難しさで、
昔のポリゴン描画の「難しい」は、キャッシュフェッチなどのスループット向上の難しさです。
昔のポリゴン描画は数学ではせいぜい外積までしか使わなかったため、そういう意味では敷居が低かったと思います。
Re:リアルタイムグラフィック関連はずいぶん変わりました (スコア:2, すばらしい洞察)
シェーダープログラミングが難しいのではなくて、シェーダーを利用する為の周辺環境を整えるのが大変なんですよ。
DCCツール上でなるべく実機での表示に近い状態でプレビューしたり、ゲーム中でどのシェーダーを使うか管理したりと、アーティスト密に連携を取る必要がある分、最速のソフトウェアレンダラーを追求するみたいなプログラマー内で完結する仕事の方がお気楽ですよ。
Re:BASICあつまれー (スコア:3, 参考になる)
昔のBASICの高速化のテクニック(?)として、
・プログラムの最初でDEFINT A-Z
・よく実行されるコードは最初の方においておく (行番号を先頭から線形探索しているから?)
・変数名は短くする
・複数行はマルチステートメント化する
・IF I=29 THEN X=X+1 ELSE IF I=30 THEN X=X-1の代わりにX=X+(I=30)-(I=29)とする。
とかいうのを思い出しました。
Re:BASICあつまれー (スコア:3, 興味深い)
MSX-BASICだと、かえって遅くなるんですよ、これ。
他のBASIC実装だと、速かったんでしょうか?
Re:BASICあつまれー (スコア:2, 興味深い)
NEC系は確か速くなったはず、と思ってN88-BASICで確かめてみると、IFを使う場合が10000回で29秒であったのに対して、論理式の方は10000回で33秒となりかえって遅くなってました。
これは高速化のための方法ではなく、N60のようなIF構文が弱い(ELSEが使えない)場合に書きやすいって話だったのかもしれません。
Re:BASICあつまれー (スコア:2, 興味深い)
条件によって処理速度がばらつかないようにという理由もあったように思います。
例のコードみたいなのって、入力に従ってキャラクタを上下左右に動かすとかだったりするんだけど、IFの羅列で書くと、上への移動と下への移動の速度が違うなんて現象が起こってしまうとか。
Re:BASICあつまれー (スコア:2)
それらは実装次第だから、処理系によるね。
例えば、For ~ Next ~は、Fortran時代からよく語られたテクニックだけど、ちゃんと最適化していたら違いはないはずだからね。
C/C++はナイフとロープ (スコア:2, すばらしい洞察)
普段使いはPythonとJavaとC#ですが、やはりいざって時にC/C++は頼りになる。
なんというか、まぁナイフとロープみたいなもんじゃないでしょうか。
日常生活ではもっと各種用途に便利な機能を備えた道具がたくさんあるけど、
極限の状態でこの道具があったから生き残れた、みたいな。
まぁ懐中電灯ですら金属製だと武器とみなされて軽犯罪法違反で
警察官にしょっ引かれるらしいので、ナイフなんて所持してたら
警察官に何されるかわかったもんじゃないですが。
屍体メモ [windy.cx]
Re:C/C++はナイフとロープ (スコア:2, 参考になる)
なんというか、止め時ってのは大事だよね。
署名スパムがウザい?アカウント作って非表示に設定すればスッキリさ。
Re:C/C++そのもの (スコア:2, 参考になる)
高木先生の到着が遅れているようですので張ってみます。
元ストーリーに沿った話だとこんな所が。
# しかし、もう10年前の話になるのか。。
Re:ハンガリアン記法とか (スコア:3, 参考になる)
char szHogeHoge[64]のようなシステムハンガリアンを使うことについては、僕も大嫌いですが。
Re:いや、存じてるでしょ (スコア:2, すばらしい洞察)
プログラミングの経験の少ない人は型でなんでも出来ると思いがちなんだけど、例えば
pxA + pxB という式は要注意だが、
(pxA + pxB) / 2 という式は平均を取っているのでたぶん問題なし、
ということを読み取りたいわけ。
ハンガリアンの目的はコードの可読性を高めることにあるので、型とは目的からして違うんだよ。
Re:いや、存じてるでしょ (スコア:2)
> おけば良い物に人間が煩わされるなんて馬鹿馬鹿しい。
コードレビューを行うときにレビュアーにとって便利なんです。
プログラムというのはあなたとコンパイラだけが読むものではありません。
Re:ハンガリアン記法とか (スコア:2)
自分ではやりませんが、人がやっている分にはスルーする事にしています。
ただ、
Windows界隈ですが、変数名ではなく型名にハンガリアン記法(っぽい)定義がされているのが、よくあります。
符号無し32ビット整数である DWORD に対して、それのポインタである LPDWORD が定義されているわけです。
これだと頭が混乱するので、素直に DWORD * と書いて欲しいわけです。
ポインタ値を“ポインタ返し”関数があったとします。たとえば以下のようなものです。
void hoge (void **p);
これをマイクロソフト流に書くと、
void hoge (LPVOID *p);
となります。この辺で、かなりイラっときます。
文字列ポインタは、マルチバイト文字かワイド文字か、constの有り無しで、以下のように定義されています。
LPSTR
LPCSTR
LPWSTR
LPCWSTR
ときどき LPCWSTR * なんてのが出てきます。この辺で、殺意を覚えます。
素直に、
char *
const char *
wchar_t *
const wchar_t *
って書いてくれれば、混乱しなくて済むのに。
Re:とりあえず (スコア:2, 参考になる)
そのへんの制限はGecko 2で一気に取っ払われる予定で、たとえば今nsresultを使っていちいち戻り値を判定しているものはC++例外処理に置き換えられたりするようです。
昔はキャスト演算子すら満足にそろっていないコンパイラが多くてマクロで代用していましたが(NS_REINTERPRET_CASTとか)、これはだいぶ前に解禁されてマクロは廃止されました。64ビット整数も同様の理由でサポートしていない環境では構造体にtypedefされる特別な型を使ったりしていたのですが、現在は解禁されています。
Re:最近はいない? (スコア:2, 興味深い)
#使用言語はC言語です。
#「もうやらなくていい」というより「使ってもよさそうなのに禁止」されています。
禁止になった理由は、コメントの最後に「\」となるマルチバイト文字をつかってしまい
(本当はコメントにしたくなかった)次の行までコメントとなってしまったからです。
#こういうのを「馬鹿基準」と言ってる人もいますね。
#低レベルな人に合わせて無駄な禁止事項が増えていくという。
無駄なコーディング規約より「DRY原則遵守」で。
Re:ループにif文 (スコア:3, すばらしい洞察)
そのコードなら ADD c, c, b だけで……いえ、何でもありません。
Re:DOS上のCでの話 (スコア:2)
Hugeが使えるQuick Basicで書き直す羽目に。
Re:クロック数を数えた (スコア:3, すばらしい洞察)
最適化が必要でないところでは、徹底的にサボりますが…。
今でもアセンブラで最適化する際は、普通にクロック数を数えますし、
投機的実行や予測分岐、アウトオブオーダー実行を行うCPUの場合は、
それぞれの実行状態を予測して至近値を求めたりします。
後、昔はやらなくて良かったメモリレイテンシによる遅延なんかも、
細かく算出して最適な挙動を考えなきゃいけません。
レジスタページングを考慮した組み方によって、数クロックを稼ぐなんて
当たり前ですから。
最適化をやろうとしたら、昔よりはるかに面倒くさいです(^^;