私は、すべての騒ぎが何であるかを見たいと思っていたclojureの新参者です。簡単なコードを書くのが一番いいと思い、フィボナッチ関数から始めようと思いました。
私の最初の努力は:
(defn fib [x, n]
(if (< (count x) n)
(fib (conj x (+ (last x) (nth x (- (count x) 2)))) n)
x))
これを使用するには、関数を呼び出すときにxに[01]をシードする必要があります。私の質問は、それを別の関数でラップせずに、返す要素の数だけをとる単一の関数を書くことは可能ですか?
いくつか読んでみると、同じ機能を実現するためのより良い方法がいくつか見つかりました。
(defn fib2 [n]
(loop [ x [0 1]]
(if (< (count x) n)
(recur (conj x (+ (last x) (nth x (- (count x) 2)))))
x)))
そして
(defn fib3 [n]
(take n
(map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))))
とにかく、何よりも運動のために、純粋に再帰的なフィボナッチ関数のより良いバージョンで誰かが私を助けることができますか?それとも、より良い/異なる機能を共有しますか?
最初の質問に答えるには:
(defn fib
([n]
(fib [0 1] n))
([x, n]
(if (< (count x) n)
(fib (conj x (+ (last x) (nth x (- (count x) 2)))) n)
x)))
このタイプの関数定義は、マルチアリティ関数定義と呼ばれます。詳細については、こちらをご覧ください: http://clojure.org/functional_programming
より良いFib関数に関しては、あなたのfib3関数は非常に素晴らしく、関数型プログラミングの概念をたくさん示していると思います。
これは速くてかっこいいです:
(def fib (lazy-cat [0 1] (map + fib (rest fib))))
差出人: http://squirrel.pl/blog/2010/07/26/corecursion-in-clojure/
Clojureでは、再帰を避け、代わりにloop
およびrecur
特殊形式を使用することをお勧めします。これにより、再帰的なプロセスのように見えるものが反復的なプロセスに変わり、スタックオーバーフローが回避され、パフォーマンスが向上します。
この手法でフィボナッチ数列を実装する方法の例を次に示します。
(defn fib [n]
(loop [fib-nums [0 1]]
(if (>= (count fib-nums) n)
(subvec fib-nums 0 n)
(let [[n1 n2] (reverse fib-nums)]
(recur (conj fib-nums (+ n1 n2)))))))
loop
コンストラクトは、初期値を提供する一連のバインディングと、1つ以上のボディフォームを取ります。これらの本体形式のいずれかで、recur
を呼び出すと、指定された引数を使用してループが再帰的に呼び出されます。
ツグミ演算子を使用して、#3を少しクリーンアップできます(誰に尋ねるかによって異なります。このスタイルが好きな人もいれば、嫌いな人もいます。オプションだと指摘しているだけです)。
(defn fib [n]
(->> [0 1]
(iterate (fn [[a b]] [b (+ a b)]))
(map first)
(take n)))
そうは言っても、おそらく(take n)
を抽出し、fib
関数を怠惰な無限シーケンスにするだけです。
(def fib
(->> [0 1]
(iterate (fn [[a b]] [b (+ a b)]))
(map first)))
;;usage
(take 10 fib)
;;output (0 1 1 2 3 5 8 13 21 34)
(nth fib 9)
;; output 34
適切な再帰的定義は次のとおりです。
(def fib
(memoize
(fn [x]
(if (< x 2) 1
(+ (fib (dec (dec x))) (fib (dec x)))))))
これにより、特定の用語が返されます。これを拡張して最初のn項を返すのは簡単です。
(take n (map fib (iterate inc 0)))
後発者向け。受け入れられた答えは、これの少し複雑な表現です:
(defn fib
([n]
(fib [0 1] n))
([x, n]
(if (< (count x) n)
(recur (conj x (apply + (take-last 2 x))) n)
x)))
それだけの価値があるので、ここ数年、これが私の解決策です 4クロージャー問題#26:フィボナッチ数列
_(fn [x]
(loop [i '(1 1)]
(if (= x (count i))
(reverse i)
(recur
(conj i (apply + (take 2 i)))))))
_
私は、これが最適または最も慣用的なアプローチであるとは決して思いません。私が4Clojureで演習を行って、 Rosetta Code のコード例を検討している理由は、 clojure を学ぶためです。
ちなみに、フィボナッチ数列に正式に0が含まれていることはよく知っています...この例はloop [i '(1 0)]
...である必要がありますが、仕様と一致しません。また、この演習にどのようにラベルを付けたかにかかわらず、ユニットテストに合格することもありません。これは、4Clojure演習の要件に準拠するために、匿名の再帰関数として記述されています...指定された式内の「空白を埋める」必要があります。 (匿名再帰の概念全体が少しマインドベンダーであることがわかりました。_(loop ... (recur ...
_特殊形式は 末尾再帰 ...に制約されていることがわかりますが、それでもまだです私にとって奇妙な構文)。
元の投稿のfib3に関する@ [Arthur Ulfeldt]のコメントも、検討中です。これまで、Clojureのiterate
を使用したのは1回だけです。
これは、n番目のフィボナッチ数を計算するために私が思いついた最短の再帰関数です。
(defn fib-nth [n] (if (< n 2)
n
(+ (fib-nth (- n 1)) (fib-nth (- n 2)))))
ただし、Clojureはループ/再帰でテールエンドの最適化を行うため、ループ/再帰を使用したソリューションは、「n」の最初のいくつかの値を除くすべての値で高速になるはずです。
これが私のアプローチです
(defn fibonacci-seq [n]
(cond
(= n 0) 0
(= n 1) 1
:else (+ (fibonacci-seq (- n 1)) (fibonacci-seq (- n 2)))
)
)