2011-08-13 8 views
6

私はシーケンスをエクササイズを4clojure.comに数えて解決しようとしています。エクササイズは、count関数を使用せずにコレクション内の要素の数を数えることです。Clojureのif条件でrecurを呼び出すにはどうすればよいですか?

私は再帰によって、restの使い方でこれを行うことができると思いました。私が得るものが空でないなら、私は1 + recur on the sequence rest returnedを返す。問題は、最終的にrecurと呼んでいますが、最終的には

となります。

(fn [coll] (let [tail (rest coll)] 
      (if (empty tail) 
       1 
       (+ 1 (recur tail))))) 

私に何かが不足していますか?

答えて

8

recurへの呼び出しではなく、最後のステートメントが追加されているため、動作しません。それがifの中にあるという事実は、それとは何の関係もありません。 (fn [coll] (let [tail (rest coll)] (+ 1 (recur tail))))も機能しません。

このような関数をテール再帰的なものにする通常の方法は、関数を2番目の引数にすることです。加算する値のアキュムレータを保持し、次のように再帰します:(recur tail (+ acc 1))recurの結果に1を加算しようとしています。

一般的なルール:recurの結果に何かをしている場合(たとえば、1を追加するなど)、末尾にはできないため、機能しません。

6

最終的な表現が(+ 1 (recur tail))であることは、テールコール最適化が最適化可能ではないことを指摘しています(単語ですか?)。問題は、関数の結果を評価するために束の上に(+ 1 ...)式の束を保持する必要があることです。テールコールの最適化は、呼び出される関数の値がの場合にのみ発生し、コールを行う関数の戻り値を知る必要があります。

あなたが書こうとしているのは、ほとんどがです。この場合、関数はコレクションの残りの部分とこれまでの数を渡す必要があります。

(fn [count coll] (let [tail (rest coll)] 
    (if (empty tail) 
     count 
     (recur (+ 1 count) tail))))) 
関連する問題