まえがき
最近、Clojure言語の学習を始めました。 彼は、jvm上で動作するLisp方言なので、私に興味を持ちました。 私自身はjavaでプログラムを作成しており、lispはその括弧で驚いた。 そして、このような混合物は興味深いものです。 そして、私は関数型プログラミングにも精通したかったです。 しかし、その後、問題が発生しました:clojureを学ぶ方法? それに何かを書く必要があります...しかし、何ですか? そして、言語を学習する際にいくつかの簡単な数学的問題を解決することは非常に便利であるという結論に達しました。構文に慣れ、機能を少し知ってください。 Project Eulerはこれに非常に適しています。 ここでは、clojureでの作業を開始し、その問題をいくつか解決する方法を示します。 私は関数型プログラミングとLispが初めてなので、関数型関数とLispの観点からより近く、より美しいソリューションを見ることができてとてもうれしいです。
準備する
最初に、もちろん、作業する場所があるようにすべてを構成する必要があります。
Clojureはjarアーカイブとして配布されます 。接続方法はここで説明されています 。
私の意見では、良いチュートリアルはこちらにあります 。
clojureには、emacsを使用します 。
それは基本的にそれです。 これで、このオイラーを解くことができます。
タスク1
タスク
1から
アルゴリズム
1. 1から
2.必要な数字だけを残して、フィルターします。
3.すべての数値を合計します。
コード
(削減+
(フィルター# ( または (ゼロ? ( rem % 3 ) ))
(ゼロ? ( rem % 5 ) ) )
(範囲1000 ) ) )
(範囲1000 ) -0から999までの数字のシーケンス
( #or ( zero? ( rem % 3 ) ) ( zero? ( rem % 5 ) ) )は、argument%が3または5で除算されているかどうかをチェックする無名関数です。 、%2、%3 ...
( filter ... ) -匿名関数を使用して投稿をフィルタリングします。
( reduce + ... )は、この場合、シーケンスの数を合計する関数です。
一般的に、 reduceの説明を個別に見る価値があります。 彼女はよく使われます。
また、関数のドキュメントは、repで(doc functionName)を使用して直接表示できます。
タスク2
タスク
400万未満のすべてのフィボナッチ数の合計を求めます。
アルゴリズム
1.フィボナッチ数列fib-seq(無限、遅延初期化のおかげ)を構築します。
2. 400万未満のフィボナッチ数の2番目のシーケンスを構築します。
3.偶数のもののみを選択します。
3.要約します。
コード
( def fib-seq
( lazy-cat [ 1 2 ] ( map + fib-seq ( rest fib-seq ) ) ) ))
(削減+
(フィルターさえしますか?
( take-while# ( < % 4000000 ) fib-seq ) )
私にとってここで最も難しいことは、このバージョンでfib-seqがどのように設定されているかを理解することでした。
これは、ベクトル[1 2]を「接着」することにより、また、fib-seqを使用して計算される方法で取得されます。 このようなもの:
fib-seq =(1 2)+(3 5 8 13 ...) = fib-seq:(1 2 3 5 ...) + (残りのfib-seq):(2 3 5 8 ...)
そして、要素が4,000,000未満である間に最初から要素を取得する間、take-whileを使用する方が簡単です。 ここでは、 マップ機能に特別な注意を払う価値があります 。
中間タスク
タスク
プライムのシーケンス(無限)を構築します。
オイラーの複数のタスクで役立ちます。
アルゴリズム
ここからアイデアと実装自体を取ります 。
それはエラトステネスのふるいに基づいています。 Sieve-これは、数値がキーとして機能するマップであり、値は数値の約数になります。 すぐに奇数でのみ動作します。
各ステップで、次の候補pを取得し、彼がふるいに入っているかどうかを確認します。 そうでなければ、それは単純な、さもなければ複合を意味します。 次に、ふるいを更新します。 pは素数であるため、pで割り切れる次の数値をふるいに追加しますが、これはふるいにはありません。 pが合成の場合、ふるいからそれを取得し、その除数を取得し、除数で除算され、ふるいにない次の数を追加します。
コード
実装するために、いくつかの関数を作成します。
( defn enqueue [ふるい候補ステップ]
( let [ m ( +候補ステップ) ]
( if (ふるいm )
(ふるいmを繰り返します)
( associeve m step ) ) ) )
この関数は、候補の次の番号をふるいに追加します。この番号は、ステップで分割され、まだふるいに含まれていません。
( defn next-sieve [ふるい候補]
( if- let [ステップ(ふるい候補)) ]
( - > ( dissocふるい候補)
(エンキュー候補ステップ) )
(エンキューふるい候補( +候補候補) ) ) )
この関数は、単純な候補または複合候補によって調べられ、これに応じて、ふるいから取り出されるかどうかが決まります。 そして、彼は次のものを追加します。それはまだふるいにありません。
( defn next-primes [ふるい候補]
( if (ふるい候補)
( next-primes ( next-sieveieve候補) ( + 2候補) )
( 短所候補
( lazy-seq ( next-primes ( next-sieveieve候補) ( + 2候補) ) ) ) ) )
この関数は主なものであり、素数の数を返します。 彼女はさらに、私が正しく理解していれば、再帰的です。 その入り口には、ふるいと候補者が与えられます。 彼女は候補の単純さをチェックし、単純な場合は8の出力に追加し、変更されたふるいと次の候補で再帰的に呼び出します。 ここに戻るのは、まさに遅延投稿(lazy-seq)であることに注意してください。
そして今、最後の機能は素数のグローバルシーケンスを作成することです。
( def primes ( concat [ 2 ] ( next-primes { } 3 ) ) ))
タスク3
タスク
600851475143の最大素数を見つけます。
アルゴリズム
2つの機能があります。
数の除数を「取り除く」ために、数を除数の最大次数で除算する1つの補助。
2番目の主要なものは、すべての素数を交互に実行し、数をそれらに分割しようとします。 そして、それに応じて、その素数、その後私たちは1を得て、望ましいものになります。
コード
( defn split-all [ x除数]
( if ( zero? ( rem x divisor ) ))
(再帰( / x除数)除数)
x ) )
( defn max-prime-divisor [ x ind ]
( let [ m ( split-all x ( nth primes ind ) ) ]
( if ( == m 1 )
( n番目の素数ind )
(再帰m ( inc ind ) ) ) ) ))
ここでは、素数を使用しました。
今のところすべてです。 私は本当にコメント、言語自体と関数型プログラミングの観点から真の道についての指示を見たいです。