1

私はclojureで再帰に頭を悩ませようとしています。次のコードでスタック オーバーフロー エラーが発生します。誰か問題を特定できますか?

(これは非効率的ですが、厳密には学習目的です)

user=> (defn addall
         ([] 0)
         ([& x]
           (if (empty? x) 0)
           (+ (first x) (addall (rest x)))))
user/addall
user=> (addall 1)
StackOverflowError   clojure.lang.ArraySeq.next (ArraySeq.java:78)
4

2 に答える 2

7

括弧が間違っているようです。フォームifが必要elseです。次のような意味だったと思います。

(defn addall
  ([] 0)
  ([& x]
     (if (empty? x) 
         0   ;;; <=== no ')' after 0
         (+ (first x) (addall (rest x))))))  ;;; <== extra ')' here

しかし、それが修正されたとしても、コードはまだ間違っています。複数の引数で呼び出されると想定しています -- (addall 1 2 3)-- しかし、それ自体にリストを渡すことで再帰します -- (addall [2 3])。これにより、進行しないループに陥ってしまいます。これは、次のprintステートメントを追加することで確認できます。

(defn addall
  ([] 0)
  ([& x]
     (print (str "new x: " x "\n"))
     (if (empty? x) 
         0   ;;; <=== no ')' after 0
         (+ (first x) (addall (rest x))))))

これにより、実際にコンピューターでセグメンテーション違反が発生しました。

また、2 つの基本ケースがあります。代わりにこれをお勧めします:

(defn addall
  [xs]
  (if (empty? xs) 
      0
      (+ (first xs) 
         (addall (rest xs)))))

ベクトルで呼び出すには:

(addall [1 2 3])

または、可変引数関数を使用する場合は、次も必要ですapply

(defn addall
  [& x]
  (print (str "new x: " x "\n"))
  (if (empty? x) 
      0
      (+ (first x) 
         (apply addall (rest x))))) ;;; <=== apply addall

とはいえ、Clojure には末尾呼び出しの最適化がないことに注意してください。つまり、このコードは中規模の入力では失敗します。Clojure ではloop/recur、組み込みのシーケンス処理関数の使用を推奨しています。

于 2013-01-29T17:39:16.643 に答える
0

これがあなたが望むものだと思います:

(defn addall ([x] (if (empty? x) 0 (+ (first x) (addall (rest x))))))

Matt Fenwick が述べたように、loop/recur を使用する必要があります。より慣用的なアプローチは、reduce を使用することです。

(reduce + [1 2 3 4 5])

clojure には多くの素晴らしいツールが組み込まれており、多くの場合、ループ/再帰や明示的な再帰などは必要ありません。

于 2013-01-29T17:45:41.720 に答える