1

継承したいくつかの反復 Java ライブラリ コードから Clojure seq を作成しようとしています。基本的に、Java コードが行うことは、パーサーを使用してファイルからレコードを読み取り、それらのレコードをプロセッサに送信して、結果の ArrayList を返すことです。Java では、これは parser.readData() を呼び出してから parser.getRecord() を呼び出してレコードを取得し、そのレコードを processor.processRecord() に渡すことによって行われます。parser.readData() を呼び出すたびに、単一のレコードが返されるか、それ以上レコードがない場合は null が返されます。Java ではかなり一般的なパターン。

そこで、パーサーから次のレコードを取得する次のレコード関数を Clojure で作成しました。

(defn next-record
  "Get the next record from the parser and process it."
  [parser processor]
  (let [datamap (.readData parser)
        row (.getRecord parser datamap)]
    (if (nil? row)
    nil
    (.processRecord processor row 100))))

次に、この関数を呼び出して、レコードを Clojure seq (できれば遅延 seq) に蓄積します。したがって、レコードが多すぎない限り、うまく機能する私の最初の試みは次のとおりです。

(defn datamap-seq
  "Returns a lazy seq of the records using the given parser and processor"
  [parser processor]
  (lazy-seq
    (when-let [records (next-record parser processor)]
      (cons records (datamap-seq parser processor)))))

パーサーとプロセッサーを作成し、(take 5 (datamap-seq parser processor)) のようなことを行うと、レイジー seq が得られます。そして、予想どおり、その seq の (最初の) 取得は 1 つの要素のみを認識し、count を実行するとそれらすべてが認識されます。

もちろん、多くのレコードがある場合、StackOverflowException が発生します。そこで、私の次の試みは loop-recur を使って同じことをすることでした。

(defn datamap-seq
  "Returns a lazy seq of the records using the given parser and processor"
  [parser processor]
  (lazy-seq
    (loop [records (seq '())]
      (if-let [record (next-record parser processor)]
        (recur (cons record records))
        records))))

これを同じ方法で使用し、(def results (datamap-seq parser processor)) を使用して定義すると、遅延 seq が得られ、要素が認識されません。ただし、(最初の結果)のようなことをするとすぐに、seq全体の実現が強制されます。

loop-recur を使用して2番目の関数でどこが間違っているのかを理解してくれる人はいますか?

アップデート:

例外のスタック トレースを詳しく調べたところ、Java クラスの 1 つからスタック オーバーフロー例外がスローされています。しかし、このような datamap-seq 関数がある場合にのみ発生します (上に投稿したものは実際に機能します)。

(defn datamap-seq
  "Returns a lazy seq of the records using the given parser and processor"
  [parser processor]
  (lazy-seq
    (when-let [records (next-record parser processor)]
      (cons records (remove empty? (datamap-seq parser processor))))))

その削除が問題を引き起こす理由はよくわかりませんが、この関数から削除すると、すべて正常に機能します(空のリストの削除を別の場所で行っています)。

4

2 に答える 2

4

loop/recurは、再帰がなくなるまで、ループ式内でループします。その周りに遅延シーケンスを追加しても、それは妨げられません。

lazy-seq / cons を使用した最初の試みは、スタック オーバーフローなしで、希望どおりに機能するはずです。コードの Java 部分にある可能性がありますが、問題が何であるかは今のところわかりません。

于 2012-09-14T16:06:43.113 に答える
2

Joostの回答に追加してここに投稿します。このコード:

(defn 整数 [開始]
  (遅延シーケンス
    (短所
      始める
      (整数 (開始を含む)))))

次のようなことをすると、StackOverflowExceptoin はスローされません。

(take 5 (drop 1000000 (integers)))

編集:

もちろん、より良い方法は(iterate inc 0). :)

EDIT2:

lazy-seq がどのように機能するかを少し説明してみます。lazy-seqは、seq ライクなオブジェクトを返すマクロです。要求されるまで2番目の引数を認識しないという短所と組み合わせると、怠惰になります。

LazySeq クラスがどのように実装されているかを見てみましょう。LazySeq.sval「凍結された」遅延シーケンスの別のインスタンスを返す次の値の計算をトリガーします。メソッドLazySeq.seqは、概念の背後にある仕組みをさらによく示しています。シーケンスを完全に実現するには、while ループを使用することに注意してください。それ自体は、スタック トレースの使用が、LazySeq の別のインスタンスを返す短い関数呼び出しに限定されていることを意味します。

これが意味をなすことを願っています。ソースコードから推測できることを説明しました。間違いがありましたらお知らせください。

于 2012-09-14T16:14:40.053 に答える