第5章 実装(Forthスタイルの要素)

正しく書かれていないForthは、「ゴミ圧縮機を通過したコード」のように見えると非難されてきました。事実、Forthは、アプリケーションをより自由に書くことができます。しかし、一方でその自由度は、私たちが良きForthスタイルの要素を意識的に採用するなら、私たちに絶妙に読解可能で容易に保守可能なコードを書く機会も与えます。

この章では、Forthのコーディング規約について詳しく説明します。

  • リストの整理
  • スクリーンレイアウト、空白開け、字下げ
  • コメント
  • 名前の選び方

私は、誰のためにもなる、最も速くて速い規約のリストを推薦することができれば、と思います。 残念ながら、そのようなリストは多くの状況で不適切な場合があります。 この章では、広く採用されている多くの規則と個人的な好みを組み合わせ、別のアイデアや好みの理由についてコメントしています。 言い換えると、

: TIP  VALUE JUDGEMENT ;

私は特にキム・ハリスに感謝しています。彼は素晴らしいForthスタイルについて意見の相違を統一する努力を続け、この章で説明されている規約の多くを提案してくれました。

リストの整理

よく整理されている本には、明確に定義された章、明確に定義された節、および一目で構成を見るのに役立つ目次があります。 よく整理されている本は読みやすいです。 本がよく整理されていないと、理解が難しくなり、後で情報を探すのはまず不可能になります。

_images/fig5-1.png

図 50 私は、これらのプログラミングコンベンションが一体どうして読みやすさを高めるのか未だに分かりませんなぁ。

優れた整理の必要性は、アプリケーションリストにも当てはまります。 良い整理には3つの側面があります。

  1. 分解
  2. 構成
  3. ディスク割当て

分解

すでに見たように、リストの整理はアプリケーションの用語集への分解に従うべきです。 一般に、これらの用語集は「使用」順に並べられるべきです。 使用されている用語集は、それらを使用している用語集の前に置くべきです。

大規模なアプリケーションでは、リスト内の要素は複雑さの度合いによって整理されるべきであり、最も複雑なバリエーションはその終わりに現れます。最後のスクリーンを表示せず(つまりロードしないで)、より高度な機能がないことを除けば適切に動作する自給自足のアプリケーションを使用できるようにすることをお勧めします。

分解の技術については、 第3章 で詳しく説明しました。

構成

構成は全体を作成するための部分の組み合わせです。 良い構成は、良い分解と同じくらいの腕前が必要です。

Forthの現在の慣習の1つは、ソースコードが1Kの大容量記憶装置の「スクリーン(screen)」上にあるということです(「スクリーン」という用語は、ソースコード専用に使用されるブロック(block)を指します)。(。 Forthでは、長い羊皮紙スクロールのようにリスト全体を線形にリンクすることで、コードのすべてのスクリーンを次のスクリーンにチェインすることができます。 これは便利なアプローチではありません。 代わりに、

ヒント

アプリケーションのリストの構造を本のようにします。つまり階層化です。

1つのアプリケーションは以下で構成されます。

(複数の)スクリーン
Forthのソースの最小単位です。
(複数の)用語集
1から3スクリーンは、1つのコンポーネントを実装するのに充分な大きさです。
(複数の)章
一連の関連する用語集。そして、
(複数の)一括ロード用スクリーン
目次と同じように、章を適切な順序でロードするスクリーンです。

アプリケーション・ロード・スクリーンの例

リスト 4 Screen #1
 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
\ QTF+ Load Screen                                      07/09/83
: RELEASE#   ." 2.01" ;
  9 LOAD  \ compiler tools, language primitives
 12 LOAD  \ video primitives
 21 LOAD  \ editor
 39 LOAD  \ line display
 48 LOAD  \ formatter
 69 LOAD  \ boxes
 81 LOAD  \ deferring
 90 LOAD  \ framing
 96 LOAD  \ labels, figures, tables
102 LOAD  \ table of contents generator

アプリケーション・ロード・スクリーン

リスト 4 は、アプリケーション・ロード・スクリーンの例です。 スクリーン1にあるので、以下のとおり入力してこのアプリケーション全体をロードできます。

1 LOAD

このスクリーン内の個々のロードコマンドはアプリケーションの章をロードします。 たとえば、スクリーン12は、ビデオプリミティブの章のロード・スクリーンです。

参照ツールとして、アプリケーション・ロード・スクリーンでは、すべての章の場所がわかります。 たとえば、フレーミングを実行するルーチンを見たい場合は、その節がスクリーン90から始まることがわかります。

各章のロードスクリーンは、順番に章を構成するすべてのスクリーンをロードします。 章ロードスクリーンのフォーマットについてはすぐ後で検討します。

この階層構造の主な利点は、アプリケーション全体をロードしなくても、任意の節たは任意のスクリーンを単独でロードできることです。 ソースコードのモジュール性は、Forthの編集・ロード・テストのための短いターンアラウンドタイム(反復アプローチのために必要)の理由の1つです。 本のページと同様に、各スクリーンにも個別にすばやくアクセスできます。 これはソースコードのメンテナンスに対する「ランダムアクセス」アプローチです。

ロード・スクリーンでスクリーン番号を変更するだけで、コードの一部を新しい試用版に置き換えることもできます。 ファイル内で大量のソースコードを移動する必要はありません。

小さなアプリケーションでは、章のようなものがないかもしれません。 アプリケーション・ロード・スクリーンはすべての用語集を直接ロードします。 ただし、大規模なアプリケーションでは、階層を追加することで保守性を向上させることができます。 スクリーンは混合ではなく、ロード・スクリーンまたはコード・スクリーンのどちらかにする必要があります。 定義が表示されているスクリーンの途中に LOADTHRU コマンドを埋め込むのは避けてください。それを行うのは「何か必要」または「スクリーンを使い果たした」という理由の時だけにしてください。

スキップ・コマンド

以下の2つのコマンドを使用すると、各スクリーンで何をロードし、何を無視するかを簡単に制御できます。

\
\S ( also called EXIT)

\ は「スキップ・ライン」(skip-line)と発音します。それはForthインタプリタに同じ行のそれの右側にあるすべてを無視させます( \ はForthのワードであるため、空白を続ける必要があります)。末尾の区切り文字は不要です。

リスト 4 では、\ が2つの方法で使われているのがわかります。スクリーンコメント行(行0)の始めと、個々の行のコードの後ろのコメントの始まりとしてです。

テスト中に、 \ は、すでに名前やコメントに右括弧が含まれている行を一時的にコメントアウトするのにも役立ちます。 例えば、以下の2つの「スキップ・ライン」は、右括弧に出くわすことによる問題を引き起こすことなく、 NUTATE の定義がコンパイルされるのを防ぎます。

\ : NUTATE  ( x y z )
\   SWAP ROT  (NUTATE) ;

\S は「スキップ・スクリーン(skip-screen)」と発音します。これはForthインタプリタに、 \S を超えて以降、そのスクリーンに何もなかったかのように、 \S 以降のスクリーン内の解釈を完全に停止させます。

多くのForthシステムでは、この機能はセミコロンのためのランタイムルーチンである EXIT と同じです。 これらのシステムでは EXIT の使用は受け付けられます。ただし、Forthシステムの中には、内部的な理由から「スクリーンのスキップ」機能に対して別のルーチンを必要とするものがあります。

\\S の定義は 付録C にあります。

章ロード・スクリーン

リスト 5 は典型的な章ロード・スクリーンを示しています。 ここでロードされるスクリーンは相対番号指定です。アプリケーション・ロード・スクリーンでの絶対番号指定とは異なります。

これは、章ロード・スクリーンが章内の連続したスクリーン範囲の最初のスクリーンであるためです。 リスト内で章全体を前後に移動できます。 章ロード・スクリーンの相対ポインタは絶対位置に依存しません。 変更する必要があるのは、アプリケーション・ロード・スクリーンの1つの数字だけで、章の先頭を指しています。

章ロード・スクリーンの例

リスト 5 Screen #100
0
1
2
3
4
5
6
7
8
\ GRAPHICS                 Chapter load                 07/11/83

 1 FH LOAD            \ dot-drawing primitive
 2 FH 3 FH THRU       \ line-drawing primitives
 4 FH 7 FH THRU       \ scaling, rotation
 8 FH LOAD            \ box
 9 FH 11 FH THRU      \ circle

CORNER  \ initialize relative position to low-left corner

ヒント

アプリケーション・ロード・スクリーンでは絶対スクリーン番号を使用してください。 章ロードまたは節ロード・スクリーンでは、相対的なスクリーン番号を使用してください。

相対ロードを実装する方法は2つあります。 最も一般的なのは以下です。

: +LOAD  ( offset -- )  BLK @ +  LOAD ;

そして

: +THRU  ( lo-offset hi-offset -- )
     1+ SWAP DO  I +LOAD  LOOP ;

私流の、より有用なファクタリング(要素分解)として提示する方法は、単一のワード FH を必要とします(その定義については 付録C 参照)。

それはこんなフレーズです。

1 FH LOAD

「1 From Here load」(ここから1へだたったスクリーンをロード)と読みます。これは「1 +LOAD 」と同じです。

同様に、

2 FH   5 FH THRU

「2 from here, 5 from here THRU 」(ここから1へだったったところから、ここから5へだたったところまで、通しで)と読みます。

プログラマの中には各章をダミーのワードで始める人もいます。

: VIDEO-IO ;

アプリケーション・ロード・スクリーンで章がロードされている行のコメントにその名前をリストします。 これにより、章自体を見なくても、任意の章を選択して、 FORGET してその場所からリロードできます。

章の中では、最初のグループのスクリーンは通常、章の中でグローバルに必要とされるそれらの変数、定数、および他のデータ構造を定義します。 それに続いて「用途」順にロードされる用語集が来るでしょう。 章ロード・スクリーンの最後の行は、通常、必要な初期化コマンドを呼び出します。

よりスタイルを意識したForth職人の中には、各章の冒頭に、その章で説明されているコンポーネントの動作を一般的な用語で説明したものがあります。 リスト 6 は、ムーアプロダクツ社書式の前書きスクリーンの例です。

ムーアプロダクツ社書式の章前書き

リスト 6 Screen #101
 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CHAPTER 5  -  ORIGIN/DESTINATION - MULTILOOP BIT ROUTINES

DOCUMENTS - CONSOLE STRUCTURE CONFIGURATION
        DESIGN SPECIFICATION
        SECTIONS - 3.2.7.5.4.1.2.8
                   3.2.7.5.4.1.2.10

ABSTRACT  -  File control types E M T Q and R can all
             originate from a Regional Satellite or a
             Data Survey Satellite.  These routines allow
             the operator to determine whether the control
             originated from a Regional Satellite or not.
リスト 7 Screen #102
 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CHAPTER NOTES - Whether or not a point originates from
                a Regional Satellite is determined by
                the Regional bit in BITS, as follows:

                  1 = Regional Satellite
                  2 = Data Survey Satellite

                 For the location of the Regional bit
                 in BITS, see the Design Specification
                 Section - 3.2.7.5.4.1.2.10

HISTORY  -
チャールズ・ムーア (ムーアプロダクツ社とは無関係です) は、私と違って、よく整理された階層型リストをあまり重要視しません。ムーアは言います。

私はアプリケーションを階層的に構成しますが、必ずしもリストではありません。私のリストはかなりずさんな編成になっています。原始的なのから始めると言う意味で階層的ではありません。

私は LOCATE を使います( VIEW としても知られています。 Starting Forth のChapter 9 Handy Hint;邦訳 FORTH入門 第9章、「知っていると便利です ソース定義を LOCATE する方法」P.291)。 その結果、リストはそれほど慎重に整理されていません。なぜなら、LOCATE が、私の探しているものを探し出してくれるからです。私はリストを見たことがありません(訳注: LOCATE は、ソース・スクリーンからワードの定義を検索)。

––> 対 THRU

相対ロードの問題では、一連の隣接するスクリーンをロードするための一般的な方法の1つは、ワード --> (「ネクスト・ブロック(next-block)」と発音)を使用することです。 このワードにより、インタプリタは直ちに現在のスクリーンの解釈を中止し、次の(大きい番号の)スクリーンの解釈を開始します。

あなたのシステムが --> を提供している場合は、章ロード・スクリーンで THRU コマンドを使用して一連のスクリーンをロードするか、一連のスクリーンを --> でリンクして最初のものだけをロードするかを選択する必要があります(両方を行うことはできません。両方使うと、ほとんどのスクリーンを複数回読み込むことになります)。

--> のいいところは、あなたが一連のシリーズの途中でスクリーンを変えて、それからスクリーンをリロードすると仮定してください。 残りスクリーンは自動的に再ロードされます。 最後のスクリーンが何であるかを知る必要がありません。

それは --> について厄介な事でもあります。いったんロードを開始すると一連のロードプロセスを止める方法はありません。 あなたは1つのスクリーンをテストするのに、必要な数よりもはるかに多くのスクリーンをコンパイルすることになります。

それについて分析するために、今述べた変更を加えた後に、あなたがしたいと思う事は3つでしょうか。

  1. 変更をテストするために1つのスクリーンのみをロードしたい。
  2. スクリーンがある節全体を読み込たい。

または

  1. アプリケーションの残りの部分全体をロードしたい。

この操作には THRU を使うの最適に思えます。

何人かの人々は、定義がスクリーンの境界を越えることを可能にする --> 役に立つと考えています。 実は --> は、複数のスクリーンに渡る高水準(コロン)定義をコンパイルする唯一の方法です。なぜなら --> は「即実行(immediate)」だからです。しかし、コロン定義をスクリーン境界を越えさせるのは決して良いスタイルではありません(定義はそんなに長くなるべきではありません!)。

一方、非常に複雑でタイムクリティカルなアセンブラコーディングは、いくつかの連続したスクリーンを占有する可能性があります。 ただしこの場合、アセンブラはコンパイルモードを使用せず、したがって即実行(immediate)は必要ないため、通常の LOAD 処理も同様に実行できます。

おまけに、 --> は、それ以降のソーススクリーンの行を無駄にします。 お勧めしません。

スクリーンの代替:名前付きファイル内のソース

幾人かのForthの専門家は、伝統的なコンパイラやエディタが使っているアプローチをわざわざエミュレートして、可変長の名前付きテキストファイルにソースコードを格納することを主張しています。このアプローチはますます一般的になるかもしれませんが、その有用性はまだ物議をかもしています。

確かに、スクリーンのスペースを使い果たすことを心配しなくてもいいのですが、制限された領域に書くわずわらしさは、コードの塊に細心の注意を払って御し続けることで補われます。 アプリケーションの開発では、スクリーンの内容を並べ替えるよりもはるかに多くの時間をかけてスクリーンのロードと再ロードを行います。

「長さ制限のない」ファイルは、ずさんな、無秩序な思考と悪いファクタリング(要素分解)を可能にします。 1Kブロック境界によって課される規律がないと、定義は長くなります。 20Kのファイルを書く、あるいはもっと悪いことに、20Kの定義を書くようになります。

おそらく、妥協の余地があるのは、ネストされたロードを可能にし、非常に小さな名前付きファイルの使用を推奨するファイルベースのシステムでしょう。 もっとも、たぶん、経験豊富なForthプログラマは5Kから10Kより長い名前付きファイルを使用しないでしょう。 では、そのメリットは何でしょうか?

そのような修辞的な質問に「数字よりも名前を覚える方が簡単だ」と答える人もいるかもしれません。そうしたいのなら、それらのブロック番号を定数として事前定義してください。

90 CONSTANT FRAMING

以降、FRAMING 節をロードするには、以下のようにします。

FRAMING LOAD

また、その節のロードブロックをリストするなら、以下のようにします。

FRAMING LIST

(節名は「ING」で終わるのが慣例です)

もちろん、スクリーンベースのアプローチの煩わしさを最小限に抑えるためには、あるスクリーンから別のスクリーンにソースの行を移動するエディタコマンドや、一連のスクリーンをリスト内で前後にスライドさせるワードなど、優れたツールが必要です。

ディスク割り当て

よく整理されたリストの最後の側面は、ディスクの何処に何を配置するかの標準化です。会社ごと・部門ごと・個々のプログラマーごと・作品の性質ごとに標準を設定する必要があります。

表 1 ある部門のディスク割当体系サンプル
Screen 0 アプリケーションの名前、現在のリリース番号、および一次著者を示すタイトル・スクリーンです。
Screen 1 アプリケーション・ロード・ブロック
Screen 2 スクリーン1に収まらず継続が必要な時の為に予約されています。
Screen 4 and 5 システムメッセージが含まれています。
Screens 9 thru 29 ここに、このアプリケーションに必要な、ただしこれに限定されない一般的なユーティリティを組み込みます。
Screen 30 ここからアプリケーション・スクリーン開始です。

表 1 は典型的な部門での割り当て方です。

Forthを使っている会社の多くは、3で割り切れるスクリーン番号でコード節を始めることが望ましいと考えています。ディスク上の主要な分割は、30スクリーン毎に行われるべきです。

なぜか? 慣例により、Forthのスクリーンは1ページに3つ出力され、出力時のトップスクリーンは常に3で均等に割り切れます。 そのようなページは「トライアド(triad)」と呼ばれます。ほとんどのForthシステムはトライアドの3つのスクリーンのうちのどれかの番号を引数として与えられてそれを生成するためのワード TRIAD を含んでいます。 例えば以下のようにタイプすると、

77 TRIAD

あなたは75,76,77を含むページを得ます。

この慣例の主な利点は、単一のスクリーンを変更した場合、現在表示されているリストを含むバインダーに新しいトライアドを入れて、重複するスクリーンがない1ページだけを置き換えられることです。

同様に、ワード INDEX は各スクリーンの最初の行を1ページに60ずつ、60で割り切れる境界毎に出力します。

ヒント

節または用語集のスクリーン番号を常に3で割り切れる数で初めます。アプリケーションや章のスクリーン番号を常に30で割り切れる数で初めます。

選択

Forthシステムのベンダーは問題を抱えています。顧客が期待するすべてのコマンド(グラフィック、プリンタ、その他の機能を制御するためのワード)を含めたい場合は、システムがコンピュータのメモリ容量の半分以上に膨れ上がっていることがよくあります。それはプログラマがアプリケーションをコンパイルする余地を少なくします。解決策は、ベンダーが、「ソース形式」で提供される追加の機能とともに、その他の機能をプリコンパイル済バイナリで提供することです。このアプローチにより、プログラマは実際に必要なルーチンを選ぶことができます。

これらのユーザロード可能なルーチンは 「選択機能(electives)」と呼ばれます。倍長演算、日付と時刻のサポート、 CASE 文、 DOER/MAKE 組(後述)は、Forthシステムが選択機能として提供する機能の一部です。

スクリーンレイアウト

この節では、各ソース・スクリーンのレイアウトについて説明します。

ヒント

行0は「コメント行」として確保して下さい。

コメント行は、スクリーンの見出しとしても、ディスクを INDEX した時の行としても役立ちます。 それはスクリーンの目的を説明するべきです(そこに定義されたワードを羅列しないでください)。

コメント行には、少なくともスクリーン名が含まれています。 大規模なアプリケーションでは、章名とスクリーン名の両方を含めることもできます。 スクリーンが用語集を実装する一連のスクリーンの1つである場合は、「ページ番号」も含める必要があります。

スクリーンの右上隅は「タイムスタンプ」のために予約します。タイムスタンプには最新の改訂日が含まれ、作成者が重要な場合はプログラマのイニシャル(日付の左に3文字)を含めます。例えば、

( Chapter name        Screen Name -- pg #      JPJ 06/10/83)

Forthエディタの中には、あるキーを押すだけでタイムスタンプを入力できるものもあります。

日付を表すための一般的な形式は、

mm-dd-yy

1984年2月6日なら、

02-06-84

最近よく使われるようになった形式は、

ddMmmyy

「Mmm」は(英語で)月を表す名前の3文字の略語です。

22Oct84

上記の形式は下記より少ない文字数で書けます。

10-22-84

そして、どっちが日でどっちが月か混乱する可能性を排除します。

システムに \ (スキップ・ライン(skip-line) 付録C 参照))がある場合は、次のようにコメント行を書くことができます。

\ Chapter name        Screen Name -- pg.#       JPJ 06/10/83

すべてのコメントと同様に、コメント行には小文字または大文字と小文字の混在を使用してください。

アプリケーションの索引でスクリーンの整理に関する詳細を明確にする1つの方法は、用語集の継続するスクリーン内のコメント行を3つの空白で字下げすることです。 リスト 8INDEX によって生成されたリストの一部を示しており、そこでは継続しているスクリーンのコメント行が字下げされています。

リスト 8 インデントされたコメント行を示す INDEX の出力。
 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 90 \ Graphics           Chapter load               JPJ 06/10/83
 91    \ Dot-drawing primitives                     JPJ 06/10/83
 92 \ Line-drawing primitives                       JPJ 06/11/83
 93    \ Line-drawing primitives                    JPJ 06/10/83
 94    \ Line-drawing primitives                    JPJ 09/02/83
 95 \ Scaling, rotation                             JPJ 06/10/83
 96    \ Scaling, rotation                          JPJ 02/19/84
 97    \ Scaling, rotation                          JPJ 02/19/84
 98    \ Scaling, rotation                          JPJ 02/19/84
 99 \ Boxes                                         JPJ 06/10/83
100 \ Circles                                       JPJ 06/10/83
101    \ Circles                                    JPJ 06/10/83
102    \ Circles                                    JPJ 06/10/83

ヒント

すべての定義をスクリーンの左端から始めて、1行に1ワードだけ定義してください。

悪い例

: ARRIVING   ." HELLO" ;   : DEPARTING   ." GOODBYE" ;

良い例

: ARRIVING   ." HELLO" ;
: DEPARTING   ." GOODBYE" ;

この規則により、リスト内で定義を見つけやすくなります(定義が複数の行にわたって続く場合は、後続の行は常に字下げされるべきです)。 VARIABLECONSTANT も1行に1つ定義されるべきです( 付録E の 「良いコメントスタイルのサンプル」 を参照)。 これは同じ行に説明コメントを書く余地を残します。 例外は、ユニークなコメントを必要としない、大規模な「ファミリー」のワード(共通の定義用のワードによって定義される)です。

0 HUE BLACK     1 HUE BLUE      2 HUE GREEN
3 HUE CYAN      4 HUE RED       5 HUE MAGENTA

ヒント

後で追加できるように、スクリーンの下部に十分なスペースを確保してください。

最初の試行で、各スクリーンを半分以下のコードで埋めます。 反復的なアプローチでは、最初にアプリケーションのコンポーネントをスケッチしてから、すべての要件が満たされるまでそれらを反復的に作成する必要があります。 通常これは、既存のスクリーンに新しいコマンドを追加すること、または特殊なケースの処理を追加することを意味します(常にではありませんが、新しい反復でコードが単純化されることもあります。または、新しい複雑さが別のコンポーネントに属しているため、別のスクリーンにまとめて表示する必要があったりします)。

最初に十分なスペースを残すことで、後で追加することがより快適になります。 ある作家は、最初のパスで、スクリーンには20から40パーセントのコードと80から60パーセントの空白を含めることを推奨します。 [stevenson81]

各定義の間にある行をスキップしないでください。 ただし、定義の「グループ間」の行をスキップすることはできます。

ヒント

すべてのスクリーンは BASEDECIMAL に設定したままにしてください。

コードが HEX で書かれている3つのスクリーンが並んでいる場合でも(例えば3つのスクリーンのアセンブラコード)、それぞれのスクリーンは BASEHEX に設定しなければなりません。 そして基数を一番下で DECIMAL に戻します。 この規則により、テストの目的で、問題の状況を気にすることなく、各スクリーンを個別にロードできます。 また、リストを読むと、スクリーンに明示的に HEX と表示されていない限り、値は10進数であることがわかります。

いくつかの会社はさらに以下の規則を設定しています。 最後に基数を DECIMAL にリセットするのではなく、何でも最初の基数にリセットします。この追加の保証は、次のようにして実現できます。

BASE @       HEX    \ save original BASE on stack
0A2 CONSTANT BELLS
0A4 CONSTANT WHISTLES
... etc. ...
BASE !              \ restore it

マルチスクリーンアセンブラの定義で BEGINIF によって返される値、ある定義語から別の定義語に渡されるベースアドレスなど、引数がスクリーンからスクリーンに渡されることがあります( 第6章 の「コンパイル時のファクタリング(要素分解)」を参照)。 このような場合、以下のようにリターンスタックに BASE の値を保存するのが最善です。

BASE @ >R     HEX
... etc. ...
R> BASE !

何人かの人々は BASE を変更するどのスクリーンでもこのアプローチを使う方針にしています。そうすることで彼らはそれについて心配する必要が無くなります。

ムーアはロード後に DECIMAL を呼び出ように LOAD を定義することを好みます。 リセットすることを心配する必要がないため、この方法だとスクリーンの内容が簡単になります。

空白とインデント

ヒント

空白とインデントは読みやすさの必需品です。

本書の例では、広く受け入れられている空白と字下げスタイルの規則を使用しています。 適切に使用される空白は読みやすさを向上させます。 安価なディスクメモリを消費する事を除いて、ソース・スクリーンに空白を空けることによるペナルティはありません。

白黒はっきりした規約を好む人のために、表 表 2 はガイドラインの一覧です(ただし、Forthのインタプリタは空白や字下げにあまり気を遣いません)。

表 2 字下げと空白のガイドライン
1つだけ空白空けるのはコロンと名前の間。
2つだけ空白空けるのは名前とコメントの間。 [1]
2つの空白か改行入れるのは、 コメントの後と、定義の前。 [1]
3つの空白空けるのは、コメントが無い時の名前とその定義の間。
3つの空白で字下げするのは後続の各行(入れ子になった場合は更に3の倍数)。
1つの空白空けるのは、フレーズの中の、ワードや数値の間。
2か3の空白空けるのはフレーズとフレーズの間。
1つの空白空けるのは最後のワードとセミコロンの間。
1つの空白空けるのは、(必要な場合、)セミコロンと IMMEDIATE の間。

グループの異なる定義を区切る場合を除き、定義の間に空白行は挟みません。

各行の最後は、以下を除いて空白にする必要があります。

  1. 次の行に続く引用符付き文字列、または、
  2. コメントの終了。

\ で始まるコメントは、行末まで続きます。 また、 ( で始まるコメントは、最後の桁で右丸括弧 ) で区切ることができます(訳注: ( はワードだが、右括弧 ) は単なる区切り文字)。

以下は空白と字下げに関するよくある間違いです。

良くない例(名前がその定義本体と分けられていない)

: PUSH HEAVE HO ;

良い例

: PUSH   HEAVE HO ;

良くない例(継続行が3つの空白で字下げされていない)

: RIDDANCE  ( thing-never-to-darken-again -- )
DARKEN  NEVER AGAIN ;

良い例

: RIDDANCE  ( thing-never-to-darken-again -- )
   DARKEN  NEVER AGAIN ;

良くない例(フレーズとフレーズの間が分けられていない)

: GETTYSBURG   4 SCORE 7 YEARS + AGO ;

良い例

: GETTYSBURG   4 SCORE   7 YEARS +   AGO ;

フレーズとフレーズの区切り(phrasing)は主観的な芸術です。 私はまだ役に立つ正式な規則のセットをみつけられていません。 あなたが読みやすくなるよう努力してください。

[1](1, 2) よく見られる代替手段では、名前とコメントの間に1空白、コメントと定義の間に3空白が必要です。 より自由な手法では、コメントの前後に3つの空白を使用します。 あなたは何を選んでも構いませんが、一貫性を保ってください。

コメント規則

適切なコメントは不可欠です。 コメントは5種類あり、それは、スタック効果コメント、データ構造コメント、入力ストリームコメント、目的コメント、物語コメントです。

スタック効果コメント
定義がスタックから消費する引数と、もしあれば、それがスタックに返す引数を表します。
データ構造コメント
データ構造内の要素の位置と意味を示します。 たとえば、テキストバッファの最初のバイトにはカウントが含まれ、テキストには63の空きバイトが含まれる可能性があります。
入力ストリームコメント
入力ストリームでワードがどの文字列を見ることを期待しているかを示します。 たとえば、Forthのワード FORGET は、入力ストリームから区切り文字によって切り出した文字列をワード名とみなして辞書エントリの名前をスキャンします。
目的コメント
可能な限り少ない言葉で、定義が何をするのかを説明します。 定義がどのように機能するかは、目的コメントの範疇ではありません。
物語コメント
定義の中で何を行っているか説明するために、通常は1行ずつ表示されます。物語コメントは、後の節で説明する「垂直書式」でのみ使用されます。

コメントは通常、ソースコードと区別するために小文字で入力されます(ほとんどのFortワードは大文字でつづられていますが、小文字のスペルは特別な場合に使われることがあります)。

次の節では、上記の種類のコメントの標準化された形式を要約し、それぞれの例を示します。

スタック表記

ヒント

スタック上の引数を消費したりスタックに返したりする、全てのコロン定義やコード定義は、スタック効果のコメントを含める必要があります。

「スタック表記」とは、スタック上の内容を表すための規則を指します。 スタック表記の形式には、「スタック状況」、「スタック効果」、「スタック効果コメント」があります。

スタック状況

スタック状況は、時系列に積み重ねられていると理解されているアイテムを表しています。 項目は左から右に向かってリストされ、一番左の項目がスタックの一番下(bottom)を表し、一番右の項目が一番上(top)を表します。

スタック状況の例です。

nl n2

スタック上の2つの数字を示し、n2が一番上(top)(最もアクセスしやすい位置)

これは、これらの値を入力するのに使用するのと同じ順序です。 つまり、n1が100でn2が5000の場合は、以下の通りタイプします。

100 5000

値をこの通りにスタックに配置します。

スタック状況には、「n1」などの省略形または完全な綴りの単語を含めることができます。 通常は略語が使用されます。 いくつかの標準的な略語が表 表 3 にあります。 省略形または完全な綴りの単語のどちらを使用する場合でも、各スタック項目は空白で区切る必要があります。

スタック項目がフレーズ(「address-of-lastest-link」など)で記述されている場合は、フレーズ内の語をハイフンで結合する必要があります。 たとえば、以下のスタック状況のように。

address current-count max-count

ここでは、スタック上に3つの要素があります。

スタック効果

「スタック効果」は2つのスタック状況を示します。定義によって「消費」される可能性のあるアイテムの1つの状況、および定義によって「返される」任意のアイテムの別の状況です。 「前」の状況が最初に来て、その後に2つのハイフンが続き、次に「後」の状況が続きます。

例えば、以下はForthの加算演算子 + のスタック効果です。

n n -- sum

ここでは + は2つの数を消費し、それらの合計を返します。

スタック効果は、スタックに対する操作の「最終結果」のみを記述するということを忘れないでください。 たまたま興味のある引数の下のスタックに存在する他の値を表示する必要はありません。 操作の実行中に表示されたり消えたりする可能性のある値を表示する必要もありません。

ワードが入力引数を変更せずに返す場合は、出力状況でそれらを繰り返す必要があります。 たとえば、以下のように。

3rd 2nd top-input -- 3rd 2nd top-output

逆に、ワードが引数を変更する場合、スタックコメントは異なる記述子を使用しなければなりません。

nl -- n2
n -- n'

スタック効果は、フォーマットされたグロッサリー(ワード集)に表示されることがあります。

スタック効果コメント

「スタック効果コメント」は、ソースコードに書く、丸括弧で囲んだスタック効果です。 以下はCOUNTというワードのスタック効果コメントです。

( address-of-counted-string -- address-of-text count)

または

( 'counted-string -- 'text count)

(「count」は、そのワードが実行された後のスタックの一番上にあります。)

定義がスタックに影響を与えない場合(つまり、定義内で、スタック上に既にある引数を一切使用せず、スタック上に何も返さない場合)、スタック効果コメントは不要です。

: BAKE   COOKIES OVEN ! ;

しかし、上記のように表記する代わりに、以下のように空のスタックコメントを使用することをおすすめします。

: BAKE   ( -- )  COOKIES OVEN ! ;

こうして、ワードがスタックに影響を及ぼさないことを強調します。

定義が引数を消費しても何も返さない場合、二連ハイフン「--」はオプションです。 例えば以下。

( address count -- )

以下のように短縮できます。

( address count)

この規約の背後にある仮定は、引数を消費して何も返さないコロン定義は、何も消費せず引数を返す定義よりも多くあるというものです。

スタック略語規格

スタック表記で使用される略語は一貫しているべきです。 表 3 は一般的に使われる略語の大部分をリストしています(この表は 付録E に再掲しています)。「1倍長(single-length)」、「2倍長(double-length)」などの用語は、特定のForthシステムにおける「セル(cell)」のサイズを表します(システムが16ビットのセルを使用している場合、「n」は16ビットの数を表し、システムが32ビットのセルを使用している場合、「n」は32ビットの数を表します)。

フラグの表記

表 3 にはブールフラグを表す3つの方法が示されています。ここでは例として、ワード -TEXT に対する同じスタックコメントの3つのバージョンを示します。

( at u a2 -- ?)
( at u a2 -- t=no-match)
( at u a2 -- f=match)
表 3 スタックコメントの略語
n 1倍長 符号付整数
d 2倍長 符号付整数
u 1倍長 符号無整数
ud 2倍長 符号無整数
t 3倍長(triple)
q 4倍長(quadruple)
c 7ビット キャラクタ値(訳注:ASCII表示文字の範囲)
b 8ビット バイト値
? ブーリアンフラグ。左記または以下、
 t= true
 f= false
a or adr アドレス
acf コードフィールドのアドレス
apf パラメータフィールドのアドレス
' ((英語では)プレフィックスとして) …のアドレス
s d (ペアで使って)元(source)、先(destination)
lo hi 下限(lower-limit)、上限(upper-limit) (内包)
# カウント。
o オフセット
i インデックス
m ビットマスク
x 詳細を気にしない何か(データ構造記法)
「オフセット」とは、バイトなどの絶対単位で表される差です。
「インデックス」とは、要素やレコードなどの論理単位で表される違いです。

記号「t」と「f」の後の等号「=」は、フラグの結果とその意味を表します。

ありうる様々な結果の記法

定義によっては、状態によってスタック効果が異なります。

スタック上の項目の数がすべての条件で同じままで、項目自体が変わる場合は、縦棒「|」を使って「または」を表すことができます。 以下は、ファイルのアドレス、または要求されたファイルが見つからない場合はゼロを返します。

( -- address|O=undefined-file)

「前」または「後」のでスタック状況の項目数が異なる場合は、スタック状況全体の両方のバージョンを二連ハイフン「--」とともに「または」の記号で区切って書き出す必要があります。

-FIND   ( -- apf len t=found | -- f=not-found )

このコメントは、ワードが見つかった場合、3つの引数が返されることを示します(trueフラグがスタック・トップにあります)。 それ以外の場合はfalseフラグのみが返されます。

2番目の「--」の重要性に注意してください。 その省略は、定義が常に3つの引数を返すことを示します。スタックトップはフラグです(訳注:2番めの「--」を省略すると「-- apf len t=found | f=not-found」となり、これは「-- apf len t=found | -- apf len f=not-found」 という意味になってしまう)。

必要に応じて、スタック効果全体を3つの空白で区切って同じ行に2回書き込むことができます。

?DUP   \ if zero: ( n -- n)    if non-zero:( n -- n n)

または垂直書式だと、

-FIND  \     found:( -- apf len t )
       \ not-found:( -- f )

データ構造コメント

「データ構造コメント」は、データ構造の要素を表します。 たとえば、 |INSERT という挿入バッファの定義は次のようになります。

CREATE |INSERT  64 ALLOT  \  { 1# | 63text }

波括弧「{…}」(faces)は、構造コメントの開始と終了を表します。 縦棒は構造内のさまざまな要素を区切ります。 数字は要素ごとのバイト数を表します。 上記のコメントでは、最初のバイトにカウントが含まれ、残りの63バイトにテキストが含まれています。

「ビットコメント」は、データ構造のコメントと同じフォーマットを使用して、バイトまたはセル内のビットの意味を表します。 例えば、以下のビットコメント、

{ 1busy? | 1acknowledge? | 2x | 6input-device |
   6output-device }

通信チャネルの16ビットステータスレジスタのフォーマットを記述します。 最初の2ビットはフラグ、2番目の2ビットは未使用、最後の6ビットフィールドのペアは、このチャネルが接続されている入力デバイスと出力デバイスを示します。

複数のデータ構造が同じパターンの要素を使用している場合は、コメントを1回だけ(おそらく前書きに)書き出し、後続のスクリーン参照するためにパターンに名前を付けます。 たとえば、前書き上記のビットパターンに「status」という名前を付ける場合は、スタックコメントで「status」を使用してそのパターンの値を示すことができます。

: STATUS?  ( -- status) ... ;

2VARIABLE が1つの2倍長値を含む場合、コメントは内容を示すスタック状況になります。

2VARIABLE PRICE  \ price in cents

2VARIABLE に2つの1倍長データ要素が含まれる場合、 2@ の後に何がスタックにあるのかを示すスタック状況を与えます。 したがって下記のようになります。

2VARIABLE MEASUREMENTS  ( height weight )

これは MEASUREMENTSCREATE によって定義されている場合に使われるであろうコメントとは異なります。

CREATE MEASUREMENTS  4 ALLOT    \ { 2weight | 2height }

(どちらの文も辞書に同じ結果を生成しますが、 2VARIABLE の使用は値を通常「2-fetched」や「2-stored」する事になることを意味しますので、「スタックコメント」コメントを使います。 CREATE を使うことは通常値が別々にフェッチされ格納されることを意味します。したがってデータ構造のコメントを使います。 左に0番目の位置が表示されます。

入力ストリームコメント

入力ストリームコメントは、どのワードや文字列が入力ストリームにあると推定されるかを示します。 表 4 は入力ストリーム引数に使われる指定をリストしています。

表 4 入力ストリームのコメント凡例
c 空白で区切られた単一文字
name 空白で区切られた文字の連なり
text 空白以外で区切られた文字の連なり

実際の区切り文字をtextの後に続けます。 例: text" または text)

入力ストリームコメントはスタックコメントの「前」に現れ、括弧で囲まれることなく、両側に3つの空白で囲まれるだけです。以下に (tick)の定義のコメントのやり方があります。それは最初に入力ストリームのコメントを表示し、それからスタックのコメントを表示します。

: '   \ name   ( -- a)

( を使いたければ、コメントは次のようになります。

: '   ( name   ( -- a)

ちなみに、文字列入力を受け取るには3種類の方法があります。 混乱を避けるために、以下に用語を示します。

何それをスキャン(Scanning-for)
(tick)の場合に単語のためか数字のためかのいずれか、または ."( の場合の区切り文字の為に、入力ストリームを先読みする事を意味します。
期待して待つ(Expecting)
待つことを意味します。EXPECTKEY およびそれらを呼び出す定義は、入力を「予期する」ものです。
想定する(Presuming)
通常の使用では何かが続くことを示します。 そのワードは、定義される名前を「スキャン(scans-for)する」、そして、定義される名前の後に定義が続くことを「想定する(presumes)」。

入力ストリームのコメントは、スキャン対象(scanned-for)の入力にのみ適しています。

目的コメント

ヒント

以下の場合を除き、すべての定義に目的コメントを付ける必要があります。

  1. その目的は、名前またはスタック効果コメントから明らか。または、
  2. それが3つ以下のワードで構成されている場合。

目的コメントは、最小限にとどめ、1行を超えないようにする必要があります。 たとえば以下のように。

: COLD   \ restore system to start condition
    ... ;

命令文を使用してください。「前景色設定」ではなく「前景色を設定しろ」。

反面、ワードの目的は、そのスタック効果コメントで説明することができます。 スタックコメントと目的コメントの両方が必要になることはめったにありません。 たとえば以下のように。

: SPACES  ( #)   ... ;

または

: SPACES  ( #spaces-to-type -- )   ... ;

この定義は、入力引数として、入力する空白の数を表す数値を取ります。

: ELEMENT  ( element# -- 'element)  2*  TABLE + ;

この定義は、それが消費するインデックスを、インデックス付けされた要素に対応する2バイト要素の表内のアドレスに変換します。

: PAD  ( -- 'scratch-pad)  HERE  80 + ;

この定義はメモリのスクラッチ領域のアドレスを返します。

時々は、両方のタイプのコメントを含めることによってとても読みやすくなります。この場合、目的コメントは最後に現れます。 たとえば以下です。

: BLOCK  ( n -- a)  \   ensure block n in buffer at a

ヒント

コメントの種類を次の順序で示します。最初は入力ストリームのコメント、次はスタック効果のコメント、3番めは目的コメントです。

例えば

: GET   \   name   ( -- a)   get first match

もし ( を使いたければ、次のように書いてください。

: GET   (   name  ( -- a)    ( get first match)

必要に応じて、目的コメントを2行目に置く事ができます。

: WORD   \   name   ( c -- a)
   \ scan for string delimt'd by "c"; leave at a
   ...  ;

定義用のワードのコメント

定義用のワードの定義は、2つの振る舞いを含んでいます。

  • 「定義用のワードの子供」を定義するときの定義用のワード(コンパイル時(compile-time)の振る舞い)、そして、
  • 子供それ自身のもの(実行時(run-time)の振る舞い)です。

これら2つの振る舞いは別々にコメントしなければなりません。

ヒント

定義語のコンパイル時の動作を通常の方法でコメントします。 その実行時の振る舞いを DOES> (または ;CODE )の後にコメントします。

例えば

: CONSTANT  ( n ) CREATE ,
   DOES>  ( -- n)  @ ;

(子供の)実行時の動作に対するスタック効果のコメントは、子供のワード実行時の正味のスタック効果を表します。 したがって、( DOES> のあとの)実行時コード開始時には DOES> によって返されるアドレスがスタック上にありますが、それは含めません。

悪い例(実行時コメントがapf(パラメータフィールドアドレス)を含んでいる)

: ARRAY   \  name  ( #cells)
   CREATE 2* ALLOT
   DOES>   ( i apf -- 'cell)  SWAP  2* + ;

良い例

: ARRAY   \  name  ( #cells)
   CREATE 2* ALLOT
    DOES>  ( i -- 'cell)  SWAP  2* + ;

このワード ARRAY によって定義されたワードはスタック効果があります。

( i -- 'cell)

定義語が実行時の振る舞いを指定していない場合でも、実行時の振る舞いが存在し、コメント化される可能性があります。

: VARIABLE   (  name  ( -- )  CREATE  2 ALLOT ;
   \ does>   ( -- adr )

コンパイル用のワードのコメント

定義用のワードと同様に、ほとんどのコンパイル用のワードには2つの振る舞いがあります。

  1. 定義に現れるコンパイル用のワードはコンパイルされます。
  2. それは、定義済のワードを呼び出す時に実行される実行時ルーチンです。繰り返しになりますが、それぞれの振る舞いは個別にコメントしなければなりません。

ヒント

コンパイル用のワードの実行時の振る舞いを通常の方法でコメントします。 コンパイル時のコメントは、ラベル「Compile:」に続いて書きます。

例えば

: IF   ( ? -- ) ...
\ Compile:   ( -- address-of-unresolved-branch)
   ... ; IMMEDIATE

コンパイル用ワードの場合、最初のコメントは実行時の動作を記述します。これは通常、ワードを使用するための構文です。 2番目のコメントは、コンパイル時にワードが実際にどのように機能するのかを説明しています(これはユーザにとっては重要性は低いです)。

他の例

: ABORT"  ( ? -- )
\ Compile:   text"   ( -- )

時折、コンパイル用のワードがコロン定義の外で呼び出されると、異なる動作を示すことがあります。 そのようなワードは(それについて忠実になるために)3つのコメントを必要とします。 たとえば以下です。

: ASCII  ( -- c)
\ Compile:   c   ( -- )
\ Interpret:   c   ( -- c )
     ... ; IMMEDIATE

付録E には、良いコメントスタイルを示す2つのスクリーンがあります。

垂直書式 対 水平書式

コメントする目的は、コードの読者が何が起こっているのかを簡単に判断できるようにすることです。 しかし、どれだけのコメントが必要ですか? あなたの状況にふさわしいコメントのレベルを決定するために、あなたは自身に2つの問い掛けをしなければなりません。

  • 私のコードを読むのは誰ですか?
  • 私の定義はどのぐらい読みやすいですか?

2つの基本的なスタイルのコメントを選択できます。しばしば「垂直書式」と呼ばれる最初のスタイルは、よくコメントされたアセンブリ言語のリストのように、プロセスのステップバイステップの説明を含みます。 これらの行ごとのコメントは「物語コメント」と呼ばれます。

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
\ CRC Checksum                                      07/15/83
: ACCUMULATE   ( oldcrc char -- newcrc)
   256 *               \ shift char to hi-order byte
   XOR                 \ & xor into previous crc
   8 0 DO              \ Then for eight repetitions,
       DUP 0< IF       \ if hi-order bit is "1"
          16386 XOR    \ xor it with mask and
          DUP +        \ shift it left one place
          1+           \ set lo-order bit to "1"
              ELSE     \ otherwise, i.e. hi-order bit is "0"
          DUP +        \ shift it left one place
              THEN
       LOOP ;          \ complete the loop

もう1つの方法では、コードのフレーズ間に物語コメントが散在しません。 これは「水平書式」と呼ばれます。

: ACCUMULATE  ( oldcrc char -- newcrc)
   256 *  XOR  8 0 DO  DUP 0< IF
      16386 XOR  DUP +  1+  ELSE  DUP +  THEN  LOOP ;

大規模なプログラマチームがアプリケーションをコーディングおよび保守している場合は、垂直書式が推奨されます。 通常、このようなチームには、小さな修正を担当する駆け出しレベルのプログラマが何人か含まれます。 そのような環境では、勤勉なコメントは時間を無駄にしたり混乱せずに済みます。ムーアプロダクツ社のジョンソン氏が言うように、「コードを保守するときには、たった1つの小さな節に関心があり、そこに書かれている情報が多ければ多いほど、迅速な修正が可能になります。」

以下がムーアプロダクツ社のForthプログラマに要求される、いくつかの適切とされる規則です(フレーズとフレーズの間隔取り)。

  1. 垂直書式を使用します。コメントはソースコードの右側に表示されますが、必要に応じて次行も使います。
  2. ソース文字よりも多くのコメント文字があるはずです(この会社は、10文字を超える長い説明的な名前を推奨し、名前をコメント文字として数えることを許可しています)。
  3. 条件付き構造またはアプリケーション・ワードは、別の行に表示する必要があります。 「ノイズワード」はまとめてグループ化できます。 字下げは入れ子になった条件を示すために使用されます。

ただし、この形式にはいくつかの問題があります。 1つには、優れたスクリーンエディタを使用したとしても、行ごとのコメントは時間がかかります。 特にコメントを書くのを中断すると思考の連鎖が崩れ、生産性が低下する可能性があります。

また、コメントが最新のものであることも慎重に確認する必要があります。 多くの場合、コードが修正され、改訂版がテストされ、変更が行われ、プログラマがコメントを変更するのを忘れます。 コメントが多いほど、それらは間違っている可能性が高くなります。 コメントが間違っているならば、それは役に立たないより悪いです。

プロジェクトの監督者がコードを慎重に確認し、コメントの正確さを保証すれば、この問題を軽減することができます。

最後に、行ごとのコメントは誤った安心感を与える可能性があります。 「各行にコメントがあるので、アプリケーションはうまく機能している」とは思わないでください。 行ごとのコメントでは、定義の操作における重要な側面については説明されていません。 たとえば、使用されているチェックサムアルゴリズムの背後にある考え方は何ですか? 物語のコメントから誰が知っているのですか?

正確に説明するために、概説では、与えられた手続きの意味は通常、単一のフレーズではなく多くの段落を必要とします。 そのような説明は、補助的な文書または章の前文に含めるのが適切です。

これらの注意にもかかわらず、多くの企業は垂直書式を必要としています。 確かに、Forthに初めて触れるチームは、他の大規模チームと同様にそれを採用する必要があります。

水平書式はどうですか? おそらくそれは芸術性と実用性の問題ですが、私は水平書式を同じように有効で、ある意味では優れていると擁護しなければならないと感じています。

Forthのコードが本当によく書かれていれば、それについてあいまいなものは何もないはずです。 つまり、以下の事が言えます。

  • サポートしている用語集は、よく練られた構文を持ちます。
  • スタックの入出力はコメント化されています。
  • (名前やスタックコメントから明らかで無い場合)目的コメントがある。
  • 定義は長すぎない
  • スタックを介して単一の定義に渡される引数が多すぎない( 第7章 の「スタイリッシュなスタック」参照)。

Forthは、他の言語とは異なり、行ごとのコメント付けは、プログラムを読みやすくするためにできるいくつかのことの1つです。

巧みに書かれたForthコードは詩のように、プログラマと機械の両方が容易に読むことができる正確な意味を含んでいます。 あなたの目標は、あなたがコメントすることを選んだとしても、コメントを必要としないコードを書くことです。 コメントではなくコードが意味を伝えるようにアプリケーションを設計してください。

あなたが成功すれば、過度のコメントをする必要がなくなり、余分な説明をせずに純粋な表現を実現できます。

_images/fig5-2.png

図 51 ウィギンス、彼のコメント・テクニックを誇りに思う。

ヒント

最も正確で安価な文書は、自己文書化コードです。

残念ながら、最高のプログラマでさえ、締め切りのプレッシャーを与えられて、コメントなしでは容易に読めない実用的なコードを書くかもしれません。 あなた自身のために、またはあなたが口頭でコミュニケーションをとることができる人と小さなグループのために書いているならば、水平書式は理想的です。 それ以外の場合は、垂直書式を検討してください。

名前の選択、それは芸術

数学的な下地に加えて、母国語への非常に優れた習熟は、有能なプログラマにとって最も重要な財産です(エドガー・ダイクストラ教授 [dijkstra82] )。

私たちはアプリケーション内のアイデアやオブジェクトを象徴するために名前を使用することの意味について説明しました。 名前の選択は設計プロセスの重要な部分であることがわかりました。

新人は名前の重要性を見逃しがちです。 新人は「結局のところ、コンピュータは私が選んだ名前を気にしない」と考えます。

しかし、良い名前は読みやすさのために不可欠です。 さらに、一つの単語の説明を召喚するという精神的な運動は、実体が何を為すべきか何を為さないべきかというあなたの認識に相乗効果をもたらします。

良い名前を選択するためのいくつかの規則は次のとおりです。

ヒント

「どのように(how)」ではなく、「何をするのか(what)」に従って名前を選んで下さい。

定義はそれを呼び出す他の定義から実装の複雑さを隠すべきです。 名前も手順の詳細を隠し、代わりに外観または正味効果を説明する必要があります。

たとえば、Forthのワード ALLOT (訳注:割り当てる の意)は辞書ポインタ(ほとんどのシステムでは DP または H と呼ばれる)をインクリメントだけです。しかし、ユーザはポインタをインクリメントするのではなく、スペースを確保することを考えているので、 ALLOT という名前は DP+! よりも優れています。

FORTH-83規格では、同じ機能に対して、以前の名前 <CMOVE の代わりに CMOVE> という名前を採用しました。 この操作により、メモリの前方領域を重複メモリにコピーすることができます。 最後のバイトから始めて後ろ向きに実行することでこれを実現します。 新しい名前では、「何をするのか(what)」の前向きさが「どうするのか(how)」の後向きさに優先します。

ヒント

最も表現力豊かな単語を見つけてください。

正しい単語は強力なエージェントです。 私たちが本や新聞の中でそれらの非常に正しい単語のうちの1つに遭遇するときはいつでも、結果として生じる効果は肉体的であると同時に霊的でもあり、そして稲妻に打たれたような啓示です。(マーク・トウェイン)

正しい単語とほぼ正しい単語の違いは、lightning(稲妻)とlightning bug(ほたる)の違いに似ています。(マーク・トウェイン)

しぐさは言葉に合わせ、言葉はしぐさに合わせるように。(シェイクスピア ハムレット 第3幕)

Forthのコンサルタント兼作家であるヘンリー・ラクセンは、最も重要なForth開発ツールは優れたシソーラス(類語辞典)であることを示唆しています。 [laxen]

定義にふさわしい言葉を考えることがあるかもしれませんが、それはまったく正しいとは言えません。 数カ月後になって、あなたが目標を達成できなかったことに気付くでしょう。 第4章 のローマ数字の例では、次のシンボルのの値よりも1小さい値を表す言葉が必要でした。私の最初の選択は 4-0R-9 でした。厄介なことに、私が ALMOST を思いついたのはずっと後になってからでした。

ほとんどのfig-Forthシステムは、現在のボキャブラリのすべての単語の名前をリストするワード VLIST を含みます。 何年もの後、誰かがより良い名前が WORDS であることに気づきました。 WORDS はそれ自体がより快適に聞こえるだけでなく、ボキャブラリ名に対してもうまく機能します。 たとえば以下のように。

EDITOR WORDS

または

ASSEMBLER WORDS

反面、ムーアは、不適切な名前は暗号化のための単純な手法になる可能性があると指摘しています。 ソースの配布を強いられたときにセキュリティを確保する必要がある場合は、誤解を招くような名前を慎重に選択することで、コードを非常に読みにくくすることができます。 もちろん、メンテナンスは不可能になります。

ヒント

そのフレーズの中で活きる名前を選択してください。

あなたが何を呼ぶべきかわからない定義に直面したら、文脈の中でその言葉がどのように使われるかについて考えて下さい。例えば以下のように。

SHUTTER OPEN
OPEN は、 SHUTTER という名前で識別されるI/Oアドレスにビットをセットするワードに適切な名前です。
3 BUTTON DOES IGNITION
DOES は、 IGNITION 機能のアドレスを機能の表にベクトル化するワードに適しています。そのため、ボタン3が押されると IGNITION が実行されます。
SAY HELLO
SAYHELLO を実行変数にベクトル化するのに最適です(私が最初にこの例をForth入門(Starting Forth)用に書いたとき、私はそれを VERSION と呼びました。ムーアは原稿をレビューして SAY を提案しました)。
I'M HARRY
よくあるように、I'M という言葉は、 LOGON HARRYLOGIN HARRY または SESSION HARRY よりも自然に見えます。
I'M という選択は ムーア のまた別の発明であり、彼はこう言っています。

私は LOGON という言葉を嫌います。 英語にそのような言葉はありません。 「私は…」という言葉を探していました。それは当たり前のことでした。 私はちょうどそれに出くわした。 たとえそれがそのアポストロフィで不器用であっても、それはその正当性の感覚を持ちます。

これらの小さな言葉はすべて、「なるほど!(Aha!)反応」 取得のための最良の方法です。 あなたが正しい言葉を考えれば、あたりまえに正しいワードになります。

あなたに幅広い連想ボキャブラリがあれば、正しい言葉を思いつきやすくなります。

ムーアのもう1つのお気に入りのワードは TH です。彼はこれを配列インデックスワードとして使用します。 たとえば以下のフレーズのように。

5 TH

配列の5番目の要素のアドレスを返します。

ヒント

スペルに省略形を使わないで下さい。

私はかつて雑誌に掲載されたForthコードを見たことがあります。その作者は「display buffer」を表す「DSPL-BFR」のような目障りな名前を発明し、名前から全ての母音を追放することに必死なように見えました。他の作者共は「length」を意味する「LEN」をこねくり出して、3文字の呪文のように皆がそれを唱えてくれると思っているようです。そのような習慣は過ぎ去った時代からの思考を引きずっています。

Forthのワードはフルスペルであるべきです。 INITIALIZETERMINALBUFFER のすべての文字を入力してください。 これらはあなたが意味する言葉です。 ワードを短縮することの最悪の問題は、あなたがそれをどのように短縮したかを忘れているということです。 それは DSPL 、それとも DSPLY ですか?

もう1つの問題は、省略形が読みやすさを妨げることです。 どんなプログラミング言語でも読み下す難易度が上がります。

しかし、例外もあります。

  1. コードで非常に頻繁に使用するワード。以下のように、Forthでは何度も繰り返し使われるが、本質的な意味をほとんど持たないか、または全く持たないコマンドがいくつかあります。

    :   ;   @   !   .   ,
    

    しかし、それらの数はごくわずかであり、それらは頻繁に使用されているので、彼らは古くからの友達になります。 私はそれらをフルスペルでタイプしまくりたいとは思いません。

    DEFINE   END-DEFINITION   FETCH   STORE
         PRINT   COMPILE#
    

    (興味深いことに、これらのシンボルの大部分は言葉(英語)に対応するものを持っていません。他に用語がないので「コロン定義(colon definition)」という成語を使用します。 他の用語はありません。)

  2. 端末オペレータが操作を制御するために頻繁に使用する可能性のあるワード。 これらのワードは、ラインエディタのコマンドと同様に、1文字のスペルで入力できるようにすべきです。

  3. 皆が知っている略語。Forthアセンブラのニーモニックは通常、製造元が推奨するニーモニック(JMPやMOVなど)の略称に基づいてパターン化されています。

あなたの名前は発音できるはずです。 そうでなければ、他の人とプログラムについて話し合おうとしたときに、後悔するかもしれません。 名前がシンボリックである場合は、発音を考案してください(たとえば、 >R は「to-r」と呼ばれ、 R> は「r-from」と呼ばれます)。

ヒント

短い言葉を好んで下さい。

同じことを意味する、3音節の単語と1音節の単語の選択を考えるなら、短い方を選択してください。 BRIGHTINTENSE よりも良い名前です。 ENABLEACTIVATE よりもいい名前です。 GORUNON の方がまだマシかもしれません。

短い名前ほど入力が簡単です。 それらはソーススクリーンのスペースを節約します。 最も重要なのは、それらがあなたのコードをくっきりときれいにすることです。

ヒント

ハイフンで繋がれた名前は悪いファクタリング(要素分解)の兆候かもしれません。

ムーアは言います。

Forthコミュニティには多様なプログラミングスタイルがあります。 そのワードが何をしているのかを言葉(英語)で表現するハイフン付きのワードを使用します。 あなたはこれらの大きな長い言葉を一緒につなぎ合わせると、かなり読みやすい何かを得ます。

しかし、私はいの一番に、プログラマが十分に慎重にワードを考えなかった、ハイフンは無くすべきであり、ワードは別々に定義されるべきであると思います。 それは必ずしも可能ではありません、そしてそれは常に有利ではありません。 しかし、私は2つの概念を混在させるという、ハイフンでつながれたワードを疑っています。

同じことを述べるための、以下の左右の戦略を比較してください。

ENABLE-LEFT-MOTOR        LEFT MOTOR ON
ENABLE-RIGHT-MOTOR       RIGHT MOTOR ON
DISABLE-LEFT-MOTOR       LEFT MOTOR OFF
DISABLE-RIGHT-MOTOR      RIGHT MOTOR OFF
ENABLE-LEFT-SOLENOID     LEFT SOLENOID ON
ENABLE-RIGHT-SOLENOID    RIGHT SOLENOID ON
DISABLE-LEFT-SOLENOID    LEFT SOLENOID OFF
DISABLE-RIGHT-SOLENOID   RIGHT SOLENOID OFF

左側の構文には8つの辞書エントリが必要です。 右側の構文は6つだけでよく、ワードのいくつかはアプリケーションの他の部分で再利用できる可能性もあります。 MIDDLE のモーターとソレノイドを使用している場合、16個の組み合わせを説明するのに7語しか必要ありません。

ヒント

名前に数値をまとめないでください。

1CHANNEL2CHANNEL3CHANNEL などのように、数値で始まる、または数値でわる名前のシリーズに注意してください。

名前と数値をこのようにまとめることは、ファクタリング(要素分解)が悪いことを示している可能性があります。 ハイフン犯罪に似ていますが、括り出す必要があるのは単語ではなく数値である点が異なります。 上記のより良いファクタリング(要素分解)は以下のようになるでしょう。

1 CHANNEL
2 CHANNEL
3 CHANNEL

この場合、3つあったワードを1つ減らせました。

多くの場合、名前と数値をまとめることはあいまいな命名を示します。 CHANNEL の例は、下記のように、よりわかりやすい名前でチャネルの目的を示すことができます。

VOICE , TELEMETRY , GUITAR

次の「ファクタリング(要素分解)」の章で、これらの考えを詳しく説明します。

命名基準:その科学

ヒント

Forthの命名規則を学び、採り入れて下さい。

短くても意味のある名前を探すために、Forthプログラマは特定の命名規則を採用しました。 付録E には、長年にわたって開発された最も有用な規約のリストがあります。

命名規則のパワーの一例は、「print」または「display」を意味するための「ドット(.)」の使用です。

.   D.   U.R

さまざまな形式でさまざまな種類の数値を表示するためのものです。 この規約はアプリケーションのワードにも適用されます。 DATE という変数があり、日付を表示するワードが必要な場合は以下のようになります。

.DATE

注意:接頭辞と接尾辞を使いすぎると、ワードが曖昧になり、最終的に読みにくくなります。 その名前だけでワードがすることすべてを説明しようとしないでください。 結局、名前はシンボルであり、コードの省略形ではありません。 以下はどちらが読みやすく、自然に聞こえますか?

Oedipus complex

(本質的な意味はありません)、または

subconscious-attachment-to-parent-of-opposite-sex complex

あなたがそのプレイを知っていると仮定していても、おそらく前者です。

ヒント

意味の詳細を名前自体に詰め込むのではなく、同じ単語を区別するために接頭辞と接尾辞を使用して下さい。

例えば、このフレーズ

... DONE IF CLOSE THEN ...

これより、以下の方が読みやすい

... DONE? IF CLOSE THEN ...

そしてきれいになる。 したがって、 DONE と呼ばれる追加のワードが(フラグとして)必要でない限り、それが望ましいです。

名前付けに関する最後のヒントです。

ヒント

名前との衝突を避けるために、すべての16進数を 0(ゼロ)で始めてください。

例えば 「ADD」ではなく、「0ADD」と書いて下さい。

ただし、あなたのForthシステムが必ずしも上記の規約に準拠することを期待しないでください。 規約は新しいアプリケーションで使用されることを意図しています。

Forthは何年にもわたってそれを最後の手段として使用した人々によって作成され洗練されました。 その当時、まだ成長し進化しているツールに命名基準を課すことは合理的でも可能でもありませんでした。

Forthが政府の委員会で設計された代物だったら、私はForthをこんなには愛さなかったでしょう。

より読みやすくするためのヒント

ここに、コードを読みやすくするための最後の提案をいくつか示します(その定義は 付録C にあります)。

ほとんどのアプリケーションでは、BL (「空白(Blank-Space)」のASCII値)という代償があります。

ASCII というワードは、ASCII文字のリテラル値を知らなくても済むようにするために、主にコロンの定義内で使用されます。

: (    41 WORD  DROP ;  IMMEDIATE

ここで、41は右括弧 ) のASCIIコードです。それは以下のように書く事ができます。

: (    ASCII ) WORD  DROP ;  IMMEDIATE

ブール値を扱いやすくするためのワードのペアは、 TRUEFALSE です。 これらの追加を使うと、以下のようなフレーズを書くことができます。

TRUE 'STAMP? !

のようにフラグをセットしたり、

FALSE 'STAMP? !

のようにフラグをクリアします。

(私はかつて TF を使ったことがありますが、そのワードが必要とされることはめったにないので、今では私は略語禁止規則に気をつける必要はありません。)

アプリケーションの一部として(必ずしもForthシステムの一部ではありませんが)、このアイデアをさらに一歩進めて次のように定義することができます。

: ON   ( a)  TRUE SWAP ! ;
: OFF   ( a)  FALSE SWAP ! ;

これらのワードによって、あなたは以下のように書く事ができます。

'STAMP? ON

または

'STAMP? OFF

これらの定義の他の名前には、 SETRESET がありますが、 SETRESET は個々のビットを操作するのに通常ビットマスクを使います。

よく使われるワードは WITHIN です。これは与えられた値が他の2つの値の間にあるかどうかを決定します。 構文は次のとおりです。

n  lo hi WITHIN

ここで、「n」はテストする値、「lo」と「hi」は範囲を表します。「n」が「lo」以上「hi」未満の場合、 WITHIN はtrueを返します。この包括的な上限の使用は、 DO LOOP の構文と同等です。

ムーアは、 UNDER+ というワードを推奨しています。 一番上のスタック項目の代わりに一番上の項目の直下にある数値に値を追加するのに便利です。 それは以下のように高レベルで実装されるかもしれません。

: UNDER+  ( a b c -- a+c b )  ROT +  SWAP ;

要約

保守性には読みやすさが必要です。 この章では、ソースリストを読みやすくするためのさまざまな方法を列挙しました。 私たちは自分たちのコードをできる限り自己文書化することを方針としています。 その技法には、リスト整理、空白と字下げ、コメント、名前の選択、および明確さを高める特別なワードが含まれます。

ここでは、リスト自体以外のすべてのドキュメントを含む、補助的なドキュメントについて簡単に説明しました。 本書では、補足資料についてはこれ以上説明しませんが、それでもこれらはソフトウェア開発プロセスに不可欠な部分です。

参考文献

[stevenson81]Gregory Stevenson, "Documentation Priorities," 1981 FORML Conference Proceedings, p. 401.
[lee81]Joanne Lee, "Quality Assurance in a ForthEnvironment," (Appendix A), 1981 FORML Proceedings, p. 363.
[dijkstra82]エドガー・ダイクストラ(Edsger W. Dijkstra), Selected Writings on Computing: A Personal Perspective, New York, Springer Verlag, Inc.,1982.
[laxen]ヘンリー・ラクセン(Henry Laxen), "Choosing Names," Forth Dimensions, vol. 4, no.4, Forth Interest Group.