2

Unix の哲学に従って、bash スクリプトを使用して結合された一連の小さなプログラムを含むプロジェクトがあります。彼らの交換フォーマットはもともと次のようなものでした:

meta1a:meta1b:meta1c AST1
meta2a:meta2b:meta2c AST2

:区切られたフィールドはメタデータであり、ASTはスクリプトがそのまま渡す s 式です。cut -d ' 'AST からメタデータを分割し、メタデータcut -d ':'を掘り下げるために使用できるため、これはうまく機能しました。ただし、スペースを含むメタデータ フィールドを追加する必要があったため、この形式が壊れてしまいました。タブを使用するフィールドがないため、次のように切り替えました。

meta1a:meta1b:meta1c:meta 1 d\tAST1
meta2a:meta2b:meta2c:meta 2 d\tAST2

将来、さらに多くのメタデータ フィールドが追加されることを想定しているため、「句読点を推測する」というゲームをプレイするのではなく、より構造化された形式に切り替える時が来たと思います。

区切り文字の代わりに、cutJSON と を使用するjqことも、XML と を使用することもできますxsltprocが、既に AST に s 式を使用しているため、代わりにここでそれらを使用する良い方法があるかどうか疑問に思っています。

たとえば、次のようになります。

(echo '(("foo1" "bar1" "baz1" "quux 1") ast1)'
 echo '(("foo2" "bar2" "baz2" "quux 2") ast2)') | sexpr 'caar'

"foo1"
"foo2"

私の要件は次のとおりです。

  • 私のプログラムがデータを読み書きする場所であるため、最小限のボイラープレートでstdioを簡単に使用できます
  • シェル スクリプトから簡単に呼び出すことができる、 bash のプロセス呼び出しとパイプライン処理に代わる非常に魅力的な手段を提供します
  • 可能であればストリーミング I/O。すなわち。クロージングを探して入力全体を消費するよりも、一度に 1 つの AST で作業したい)
  • 特に数回呼び出される場合は、高速で軽量です。各 AST はわずか数 KB ですが、合計すると数百 MB になる可能性があります
  • 少なくとも Linux では動作するはずです。クロスプラットフォームはいいだろう

Lisp/Scheme インタープリターを使用するのが当然の選択ですが、私が経験した唯一の方法は Emacs で、これはあまりにも重すぎます。おそらく、別の実装がより軽量でこれに適していますか?

Haskell では、shelly、turtle、atto-lisp をいじりましたが、私のコードのほとんどは、String/Text/ByteString 間の変換、 のラップ/ラップ解除、Lisp独自の 、 、 などの実装carcdr費やさconsれました。

scshについて少し読んだことがありますが、それが適切かどうかもわかりません。

4

2 に答える 2

0

少し異なるタスクに取り組んでいると、再び大量の s 式を処理する必要があることがわかりました。今回は、不透明な文字列として渡すオプションを使用するのではなく、指定された s-expression の重要な処理 (使用されているシンボルのリストの抽出など) を実行する必要がありました。

私はRacketを試してみて、嬉しい驚きを覚えました。それは、私が以前に使った他の Lisp (Emacs Lisp とさまざまなアプリケーション固有の Scheme スクリプト) よりもはるかに優れていました。なぜなら、それには優れたドキュメントがあり、バッテリーが標準ライブラリに含まれているからです。

この種のタスクに関連するいくつかのポイント:

  • データを読み書きするための「ポート」。これらは (動的に?) 式全体にスコープを設定でき、デフォルトで stdio になります (つまり(current-input-port)、デフォルトで stdin になり、(current-output-port)デフォルトで stdout になります)。ポートは stdio とファイルへのアクセスをシェルと同じくらい使いやすいものにします。
  • port->stringfile->lines、などのさまざまな変換関数をread使用すると、適切な粒度 (文字、行、文字列、式など) でデータを簡単に取得できます。
  • 複数の s 式を読み取る「標準的な」方法を見つけることができませんでした。read返されるのは 1 つだけであるため、ストリーミング方式でこれを行うには反復/再帰が必要になります。
  • ストリーミングが必要ない場合は、入力全体を文字列として読み取り、追加"(\n"して"\n)"から を使用(with-input-from-string my-modified-input read)して 1 つの大きなリストを取得するのが最も簡単であることがわかりました。

Racket の起動時間がかなり遅いことがわかったので、速度が問題になる場合は、ループの一部としてスクリプトを何度も呼び出すことはお勧めしません。ループを Racket に移動し、スクリプトを 1 回呼び出すのは簡単でした。

于 2016-05-17T17:48:24.650 に答える