17

尾の再帰を使わずに無名関数で再帰を行うにはどうすればよいですか?例えば尾の再帰を使わないで匿名fnで再帰を行う方法

(Vanderhart 2010、P 38から):

(defn power 
    [number exponent] 
    (if (zero? exponent) 
    1 
    (* number (power number (- exponent 1))))) 

のは、私は無名関数としてこれをやってみたかったとしましょう。何らかの理由で、私は尾の再帰を使用したくありませんでした。どうすればいい?たとえば:

((fn [number exponent] ......))))) 5 3) 
125 

私はこのためにループを使用でき、またはのみで使用するループはを再発することができますか?

答えて

41

fn特別なフォームはあなたの再帰のために内部的に使用することができますoption to provide a nameを提供します。

(doc fn) 
;=> (fn name? [params*] exprs*) 

したがって、名前として「power」を追加して例を完成させてください。

(fn power [n e] 
    (if (zero? e) 
    1 
    (* n (power n (dec e))))) 

再帰は末尾位置で起こったとしても、現在のスタックフレームを交換するように最適化されることはありません。 Clojureは、あなたにloop/recurtrampolineと明示するように強制します。

+1

ありがとう、ジェレミー、私は名前のオプションについて知りませんでした。私は[4clojure](http://www.4clojure.com/)の質問に取り組んでおり、彼らはdefnを許可していません。テールの再帰は明らかに優れていますが、実行する前に歩きたいです。 –

16

私は、Clojureには、無名関数を「命名」するための構文サポートがあることを知っています。他の答えも指摘しています。しかし、私はプログラミング言語の特別な構文の存在に依存せず、一次手続き(lambdas)を使ってどの言語でも動作する、問題を解決する第一原理アプローチを示したい。

原則として、再帰関数呼び出しを行いたい場合は、関数の名前を参照する必要があります。つまり、「匿名」(つまり、名前なし関数)は再帰を実行するために使用できません。 Y-Combinatorを使用します。 HereのClojureでの動作の説明。

例でどのように使用されているかをお見せしましょう。まず、可変数の引数を持つ関数のために働くY-Combinator

(defn Y [f] 
    ((fn [x] (x x)) 
    (fn [x] 
     (f (fn [& args] 
       (apply (x x) args)))))) 

さて、質問に定義されているようpower手順を実装匿名機能。明らかに、それは名前を持っていない、powerは、最も外側の関数のパラメータのみです:

最後に
(fn [power] 
     (fn [number exponent] 
      (if (zero? exponent) 
       1 
       (* number (power number (- exponent 1)))))) 

、ここでパラメータとして渡し、匿名powerプロシージャにY-Combinatorを適用する方法ですnumber=5exponent=3(それはありません末尾再帰的BTW):

((Y 
    (fn [power] 
     (fn [number exponent] 
      (if (zero? exponent) 
       1 
       (* number (power number (- exponent 1))))))) 
5 3) 

> 125 
+3

GraciasÓscar。 Y-Combinatorは非常に面白そうです - 私はそれをもっと勉強します! –

+0

Yコンビネータを理解するもう一つの良い情報源は[The Little Schemer](https://mitpress.mit.edu/books/little-schemer)です。 – Mars

3

fnは、関数を再帰的に呼び出すために使用できるoptional name argumentをとります。

など。:

user> ((fn fact [x] 
      (if (= x 0) 
       1 
       (* x (fact (dec x))))) 
     5) 
;; ==> 120 
+0

グレッグさん、ありがとうございます。 –

2

はいあなたはこのためloopを使用することができます。 recur両方loop sおよびfn

user> (loop [result 5 x 1] (if (= x 3) result (recur (* result 5) (inc x)))) 
125 

で作品idomatic Clojureのソリューションは、次のようになります。

user> (reduce * (take 3 (repeat 5))) 
125 

またはMath.powを(使用);-)

user> (java.lang.Math/pow 5 3) 
125.0 
+0

しかし、質問は「尾の再帰をしないで」です:-) –

0

loopすることができますあなたはそれを使ってそれを行うことができます。

+0

質問は「尾の再帰をしないで」です:-) –

+0

:-)私は知っていますが、再帰は再帰を行わず、コンパイラはループをフィックスアップします(尾部再帰の状況であっても)。だからそうではない。 – Bill