人間の顔を持つスタックプログラミング(パート2)

予想どおり、前の投稿は矛盾したコメントを引き起こしました。 誰かが問題を解決するために既存のフォートに満足している、誰か(私のような)はその機能に悩まされています。



画像



すぐにすべてのiにドットを付けましょう。Fortを置き換えるつもりはありません。 Fortは、割り当てられたタスクを引き続き効率的に解決し、引退することはない中級レベルのプログラミング言語のファミリです。 しかし、私は別のニッチで考えています:初心者のためのプログラムの読みやすさ(可能な限り)に重点を置いた高レベルのスタック言語。 偉大な伝統と高いレベルには利点がありますが、同時にフォートの機能のいくつか(プラスの機能を含む)は失われます。



新しい架空の言語には、独自の哲学と独自の概念があります。 私はこれについて書き続けます。



ループ設計の選択



再帰があるだけでなく、遅かれ早かれ言語の創始者はみな、循環構造のセットについて考えます。 私は最初に、残りを導き出すことができる一般的な普遍的なサイクルの設計を検討し、次に、実際の経験に基づいて、最も一般的なケースのために追加の構成を追加することを好みます。



メインループは次のようになります。



repeat     -     when 1 do 1     when 2 do 2     when N do N     otherwise - end-repeat
      
      





怖いですよね? しかし、実際には、これは正式な説明にすぎず、すべてが基本的に機能します。 繰り返しごとに、すべての単語が繰り返しの後に実行されます。 次に、when条件の発生の計算。 condition1がtrueの場合、words1が満たされ、サイクルは最初から新しい反復を開始します。 condition1がfalseの場合、次の条件への遷移が発生します。 いずれの条件も真でない場合、ブランチはそれ以外の場合に実行されます。



このサイクルは、必要なものをすべて取り出すことができるという点で優れています。つまり、一般的な基本構造です。

1.無限ループ(省略された場合、および省略された場合、必要ありません):



 repeat    - end-repeat
      
      





ループ内のどこかに、終了条件とexit-repeatという単語のあるifがあると想定されます。



2.前提条件のあるサイクル:



 repeat    when -? do -    otherwise exit-repeat end-repeat
      
      





3.事後条件のあるサイクル:



 repeat    -    when - do exit-repeat end-repeat
      
      





4.カウンターを使用したループ(整数変数カウンターを例にとります):



 repeat    counter @ ( )    when 100 < do      |counter @ ++| counter set (   ,    100)      -    otherwise exit-repeat end-repeat
      
      





5.出力が中央にあるサイクル:



 repeat    --    when ? do exit-repeat    otherwise     --- end-repeat
      
      





6.そして、ダイクストラサイクルも!



何が起こったのか見てみましょう。 無限のサイクルは直感的で簡潔で、余分な言葉はないので、そのままにしておきます。 後条件付きのサイクルは、あまり一般的ではないため、別の構成には意味がありません。 そのようなサイクルがまだ必要な場合、それは明確で簡潔であることが判明したため、一般的な構造から容易に推測できます。



しかし、前提条件とカウンターのサイクルはより厄介であることが判明しました。 それらはしばしば必要なので、それらを別々の単語として実装するのは理にかなっています:



1.前提条件のあるサイクル:



 while  do     end-while
      
      







2.カウンターでサイクル:



 for - - to - step  do     end-for
      
      







3.そして、事後条件のあるサイクル(大きな欲求):



 loop        until - end-loop
      
      





重要な点に注意してください:whileおよびloopループは、単純なテキスト置換として実装できます。 実際、whileを「repeat when」に、end-whileを「otherwise exit-repeat end-repeat」に置き換えると、一般的なループが発生します。 事後条件付きのループは似ています。「繰り返し」へのループ、「まで」、「終了-繰り返しを行わない場合-終了-繰り返し」への終了ループです。 繰り返しループ自体は、必要に応じてifセットに変換できます。



つまり、トランスレーターに繰り返しとforの2つのループのみを実装する必要があります。 whileおよびloopループは、言語自体を使用したテキスト置換で実行できます。 同様のアプローチがEiffelとLispで採用されています。一般的な構造(たとえば、EiffelのloopとLispのcond)があり、非常に柔軟に使用できます。 可能であれば、古いデザインの上に新しいデザインを実装します。 フォートでは、原則は反対です。必要に応じて必要な構造を自分で作成できる特別なケースとツールがたくさんあります。



それぞれのアプローチには長所と短所があります。 「一般から特定へ」のアプローチは、プログラマーが翻訳者の「内臓」を調べて次の自転車の実装について賢明にする必要がないため、高レベルのプログラミングに適しています。 フォートでは、まず砦システムの内部を調べて新しい単語を入力する必要がありますが、新しい単語を無制限に使用し、迅速かつ効率的に実行する機会が得られます。 1つのアプローチが他のアプローチより優れているわけではなく、それらは異なるだけです。 私はコードを読むことの利便性と初心者のための単純さを気にしているので、私は主なものとして最初のアプローチを取りました。



条件付き構造



条件式でも同じことを行います。 一般化された構造(hello、Lisp!):



 cond    when 1 do 1    when 2 do 2    when N do N    otherwise - end-cond
      
      





Condは同じように繰り返しますが、繰り返しではなく1回だけです。 早期終了のために、exit-condという単語が提供されています。 condはrepeatからも推論できます。各ブランチの後にexit-repeatを置くだけです。 それだけです!



condはあらゆる複雑な分岐に使用できますが、いくつかの一般的なパターンを個別に実装すると便利です。



1.最も単純な条件演算子:



  if    1 else    2    (, ) end-if
      
      







2.ただし、ケースコンストラクトはスタックプログラミングに固有です。 何らかの変数xがあり、xの値に応じて特定のcondブランチを実行する必要があるとします。 condのwhenの前またはifの前(実装方法に応じて)にdupを配置する必要があります。つまり、重複や余分な要素のドロップを処理します。



 : testif dup 1 = if ." One" else dup 2 = if ." Two" else dup 3 = if ." Three" then then then drop ;
      
      





ここでの「ノイズ」という言葉は完全に不要です。 確かに、そのような状況が定期的に発生する場合、なぜだましやドロップを自動化し、読みやすさと簡潔さを増やさないのでしょうか? そして、2つの変数を比較する必要がある場合は? そして、もし3つ? これは、各条件の前にスタックでどれだけ賢明にしなければならないかです!



特にこのような状況では、ケースコンストラクトが必要です-condの「スマート」バージョン。 構文は非常に似ています:



 case --    when 1 do 1    when 2 do 2    when N do N    otherwise - end-cond
      
      





主な違いは、比較される要素が2倍になる前と、エンドケースの前に、余分なコピーがスタックから削除されることです。 つまり、case = cond + apart dup and dropです。 比較対象要素の数は、スタックの最上部の要素を2倍にする必要がある数を示します。



 x @ y @ case 2 [xy -- xyxy]    when = do "" print-string    when < do "y " print-string    otherwise "x " print-string end-case
      
      







新しい単語を入力し、置換を説明します



さて、私たちはすでにビルディングブロックを持っています。 解決策がありました-新しい単語の定義。 このような単語は、アセンブリ言語の観点から話す場合、呼び出しによって呼び出されます。



 define   end
      
      







 define-macro   end-macro
      
      





しかし、そのような単語はテキストの置換によって機能します。名前は本文に置き換えられます。 ご想像のとおり、while、loop、およびifは、次のようにマクロに実装できます。



 define-macro while    repeat when end-macro define-macro end-while    otherwise exit-repeat    end-repeat end-macro define-macro loop    repeat end-macro define-macro until    when end-macro define-macro end-loop    not do exit-repeat    end-repeat end-macro define-macro if    cond when do end-macro define-macro else    otherwise end-macro define-macro end-if    end-cond end-macro
      
      





美を表す別のよく知られた言葉を紹介します



 define-macro break!    do exit-repeat end-macro
      
      





これで、非常に簡潔で明確なものを書くことができます

 repeat ... when 666 = break! ... end-repeat
      
      







合計:基本テキスト置換の助けを借りたrepeatとcondの柔軟な構成のおかげで、あらゆる場面でビルディングブロックのセット全体を実装できます。 Fortとは異なり、言葉の実装について考える必要はまったくありません。実装の詳細から抽象化します。



たとえば、cond and repeatの場合の単語は、本質的に見た目がよく、条件を他の単語から視覚的に分離できます。 しかし実際には、doという単語だけが何らかの役割を果たします。 極端に簡潔にしたいですか?



 define-macro ==>    when do end-macro
      
      





そして書く

 cond    x @ y @ = ==> ""    x @ y @ < ==> "y "    otherwise "x " end-cond print-string
      
      





翻訳者の実装をまったく考えずに。 これは私たちの関心事ではなく、私たちは高いレベルにあります!



次回は、コンパイラがプログラムのソースコードを解析すること、スタック、データ、変数について話します。



All Articles