Clojureを学ぶために、 4clojureで問題を解いています。私は現在、 DFAが受け入れる言語 (の一部) を列挙する質問164について歯を磨いています。興味深い条件は、言語が無限である可能性があるため、ソリューションが遅延する必要があることです (その場合、ソリューションのテスト ケース.(take 2000 ...
私のマシンで機能するソリューションがありますが、それを Web サイトに送信すると、スタックが吹き飛ばされます (決定される許容文字列の量を 2000 から 20000 に増やすと、スタックもローカルで吹き飛ばされるため、私の解決策の欠陥)。
私の解決策[1]は次のとおりです。
(fn [dfa]
(let [start-state (dfa :start)
accept-states (dfa :accepts)
transitions (dfa :transitions)]
(letfn [
(accept-state? [state] (contains? accept-states state))
(follow-transitions-from [state prefix]
(lazy-seq (mapcat
(fn [pair] (enumerate-language (val pair) (str prefix (key pair))))
(transitions state))))
(enumerate-language [state prefix]
(if (accept-state? state)
(cons prefix (follow-transitions-from state prefix))
(follow-transitions-from state prefix)))
]
(enumerate-language start-state ""))
)
)
DFA を受け入れる
'{:states #{q0 q1 q2 q3}
:alphabet #{a b c}
:start q0
:accepts #{q1 q2 q3}
:transitions {q0 {a q1}
q1 {b q2}
q2 {c q3}}}
DFA が受け入れる言語を返します ( #{a ab abc}
)。ただし、DFA の最初の 2000 の受け入れられた文字列を決定するとき
(take 2000 (f '{:states #{q0 q1}
:alphabet #{0 1}
:start q0
:accepts #{q0}
:transitions {q0 {0 q0, 1 q1}
q1 {0 q1, 1 q0}}}))
それはスタックを吹き飛ばします。明らかに、ソリューションを末尾再帰的に再構築する必要がありますが、それがどのように可能かわかりません。特に、遅延性と末尾再帰性を組み合わせることがどのように可能かさえわかりません ( または のいずれrecur
かを介してtrampoline
)。このlazy-seq
関数はクロージャを作成するため、recur
insidelazy-seq
を使用するとクロージャが再帰ポイントとして使用されます。lazy-seq
insideを使用する場合、引数を評価する必要がある関数呼び出しを発行するためrecur
、lazy-seq
は常に評価されます。recur
を使用する場合trampoline
、要素を遅延評価できるリストを繰り返し作成する方法がわかりません。私がそれを使用して使用したのを見ると、trampoline
最終的に終了したときにのみ値を返すことができます (つまり、トランポリン関数の 1 つが関数を返さない)。
他のソリューションは範囲外と見なされます
この質問の範囲外で、この 4Clojure の問題に対する別の種類の解決策を考えています。私は現在、を使用してソリューションに取り組んでいますiterate
。各ステップは、「次のステップ」(現在の状態からの遷移に続く) が受け入れる文字列のみを計算するため、再帰はまったくありません。次に、現在の状態と、その状態になった文字列 (次の状態のプレフィックス) のみを追跡します。この場合、難しいのは、有限言語を受け入れる DFA が結果を返さなくなる時期を検出することです。take-while
周囲の適切な停止基準をまだ考案していませんiterate
、しかし、私はこのソリューションをうまく機能させることができると確信しています。この質問では、基本的な質問に興味があります: 怠惰と末尾再帰を組み合わせることができるか、それとも基本的に不可能ですか?
def
[1]とを使用できないなど、サイトにはいくつかの制限があることに注意してくださいdefn
。