foreachについて話すのをやめます。 例として、互いに非常によく似た2つの演算子を直接示します。1つは実装として「例外」を使用し、もう1つは「戻りコード」を使用します。
プロセス例外 [ マッピングと本体 ]
ここで、 マッピングは{例外1 <->処理方法1、例外2 <->処理方法2、...}、body-例外が発生する可能性のある演算子の形式のマップです。
および2番目の演算子:
process-retcode-answer [ マッピングとボディ ]
ここで、 マッピングは{戻りコード1 <->処理方法1、戻りコード2 <->処理方法2、...}の形式のマップです。 本文 -サブプログラムの応答で終わるステートメントの本文または必要な呼び出し先システム戻りコードに基づいたプロセス。
彼らが働くのを見てみましょう。
プロセス再コード回答
リターンコード0、-1、-2、および残りのコードを処理するロジックを処理する関数があるとします。
( defn ok-processor [結果]
( println ( str " ok。result :" result ) ) ))
( defn error-processor [結果]
( println ( str " error。result :" result ) ) ))
( defn another-error-processor [結果]
( println ( str "another error。result :" result ) ) )
( defn unknown-error-processor [結果]
( println ( str "unknown error。result :" result ) ) )
それらを処理する関数の名前のマップ戻りコードを定義します。
( def result-mapping { 0 'ok-processor
-1 'エラープロセッサ
-2 '別のエラープロセッサ
: その他の 'unknown-error-processor } )
次に、さまざまな戻りコードと対応する結果を返すテストルーチンを作成します。
( defn test-call-ok [ ]
[ 0 "テスト結果" ] )
( defn test-call- error [ ]
[ -1 "テスト結果" ] )
( defn test-call-another- error [ ]
[ -2 "テスト結果" ] )
( defn test-call-unknown- error [ ]
[ -1000 "テスト結果" ] )
この場合のオペレーターの作業は次のようになります。
( process-retcode-answer結果マッピング( test-call-ok ) )
わかった 結果: テスト結果
( process-retcode-answer result-mapping ( test-call- error ) )
エラー 結果: テスト結果
( process-retcode-answer result-mapping ( test-call-another- error ) )
別のエラー 。 結果: テスト結果
( process-retcode-answer結果マッピング( test-call-unknown- error ) )
不明なエラー 。 結果: テスト結果
ここでは、各ボディは1つのメソッドのみで構成されています。 実際には、代わりに任意の関数シーケンスを挿入できます。
この演算子の利点は、ハンドラーを実装し、マッピングを適切に変更することにより、新しいコードの処理または既存のハンドラーの変更が透過的に実行されることです。
ifsはありません。 このアプローチは、リターンコードを処理するためのかなり柔軟なパターンを実装します。
プロセス例外
前の例との類推によって。 いくつかの例外を処理するための関数があります。
( defn算術例外プロセッサ[ e ]
( println ( str "算術例外。" ) ) )
( defn nullpointer-exception-processor [ e ]
( println ( str "Nullpointer exception。" ) ) )
( defn another-exception-processor [ e ]
( println ( str "その他の例外。" ) ) )
それらを処理する関数の名前の例外をマップします。
( def exception-mapping { java。lang。ArithmeticException 'arithmetic-exception-processor
java lang 。 NullPointerException 'nullpointer-exception-processor
java lang 。 例外 'another-exception-processor } )
さまざまな例外を生成するテストルーチンを作成します。
( defn test-call-ok [ ]
「テスト結果」 )
( defn test-throw-arithmetic-exception [ ]
( throw ( new java。lang。ArithmeticException ) )
「テスト結果」 )
( defn test-throw-nullpointer-exception [ ]
( throw (新しいjava。lang。NullPointerException ) )
「テスト結果」 )
( defn test-throw-other-exception [ ]
( throw (新しいjava。lang。ClassNotFoundException ) )
「テスト結果」 )
オペレーターの仕事:
(プロセス例外の例外マッピング
( test-call-ok ) ))
「テスト結果」
(プロセス例外の例外マッピング
( test-throw-arithmetic-exception ) )
算術例外。
(プロセス例外の例外マッピング
( test-throw-nullpointer-exception ) )
ヌルポインター例外。
(プロセス例外の例外マッピング
( test-throw-other-exception ) )
その他の例外。
前のステートメントの説明の最後にある注意がここに適用されます。
結論
これらの演算子は非常に似ており、原則として、同じ応答処理パターンを実装しますが、実装は異なります。 ここでは、 process-retcode-answerを優先します 。 他の言語では、例外を使用するバリアントと比較して、リターンコードを持つバリアントが常に有利であるとは限りません(もちろん、問題の条件と言語自体に依存します-これについては既に説明しました)。
上記の演算子の実装オプションは次のとおりです。
( defmacroプロセス例外[マッピングと本体]
( let [ catch-items ( map ( fn [ m ]
` ( catch〜 ( first m ) e#
( 〜 ( eval ( second m ) ) e# ) ) )
( eval mapping ) ) ]
` ( try〜@ body
〜@ catch-items ) ) )
( defmacro process-retcode-answer [ mapping & body ]
` ( let [ answer# ( do〜@ body )
retcode# (最初の回答# )
結果# ( 2番目の回答# )
プロセッサー# ( get〜マッピングretcode# )
プロセッサ# ( if ( nil ?processor# ) ( : other〜mapping )プロセッサ# ) ]
( ( eval processor# ) result# ) ) )
このかなり誇張された例は、新しいコンストラクトで言語を拡張し、同じ高次関数、ラムダ、クロージャーを持つ機能のように、言語の基本要素はあまり重要ではないことを示しています。 このような言語により、プログラマーはアーティストになれます。 彼は何度も何度もパターンを書きません。 彼は、自分の仕事の解決策を最も自然に、グラフィカルに、簡潔に定式化できる言語を単に「作成」します。 職業は工芸ではなく芸術になります。