5

元のスクリプトは次のようになります。

#lang racket
(for ([i (in-range 3)])
    (for ([j (in-range 9)])
      (display "X"))
     (display "\n"))

(for ([i (in-range 6)])
  (for ([j (in-range 3)])
    (display " "))
  (for ([j (in-range 3)])
    (display "X"))
  (for ([j (in-range 3)])
    (display " "))
  (display "\n"))

(for ([i (in-range 3)])
    (for ([j (in-range 9)])
      (display "X"))
     (display "\n"))

出力は次のとおりです。

XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
   XXX   
   XXX   
   XXX   
   XXX   
   XXX   
   XXX   
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX

次のような DSL を使用してこれを書き直すことができるかどうか疑問に思っています。

(define a
  "3 9 X
6 3 b 3 X 3 b
3 9 X")

その後:

(interpret a)

このグラフを描画します。

それを行う最善の方法を知っている人はいますか?

4

2 に答える 2

10

このような問題に対処するには、表面的な構文に集中するのではなく、最初に DSL で必要な操作をキャプチャするデータ型を記述します。データ型を手に入れたら、問題を簡単に解決できるはずです。

一見すると、あなたの言語で 3 つの基本的な形式を設計できるように見えます。

  1. ストリングス
  2. 繰り返し
  3. シーケンシング

このばらばらなクラスは、プリミティブ文字列と構造体で表すことができます。このクラス全体を「印刷可能な expr」の 'pexpr' と呼びましょう。コード内:

;; An pexpr is one of the following:
;;   * a primitive string,
;;   * a seq, or
;;   * a repeat
(struct seq (bodies) #:transparent)    ;; bodies is a list of pexpr
(struct repeat (n body) #:transparent) ;; n is a number, body is a pexpr

「seq」と「repeat」はそれ自体が少し長いので、一部のヘルパー関数を省略形にすることが役立つ場合があります。

;; For convenience, we define some abbreviations s and r for seq and repeat,
;; respectively.
(define (s . bodies)
  (seq bodies))
(define (r n . bodies)
  (repeat n (seq bodies)))

あなたの例の「I」文字列は次のように書くことができます:

(define an-example
  (s
   (r 3 (r 9 "X") "\n")
   (r 6 (r 3 " ") (r 3 "X") "\n")
   (r 3 (r 9 "X") "\n")))

このエンコーディングには、改行の明示的な表現があることに注意してください。これは、表面の構文だけからすると暗黙的です。次に、表面構文の行を取得して pexpr に変換するのはパーサーの仕事になりますが、それほど難しくはありません。うまくいけば。:)

とにかく、解釈機能は、次のようなテンプレートの詳細を入力することの問題になります。

(define (interpret pexpr)
  (match pexpr
    [(? string?)
     ...]
    [(struct seq (bodies))
     ...]
    [(struct repeat (n body))
     ...]))

「...」は簡単に入力できるはずです。

この種の問題に対するこのアプローチは、How to Design Programs and Programming Languages: Application and Interpretationで説明されているものです。それらを見ることをお勧めします。それらは良いものです。

于 2012-09-10T19:15:30.623 に答える
2

確かに、それは実行可能に見えます。ほとんどの場合、これは解析の問題です。このように分解します。各入力行は、出力行のブロックを指定します。ラケットでそれを表現する良い方法を見つけてください。カバーしたい内容を確実にカバーするために、いくつかの例を作成します。次に、おそらくこれらのブロックの 1 つをレンダリングする関数を作成します。ほとんどの場合、出力を見て満足できるように、最初にそれを行います。次に、これらのブロック仕様のリストを取り、それらをすべて出力する関数を作成します。次に、1 行の入力を解析する関数を作成します。これらの行を空白を使用して (たとえば、「regexp-split」を使用して) 分割し、アドホック パーサーを使用してこれらのリストを処理できるようです。これは、私が間違いを犯す可能性が最も高いと思われる部分です。d コーディングする前に、一連のテスト ケースを作成します。最後に、入力の各行でこのパーサーを呼び出し、結果のブロック仕様を表示関数に送信する関数が必要です。

于 2012-09-10T06:35:51.810 に答える