プログラミング言語J.アマチュアを見てください。 パート2.暗黙のプログラミング

プログラミング言語の前の記事J.アマチュアの外観。 パート1.はじめに

質問:関数がデータを変更し、オペレーターが関数を変更した場合、誰がオペレーターを変更しますか?
回答:ケン・アイバーソン
チラグ・パタク


Jは、暗黙の(暗黙の "tacit"という言葉から)プログラミングの考え方を使用します。これは、定義された関数(プログラム)の引数について明示的に言及する必要はありません。 暗黙のアプローチでの作業は、原則として、個々の要素ではなく、データ配列で行われます。
TacitプログラミングがAPLの前にBackusによって発見され、FP言語で実装されたことに注目することは興味深いです。 このアプローチをサポートする最新の言語(もちろんJを除く)では、Haskell(ポイントフリーアプローチのため)と同様に、Fortや他の連結言語に名前を付けることができます。

1.動詞


最初の動詞を定義します。 便宜上、動詞は引数がデフォルトで指定された関数であると想定できます。 インタプリタJを開き、紹介します

neg =: - 


ここで、「neg」は動詞の名前、「=:」は割り当て演算子、「-」は動詞の実際の本体で、組み込みの動詞「-」の呼び出しで構成されています。 それでは、動詞を意味に適用してみましょう(つまり、それを呼び出します)。 たとえば、ユニット化するには:

  neg 1 _1 


例からわかるように、結果は数値「-1」になります(Jでは、マイナスが組み込みの動詞に使用されるため、アンダースコアを使用して負の数値を記述します)。

ここで、変数(Jでは「名詞」と呼ばれる)「x」を定義し、-1の値を割り当てます。

  x =: _1 


neg x構文は1を返すと予想されます。 これは本当です:

  neg x 1 


この呼び出しは簡単です

  - x 1 


別の例:

  x _1 neg - 


つまり 変数「x」の値は「-1」であり、動詞の値はその本文です。 動詞「9 !: 3」を使用して、画面に値を表示する方法を変更できます。
別の動詞を定義します。これは、組み込みの動詞の単純な同義語にもなります。

  twice =: +: 


この動詞は、引数の意味を倍にします。 ここでは、動詞の命名の規則性を追跡できます。たとえば、動詞「+:」が引数を2倍にすると、動詞「-:」が引数を半分にします。

  NB.    x =: _1 NB. ,  «NB.»   . NB.     «Nota Bene»,    « ». twice x _2 twice (neg x) 2 


それにしても:

  twice (-: 2) 2 


そしてそう

  +: (+: (- 2)) _8 


2.モナドとダイアド


このセクションはHaskellとは関係ありません。


動詞は、1つまたは2つの引数で呼び出すことができます。 Jに関して1つの引数(オペランド)を持つ動詞はモナドと呼ばれ、2つの引数を持つ動詞はダイアドと呼ばれます。 後者の場合、引数は呼び出された動詞の左と右(それぞれ最初と2番目の引数)に書き込まれます。 最初のモナドを思い出してください:

  minus =: - minus 1 _1 minus 7 _7 


ただし、定義した動詞は2つの引数で呼び出すことができます。

  4 minus 2 2 12 minus 5 7 


この場合、動詞マイナスは2項として使用され、置換の結果として、式「4–2」および「12–5」がそれぞれ計算されます。

2つの引数は、動詞の定義と呼び出しに使用できる最大値です。
J言語が2次元構文(たとえば、難解なBefunge言語など)をサポートしている場合、引数は左右だけでなく上から下に記述することができると想像できます。 幸いなことに、Jでは線形構文のみがサポートされています。

別の例として、動詞「div」を組み込み動詞「%」の同義語として定義します。 さらに、「%」は除算演算子であり、他のタスクにはより馴染みのある「/」記号が使用されます(後で説明します)。

  div =: % 1 div 2 0.5 4 div 3 1.3333 


この例では、動詞はダイアドと呼ばれ、それぞれ1で2分割し、4で3分割します。 また、オペランドは整数ですが、除算結果は浮動小数点数であることに注意してください。

1つのオペランドで呼び出されると、動詞「%」はデフォルトでユニットをこのオペランドに分割します。

  div 2 0.5 div 4 0.25 


上記の式は、それぞれ「1%2」および「1%4」と書くことができます。

上記の例では、言語のかなり重要な機能が使用されています。同じ動詞のモナド呼び出しとダイアド呼び出しは、意味がまったく異なるアクションを実行できます。 したがって、たとえば、単項の場合の動詞「*:」は引数の二乗を計算し、二項の場合は論理演算「Not-And」を実行します。

  *: 3 9 *: 4 16 0 *: 0 1 1 *: 1 0 0 *: 1 1 1 *: 0 1 


動詞が左または右の引数を明示的に使用するために、動詞「[」および「]」が意図されています。 最初の引数は左引数を返し、2番目の引数は右引数を返します。 例:

  11 [ 22 11 11 ] 22 22 ] 33 33 [ 44 NB.  ,   ,  [   . 44 


荷物を定数動詞「0:」、「1:」、「2:」、...、「9:」で補完します。 これらは定数と呼ばれます。これは、引数を指定しても、常に同じ値を返すためです。 「0:」はゼロ、「1:」-1などを返します。 例:

  7 3: 9 3 3: 7 3 


3.組合


さらに、同じ動詞に対して、モナド(1つの引数)呼び出しとダイアディック(2つの引数)呼び出しに対して異なる動作を定義する必要があるとします。 これは十分に根拠のある欲求です。なぜなら Jは動的言語であり、コードは誤用から安全ではありません。 動詞のモナドとダイアディックの振る舞いを分離するために、特別な結合「:」があります。

動詞がデフォルトで1つまたは2つの引数を持つ関数であり、引数の値が変数または即値のいずれかである場合、この場合、「ユニオン」は2つの関数です(そして2つだけの引数は動詞を取ります。 連合と動詞は、ユーザーが独自に決定できます。

ここでは、品詞との類似性を描くことができます(Jで、後で見るように、副詞もあります)が、これは不必要な混乱につながると著者に思われます。

モナドに戻りましょう。 ユニオン「:」の左側には、動詞の単項呼び出しが記録され、右側にはダイアディックがあります。 例:

  v =: 1: : 2: 11 v 22 2 v 11 1 


動詞「v」をダイアドとして呼び出す場合(この例では、引数「11」と「22」を使用)、式「2:」が参照されます。 モナドとして(例では-引数 "11"を使用)、「1:」になります。 そして、これらは常に意味を返す定数動詞です。最初の場合は「2」、2番目の場合は「1」です。

これまでのところ、数値のみを操作していました。 今回は、一重引用符で記述された文字列を使用します(Jの二重引用符は結合です)。 前述の動詞「v」を再定義します。

  v =: 'monadic call' : 'dyadic call' v 11 |domain error 11 v 22 |domain error 


ご覧のとおり、定義に誤りがあります。 文字列は動詞ではなく意味であるため、アクションは生成されません。 また、動詞のタクティックな定義では、動詞の適用順序とその組み合わせのみを示す必要があります。

Jの文字列と配列(少し後で説明します)を操作できます。 たとえば、モナド呼び出しの動詞「#」は、配列の長さを返します。

  # 23 1 # '12ab' 4 # '012345' 6 # '' 0 


前に、引数の符号を変更する動詞negを定義しました。 ただし、ダイアディックバージョンでこの動詞を呼び出すと、次のようになります。

  neg =: - 1 neg 2 _1 


つまり 置換後、動詞は「1-2」の減算として機能しました。 動詞の範囲をモナド呼び出しのみに制限することをお勧めします。 前述のように、Jの動的な性質により、プログラムの実行中に同様のチェックを実行する必要があります。 おそらく、Jの他のすべてと同様に、このようなチェックは可能な限り短く書かれています。

  neg =: - : [: 


新しい動詞「[:」を使用しました。 「J for Cプログラマー」という本では、この動詞は「自殺動詞」と呼ばれています。呼び出されると、プログラムがクラッシュするからです。

  neg 1 _1 1 neg 2 NB.      - - |domain error 


このような状況をキャッチするために、いわゆるを使用できます。 「トラップ」。 Jでは、単語の通常の意味に例外はなく、代わりにユニオン「::」が使用されます。

ユニオン「::」はその左側の式を実行し、エラーで終了した場合は右側の式を実行します。 例:

  v =: [: :: 3: 1 v 2 3 v 7 3 


前述のように、左側の式が最初に実行されるため、この動詞は常に3を返します。 そして、左側に動詞自殺を置きます。 したがって、動詞「v」の各呼び出しでの「::」の接続詞は、自殺「[:」をキャッチし、すべての引数を右側に書き留めた動詞に渡します。 動詞定数「3:」。

3.1。 連続組合



これまでのところ、単一の動詞のみを使用しました。 逐次計算を実行するために、ユニオン「@」または特殊動詞「[:」が使用されます。これは、動詞の構成の最初の要素であり、スキップされ、逐次計算を実行できます。

  ([: +: +:) 10 NB.  (+: (+: 10)) 40 (+: @ +:) 10 NB.  (+: (+: 10)) 40 


コレクションの次のユニオンは「&。」です。その原理は次のとおりです。

  u&.vy NB.  v_ uvy x u&.vy NB.  v_ (vx) u (vy). +:&.*: 10 14.1421 %: @: +: (*: 10) NB.    14.1421 %: (+: (*: 10)) NB.   14.1421 

ここで、「v_inversion」は「v」の逆動詞を意味します。 「+:&。*:」という表現では、反転が行われる動詞は「*:」です(これは、結合「&。」の右側に立っているためです)。 そして、動詞の逆「*:」は「%:」です。 つまり 二乗の逆は平方根を取ります。

私たちにとって便利な別のユニオンは「&」です。これは、動詞に名詞(オペランド値)を追加することにより、ダイアディックコールから単項を作成します。 一定の仮定で、この組合はカレーを実施していると言えます。 例:
  inc =: 1&+ NB.  inc 41 42 inc _3 _2 div3 =: %&3 div3 9 3 div3 12 4 


モナドの場合にのみ増分を定義します:

  inc =: 1&+ : [: inc 1 2 1 inc 2 |domain error: inc | 1 inc 2 


4.副詞



前に副詞について言及しました。 J言語の観点では、副詞は引数として動詞を取り、異なる動作を持つ新しい動詞を形成する式です。

副詞「〜」を与えます。これは、ダイアディックコールの引数を交換し(「xu〜y」は「yux」に置き換えられます)、動詞の単項呼び出しをダイアッドに置き換え、オペランドをコピーします(「u〜y」は「yuy」に置き換えられます)。

  % 2 NB.   ,    «1 % 2» 0.5 %~ 2 NB.  «2 % 2» 1 2 % 3 0.666667 2 %~ 3 NB.  «3 % 2» 1.5 


最もよく使用される副詞の1つは副詞/(「between」)です。これは、配列のセクションまで延期します。

5.フックとフォーク



Jには非常に独特な機能の構成方法があります。2つの連続する動詞が示されている場合、これはそれらが連続して適用されることを意味しません。 それらは、ダイアディックコールとモナドコールに対して個別に特別なコール順序を持ちます。 このような2つの動詞の構成は、フック(フック)と呼ばれます。 動詞vを定義します。これは、動詞fとgのフック呼び出しです。

  v =: fg 


vを名詞yのモナドとして呼び出す場合:

  vy 


この呼び出しは次と同等です:

  yf (gy) 


動詞vの単項呼び出しにもかかわらず、フックの要素の1つ(つまりf)は、唯一のオペランドの左側に重複を持つダイアドとして呼び出されることに注意してください。

いくつかの名詞xとyでvをダイアドとして呼び出す場合:

  xvy 


この呼び出しは次と同等です:

  xf (gy) 


モナド呼び出しで文字列/配列の長さを返す動詞ラティス«#»思い出してみましょう。ダイアドでは、左オペランドに示されている回数だけ右オペランドを呼び出します。 ダイアドを定義する:

  v =: # # 


上記のスキームに従って、動詞vの二項呼び出しを展開します。

   # (# y) 


ご覧のとおり、vはオペランドの長さに等しい数をy、x回コピーします。 例:

  2 v '1234' 4 4 


3つの連続した動詞がフックからのフックであると想定できます。 ただし、これはそうではありません-代わりに、フォーク構成(いわゆるフォーク)が実装されます。 だから、フォークのモナド呼び出し

  (fgh) y 


次の呼び出しと同等:

  (fy) g (hy) 


そのような式の例は、数字の配列の平均を計算する動詞です。

  mean =: +/%# mean 0 1 2 3 4 5 2.5 


このような式は次と同等です。

  ((+/ @ [ )%(# @ ])) 0 1 2 3 4 5 2.5 


別のフォークの例を考えてみましょう。

  v =: # # # v '12345' 5 5 5 5 5 


ご覧のとおり、この式は、オペランドの長さと同じ数のオペランドをコピーします。

前の動詞は、フォークを使用せずに書き直すことができます。

  ((#@]) # (#@])) '12345' 5 5 5 5 5 


dyad呼び出しで名詞xとyにフォークを定義します。

  x (fgh) y 


これは、次の式と同等です。

  (xfy) g (xhy) 


古い友人の助けを借りてダイアドフォークを説明します-動詞の意味:

  2 mean 3 1.66667 1.66667 


この表現を明らかにします。

  (2 +/ 3) % (2 # 3) 


式の右側が3の2倍をコピーします。 そして、左のものは、左と右のオペランドの合計を計算します。 T.O. 私達は得る:

  5 % 3 3 1.66667 1.66667 


Jで4つの動詞が指定されている場合、この式はフォークからのフック、5がフォークからのフォーク、6-フォークからのフォークなどのフックになることを理解することが特に重要です。 つまり 式「(#######)」と「(#(##(####)))」は同等です。

括弧を使用して計算の順序を変更する方法を示します。 例:

  (# # #) 3 1 


でも

  (# (# #)) 3 1 1 1 1 1 1 1 1 1 


最後の式は次と同等です。

  3 # (3 # (# 3) 3 # 1 1 1 NB.  3   . 


関数の構成を使用したより複雑な例を示しましょう

  (* + - %) 3 8 


この式は次と同等です。

  3 * ((+ 3) - (% 3)) 


5つの動詞の組み合わせは次のようになります。

  (>: * + - %) 3 10.6667 


最後の式は次と同等です。

  (>: 3) * ((+ 3) - (% 3)) 4 * (3 – 0.333) 


プログラミング言語の次の記事J.アマチュアの外観。 パート3.配列

Source: https://habr.com/ru/post/J198066/


All Articles