3

同じClojure関数でrecur機能とpost-condition機能の両方を使用することは可能ですか?事後条件を使用して例外をスローすることを望んでいましたが、Clojureは何らかの理由で再発後に例外スローコードをラップしようとしているようであるため、(ばかげた例として)このような関数は評価できません。

(defn countup [x]
  {:pre [(>= x 0)]
   :post [(>= % 0)]}
  (if (< x 1000000)
    (recur (inc x))
    x))

現在、Clojure1.3を使用しています。

4

4 に答える 4

4

https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L3905の実装をdefn見ると、関数の本体が変更されて末尾が変更されていることがわかります。呼び出しは末尾位置からプッシュされます。これを回避する1つの方法は、補助関数を使用してrecur'd関数を呼び出し、代わりに事後条件を設定することです。

(defn- countup* [x]
  (if (< x 1000000)
    (recur (inc x))
    x))

(defn countup [x]
  {:pre [(>= x 0)]
   :post [(>= % 0)]}
  (countup* x))

(countup 999999)
;=> 1000000

(countup -1)
; Assert failed: (>= x 0)
于 2010-11-15T15:07:15.607 に答える
3

「recur」は、「そのパラメーターを使用してブロックの先頭に移動する」のようなものです。

コードはスタックに保存されないため、その後にコードを配置することはできません。そのため、コードがどこから来たのか(および実行後にどのようなチェックを実行する必要があるのか​​)はわかりません。

たとえば、(loop [] (recur))スタックを消費せずに永久ループします。

あなたの例では:post、x == 1000000のときに、一度実行されることを期待しています。

于 2010-11-15T11:32:42.277 に答える
1

これは完全に行うことができ、多くの場合、事前/事後条件を取得する機能は、loop/recur特殊形式を使用しないことで失われる速度に見合う価値があります。

recur特殊形式は、実際には関数呼び出しではないため、yaに対しては実行されません。すべてのコードの実行後に事前条件と事後条件で境界チェックを行う独自のラッパー関数を作成するか、組み込みtrampoline関数を使用して少しの労力を節約し、反復ごとに条件をチェックすることができます(あなたはあれが欲しい)

スタックを吹き飛ばすことなく、これを再帰関数に変えることができます。

(defn countup [x]
              {:pre [(>= x 0)]
              :post [(or (ifn? %) (>= % 0))]}
              (if (< x 1000000)
                  #(countup (inc x))
                  x))

(trampoline (countup 0))
1000000

これにより、post条件が変更され、中間のケース(次に実行する関数が返される)が無視され、最終結果のみが検証されます。

トランポリンの背後にある考え方は、関数の各反復が直接呼び出すのではなく、次の関数への呼び出しを返すようにすることで、スタックを壊さないようにすることです。このようにして、1つの2つのスタックフレームが使用されます(1つはトランポリン用、もう1つは現在のステップ用)

于 2010-11-16T00:49:42.087 に答える
1

IMHOの最も簡単な方法(トランポリンや補助機能の使用なし)は次のようになります。

(defn countup [x]
  {:pre [(>= x 0)]
  :post [(>= % 0)]}
  (loop [x x]
        (if (< x 1000000)
            (recur (inc x))
          x)))

(countup 999999)
1000000

(countup -1)
; Evaluation aborted.

したがって、補助ループを挿入するだけです(関数シグネチャと同じ)。

于 2011-11-14T11:46:18.987 に答える