実際のやり過ぎのスパゲッティコードの短い例を投稿して、おそらくそれが何をするのかを言ってもらえますか?デバッガの悪夢を見せてくれませんか。
[〜#〜] ioccc [〜#〜] コード、つまり空想科学小説という意味ではありません。私はあなたに起こった実際の例を意味します...
焦点が「スパゲッティコードを投稿する」から「正確にスパゲッティコードとは何ですか?」に変更されました。歴史的な観点から、現在の選択肢は次のように思われます。
私にとって、スパゲッティコードのもっとモダンの例は、20個のdllがあり、すべてのDLLが何らかの方法で相互に参照している場合です。依存関係グラフは次のようになります。巨大なブロブ、そしてあなたのコードは実際の順序なしで至る所でホップします。すべては相互に依存しています。
私はこれを頭から引き出していません。これは、単純化されていますが、私が作業しなければならなかったものです。基本的に列挙型を必要とするプログラムがあるとしましょう:
enum {
a, b, c;
} myenum;
しかし、代わりに私たちが持っているのは
HashTable t;
t["a"] = 0;
t["b"] = 1;
t["c"] = 2;
しかしもちろん、ハッシュテーブルの実装は十分ではないため、ハッシュテーブルのローカル実装があります。これには、機能が半分でバグ数が2倍の平均的なオープンソース実装の約10倍のコードが含まれています。 HashTableは実際には仮想で定義されており、HashTableのインスタンスを作成するためのファクトリHashTableFactoryがありますが、パターンHashTableFactoryも仮想です。仮想クラスの無限のカスケードを防ぐために、関数があります
HashTableFactory *makeHashTableFactor();
したがって、コードが必要とするすべての場所myenumは、より多くのHashTableを作成する場合に備えて、HashTableおよびHashTableFactoryのインスタンスへの参照を保持します。しかし、待ってください、それだけではありません!これはハッシュテーブルが初期化される方法ではありませんが、XMLを読み取るコードを記述することによって行われます。
<enum>
<item name="a" value="0"/>
<item name="b" value="1"/>
<item name="c" value="2"/>
</enum>
ハッシュテーブルに挿入します。ただし、コードは「最適化」されているため、ASCIIファイルmyenum.xmlは読み取られませんが、代わりに、以下を生成するコンパイル時スクリプトがあります。
const char* myenumXML = [13, 32, 53 ....];
myenum.xmlから、ハッシュテーブルは次の関数によって初期化されます。
void xmlToHashTable(char *xml, HashTable *h, HashTableFactory *f);
これは呼ばれます:
HashTableFactory *factory = makeHashTableFactory();
HashTable *t = facotry.make();
xmlToHashTable(myenumXML, t, f);
さて、列挙型構造を取得するためのコードがたくさんあります。これは基本的に関数で使用されます。
void printStuff(int c) {
switch (c) {
case a: print("a");
case b: print("b");
case c: print("c");
}
}
これは、次のコンテキストで呼び出されます。
void stuff(char* str) {
int c = charToEnum(str);
printStuff(c);
}
だから私たちが実際に持っているのは
void stuff(char *str) {
printf(str);
}
上記の3の代わりに、数千行のコード(プライベートな新しい、バグのある、複雑な、ハッシュテーブルの実装、xmlリーダー、およびライター)を生成するように管理しました。
Ravioli Code もありますが、これは逆です。機能の素敵な小さな塊、肉の良さをきちんと包み込んだすっきりとしたインターフェース、すべてがフレームワークの素敵なソースに座っていました。
実際のスパゲッティコードはCOBOLで実行され、ALTERステートメントを使用しました。
これが 例 ですが、「ユーモア」がリストされていますが、私はこの種のことを見てきました。 Alterステートメントを含むプログラムが明らかに罪の状態にあることに気付いたために、ほとんど一度解雇されました。私はそのプログラムを「維持」することを拒否しました、それを理解するよりもそれを置き換える方が迅速でした。
Linux SCSIドライバーから(有罪を保護するために無名のままにする必要があります):
wait_nomsg:
if ((inb(tmport) & 0x04) != 0) {
goto wait_nomsg;
}
outb(1, 0x80);
udelay(100);
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
goto wait_io;
}
}
goto TCM_SYNC;
wait_io:
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x81) == 0x0081) {
goto wait_io1;
}
}
goto TCM_SYNC;
wait_io1:
inb(0x80);
val |= 0x8003; /* io,cd,db7 */
outw(val, tmport);
inb(0x80);
val &= 0x00bf; /* no sel */
outw(val, tmport);
outb(2, 0x80);
TCM_SYNC:
/* ... */
small_id:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto small_id;
}
G2Q5: /* srch from max acceptable ID# */
k = i; /* max acceptable ID# */
G2Q_LP:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto G2Q_LP;
}
G2Q_QUIN: /* k=binID#, */
この宝石をどのように見つけましたか?
find /usr/src/linux -type f -name \*.c |
while read f
do
echo -n "$f "
sed -n 's/^.*goto *\([^;]*\);.*/\1/p' $f | sort -u | wc -l
done |
sort +1rn |
head
出力は、次のように、gotoの数順にファイルを個別のラベルにリストする一連の行です。
kernel/fork.c 31
fs/namei.c 35
drivers/infiniband/hw/mthca/mthca_main.c 36
fs/cifs/cifssmb.c 45
fs/ntfs/super.c 47
オブジェクト指向スパゲッティに言及することを忘れないでください。これは、意味がない場合でも、本のすべてのデザインパターンを使おうとするときです。これにより、概念レベルのスパゲッティコードが作成されます。これは、従来のgotoベースのスパゲッティコードよりも品質にはるかに悪影響を及ぼします。
あなたはそれを求めました、あなたはそれを得るでしょう:
これは、美しく青きドナウのワルツを再生するDOS.comファイルのソースです。実行可能ファイルのサイズはわずか176バイトです。コードはデータとして再利用され、その逆も同様です。
.286
.model tiny
g4 equ 55-48 ; removed note-decoding !
a4 equ 57-48 ; now: storing midi-notes for octaves 0..2 and convert
h4 equ 59-48 ; to 4..6 with a simple add 48.
c5 equ 60-48
d5 equ 62-48
e5 equ 64-48
g5 equ 67-48
h5 equ 71-48
c6 equ 72-48
d6 equ 74-48
e6 equ 76-48
g6 equ 79-48 ; = 00011111b
pp equ 0 ; c4 is not used in the walz, using it as play-pause.
EOM equ 1 ; c#4 is also available... End Of Music
; warning: experts only beyond this point !
pau1 equ 00100000b ; bitfield definitions for note-compression
pau2 equ 01000000b ; you can or a pau to each note!
pau3 equ 01100000b
;rep1 equ 01000000b ; rep1 is history (only used once).
;rep3 equ 11000000b ; rep3 was never used.
rep2 equ 10000000b ; or a rep2 to a note to play it 3 times.
drumsize equ 5
.code
org 100h
start:
mov ah,9
mov dx,offset msg
int 21h ; print our headerstring
mov dx,0330h ; gus midi megaem -port
mov si,offset music_code ; start of music data
mainloop:
; get new note (melody)
xor bp,bp ; bp= repeat-counter
lodsb ; get a new note
cmp al, EOM ; check for end
jne continue
ret
continue:
jns no_rep2 ; check for rep2-Bit
inc bp
inc bp ; "build" repeat-counter
no_rep2:
Push ax ; save the note for pause
; "convert" to midi-note
and al,00011111b
jz skip_pp ; check pp, keep it 0
add al,48 ; fix-up oktave
skip_pp:
xchg ax,bx ; bl= midi-note
play_again:
mov cl,3
Push cx ; patch program (3= piano)
Push 0c8h ; program change, channel 9
; wait (cx:dx) times
mov ah,86h ; wait a little bit
int 15h
; prepare drums
dec di ; get the current drum
jns no_drum_underflow
mov di,drumsize
no_drum_underflow:
; play drum
Push dx ; volume drum
Push [Word ptr drumtrk+di] ; note drum
mov al,99h
Push ax ; play channel 10
; play melody
Push dx ; volume melody
Push bx ; note melody
dec ax ; replaces dec al :)
Push ax ; play channel 9
; send data to midi-port
mov cl,8 ; we have to send 8 bytes
play_loop:
pop ax ; get the midi event
out dx,al ; and send it
loop play_loop
; repeat "bp" times
dec bp ; repeat the note
jns play_again
; check and "play" pause
xor bx,bx ; clear the note, so we can hear
; a pause
; decode pause value
pop ax
test al,01100000b
jz mainloop ; no pause, get next note
; decrement pause value and save on stack
sub al,20h
Push ax
jmp play_again ; and play next drum
; don't change the order of the following data, it is heavily crosslinked !
music_code db pp or rep2
db g4 or rep2 or pau1
db h4 or pau1, d5 or pau1, d5 or pau3
db d6 or pau1, d6 or pau3, h5 or pau1, h5 or pau3
db g4 or rep2 or pau1
db h4 or pau1, d5 or pau1, d5 or pau3
db d6 or pau1, d6 or pau3, c6 or pau1, c6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3
db e6 or pau1, e6 or pau3, c6 or pau1, c6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3
db e6 or pau1, e6 or pau3, h5 or pau1, h5 or pau3
db g4 or rep2 or pau1
db h4 or pau1, g5 or pau1, g5 or pau3
db g6 or pau1, g6 or pau3, d6 or pau1, d6 or pau3
db g4 or rep2 or pau1
db h4 or pau1, g5 or pau1, g5 or pau3
db g6 or pau1, g6 or pau3, e6 or pau1, e6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3, pp or pau3
db c5 or pau1, e5 or pau1, h5 or pau3, pp or pau3, d5 or pau1
db h4 or pau1, h4 or pau3
db a4 or pau1, e5 or pau3
db d5 or pau1, g4 or pau2
; db g4 or rep1 or pau1
; replace this last "rep1"-note with two (equal-sounding) notes
db g4
db g4 or pau1
msg db EOM, 'Docking Station',10,'doj&sub'
drumtrk db 36, 42, 38, 42, 38, 59 ; reversed order to save some bytes !
end start
簡単に言うと、スパゲッティコードとは、実行の次の投稿を追跡できない、または少なくとも1つのアクションに応答して次のポイントがどこに行くのかを判断するのが難しいプログラミング言語のコードです。
Realスパゲッティコードには、ローカル以外の多数のgotoが必要です。悲しいことに、これはほとんどの現代言語では不可能です。
編集:GOTOの代わりに例外とlongjmpを提案する人もいます。ただし、これらはfarに制限され、構造化されています。これは、コールスタックを返すことしかできないためです。 Real GOTOを使用すると、プログラム内のany行anywhereにジャンプできます。これは本物のスパゲッティを作るのに必要です。
これは、先ほど書いたMIDIパーサーからのものです。これは、すばやく汚い概念実証でしたが、それでも、その醜さのせいにします。4つのレベルのネストされた条件とこのコードは、2つのMIDIイベントを比較して、ファイルに書き込むときに優先度で並べ替えることを目的としていました。醜いのですが、それでもうまくいきました。
internal class EventContainerComparer : IComparer {
int IComparer.Compare(object a, object b) {
MIDIEventContainer evt1 = (MIDIEventContainer) a;
MIDIEventContainer evt2 = (MIDIEventContainer) b;
ChannelEvent chanEvt1;
ChannelEvent chanEvt2;
if (evt1.AbsoluteTime < evt2.AbsoluteTime) {
return -1;
} else if (evt1.AbsoluteTime > evt2.AbsoluteTime) {
return 1;
} else {
// a iguar valor de AbsoluteTime, los channelEvent tienen prioridad
if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is MetaEvent) {
return -1;
} else if(evt1.MidiEvent is MetaEvent && evt2.MidiEvent is ChannelEvent){
return 1;
// si ambos son channelEvent, dar prioridad a NoteOn == 0 sobre NoteOn > 0
} else if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is ChannelEvent) {
chanEvt1 = (ChannelEvent) evt1.MidiEvent;
chanEvt2 = (ChannelEvent) evt2.MidiEvent;
// si ambos son NoteOn
if( chanEvt1.EventType == ChannelEventType.NoteOn
&& chanEvt2.EventType == ChannelEventType.NoteOn){
// chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
if(chanEvt1.Arg1 == 0 && chanEvt2.Arg1 > 0) {
return -1;
// chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
} else if(chanEvt2.Arg1 == 0 && chanEvt1.Arg1 > 0) {
return 1;
} else {
return 0;
}
// son 2 ChannelEvent, pero no son los 2 NoteOn, el orden es indistinto
} else {
return 0;
}
// son 2 MetaEvent, el orden es indistinto
} else {
return 0;
}
}
}
}
スパゲッティコード:特定のパスタ料理の代替レシピとして60年代初頭にイタリアで生まれたスパゲッティコードは、誰にでもできるメインディッシュの作成を自動化しようとした1人のレストラン起業家によって作成されました。設計を完了するための時間の不足に押されて、エンジニア/シェフは早い段階でレシピに問題をもたらしたコーナーを切りました。うまくいかなかった良いアイデアを改善するための必死の試みで、レシピが制御不能になるにつれて、さまざまなスパイスがすぐに調合に追加されました。その結果、ひもでねじれた、しかし潜在的においしいテキストの山ができ、後に世界中の開発者が大事にする慣習に成長しました。
Flex/Bisonスキャナーとジェネレーターによって生成されたコードを見たことがありますか?多数のラベルとプリプロセッサディレクティブ。
中身を理解することは絶対に不可能です。そしてプログラムの流れを追うことは絶対に不可能です。
それは間違いなくスパゲッティコードです。