1

私の理解が正しければ、Clojure は (他の Lisp のように) リストだけでなく、ベクトルとセットも返すことができます。

私が本当に得られないのは、返されるコレクションが常にあるとは限らない理由です。

たとえば、次のコードを使用するとします。

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

128 64 32 16 8 4 2 を出力します。しかし、それは単にprintlnが呼び出され、printlnが何かを出力するという副作用 (?) を持っているためです。

だから私はこれに置き換えてみました(printlnを削除します):

(loop [x 128]
  (when (> x 1)
    x
    (recur (/ x 2))))

そして、次のようなコレクション(おそらくリスト)を取得することを期待していました:

(128 64 32 16 8 4 2)

しかし、代わりにnilを取得しています。

コレクションを作成するものとそうでないものを決定するものと、あるものから別のものに切り替える方法がわかりません。また、Clojure が何らかの形で「関数型」のプログラミング方法を奨励していることがわかりましたが、ほぼ常にコレクションを返すことになっているのではないでしょうか?

明らかにコレクションを返さない関数が非常に多いのはなぜですか? そして、これらの戻り値のコレクションを作成する慣用的な方法は何でしょうか?

たとえば、最初にコレクションを構築してから、結果のリスト/ベクトル以外の慣用的な方法で反復 (?) することによって、上記の問題をどのように解決しますか?

まず、ループを変換してnil以外のものを生成する方法がわからないため、次のことを試しました。

(reduce println '(1 2 3))

しかし、期待していた「1 2 3nil」ではなく、 「1 2nil 3nil」と表示されます。

これは基本的なことだと思いますが、始めたばかりで、明らかに基本的なことが欠けています。

(PS: 適切に再タグ付けしてください。ここでどの用語を使用すればよいかわかりません)

4

2 に答える 2

3

他のいくつかのコメントは、いつが実際にはそうでないかのように機能しないことを指摘しています-しかし、それは本当にあなたの質問ではないと思います。

ループ形式と繰り返し形式は、他の言語のforループのように反復を作成します。この場合、あなたが印刷しているとき、それは確かに副作用のためだけです。シーケンスを返したい場合は、シーケンスを作成する必要があります。

(loop [x 128                                                                                                        
       acc []]                                                                                                      
  (if (< x 1)                                                                                                       
    acc                                                                                                             
    (recur (/ x 2)                                                                                                  
           (cons x acc))))                                                                                          

=> (1 2 4 8 16 32 64 128)

この場合、printfを呼び出していた場所を、繰り返しと、そのアキュムレータの前にxを追加するフォームに置き換えまし。xが1未満の場合、コードはアキュムレータを返します。つまり、シーケンスを返します。ベクトルの前ではなく最後に追加する場合は、conjに変更します。

(loop [x 128                                                                                                    
       acc []]                                                                                                  
  (if (< x 1)                                                                                                   
    acc                                                                                                         
    (recur (/ x 2)                                                                                              
           (conj acc x))))                                                                                      

=> [128 64 32 16 8 4 2 1] 

それがあなたの式の結果だったのであなたはnilを得ていました-それは最終的なprintlnが返したものです。

これはすべて意味がありますか?

reduceはまったく同じものではありません。これは、バイナリ関数(2つの引数を取る関数)をシーケンスの初期値と最初の要素、またはシーケンスの最初の2つの要素に繰り返し適用することによってリストを減らすために使用されます。最初の反復のシーケンス、次に後続の反復には、前の反復の結果とシーケンスの次の値が渡されます。いくつかの例が役立つかもしれません:

(reduce + [1 2 3 4])                                                                                            

10  

これにより、以下が実行されます。

(+ 1 2) => 3
(+ 3 3) => 6
(+ 6 4) => 10

Reduceは、実行されているバイナリ関数からの最終結果が何であれ、結果になります。この場合、シーケンス内の数値をすべての要素の合計に減らします。

初期値を指定することもできます。

(reduce + 5 [1 2 3 4])                                                                                            

15  

これは以下を実行します:

(+ 5 1)  => 6
(+ 6 2)  => 8
(+ 8 3)  => 11
(+ 11 4) => 15

HTH、

カイル

于 2012-04-28T18:53:44.087 に答える
2

コレクションに対する一般化された抽象化は、Clojure ではシーケンスと呼ばれ、多くのデータ構造がこの抽象化を実装しているため、どのデータ構造が関数に渡されるかを考えずに、これらのデータ構造ですべてのシーケンス関連操作を使用できます。

サンプルコードに関する限り - ループ、recur は再帰のためのものです - 基本的に、再帰を使用して解決したい問題はすべて再帰を使用して解決できます。古典的な例は階乗です。ループを使用してベクトル/リストを作成できますが、アキュムレータをベクトルとして使用し、アイテムをベクトルに追加し続け、再帰が存在する状態で累積ベクトルを返しますが、以下に示すようにreductionsand関数を使用して作成できます。take-whileこれは遅延シーケンスを返します。

元:

(take-while #(> % 1) (reductions (fn [s _] (/ s 2)) 128 (range)))
于 2012-04-29T05:40:39.410 に答える