3

Lisp では、引数を関数に渡して、関数内で変更することができます。(AKA 破壊関数)。ただし、Clojure では、同じ関数内で指定された引数を変更することは許可されていないことをどこかで読みました。例えば:

(defn add-two-lists [list1 list2]
  (for [n (range (count list1))]
    (+ (nth list1 n) (nth list2 n))))

これは通常の関数であり、その出力は 2 つの同一のリストの加算です。ただし、次のようなものが必要です。

(defn add-two-lists [list1 list2 added_list]
  (set! added_list 
       (for [n (range (count list1))]  
          (+ (nth list1 n) (nth list2 n)))))

おそらく、私の使用法set!が間違っているか誤用されているため、まだエラーが発生します。Clojureで引数を破壊的に変更するエレガントな方法はありますか?

4

4 に答える 4

12

Clojure では破壊的な変更は推奨されていません。破壊的な更新に頼らずにコードを記述する方法を見つけることをお勧めします。

Clojurey ソリューションを提供するという精神で、add-two-lists関数を次のように記述します。

(defn add-two-lists [list1 list2]
  (map + list1 list2))

これにはいくつかの利点があります。

  • それは純粋に機能的です
  • 怠け者なので、無限長のリストを追加することもできます (破壊的に更新された引数を使って試してみてください!)
  • パフォーマンスは最適な O(n) です。問題のバージョンは、nthそれ自体がリストに対する O(n) 操作であるため、実際には O(n^2) です。
  • 素晴らしく簡潔です:-)
于 2012-08-01T03:14:00.157 に答える
6

Clojure は、この状況でうまく機能するいくつかの変更可能な型を提供します。たとえば、関数に を渡してatom、そのアトムに値を設定させることができます。

(defn add-two-lists [list1 list2 added_list]
  (reset! added_list 
    (for [n (range (count list1))]  
       (+ (nth list1 n) (nth list2 n)))))

これを呼び出した後、@/ deref edit を使用してアトムから値を取得します。効率が目標である場合は、一時的なコレクションを使用すると役立つ場合があります

于 2012-08-01T02:58:41.057 に答える
2

with-local-varsマクロを使用すると、 var-setで変更できるスレッドローカルにバインドされた変数を作成できます。また、 var-getを使用して var の値にアクセスする必要があります。これは @ に短縮できます。

(defn add-two-lists [list1 list2 added-list]
  (var-set added-list 
           (for [n (range (count list1))]  
             (+ (nth list1 n) (nth list2 n)))))

(with-local-vars [my-list nil]
  (add-two-lists '(1 2 3) '(3 4 5) my-list)
  @my-list)

編集:

スタイル上の注意として、各リストの各インデックスにランダム アクセスする n 番目の関数を使用せずに、mapを使用して 2 つのリストを追加できます。

(defn add-two-lists [list1 list2 added-list]
  (var-set added-list (map + list1 list2)))
于 2012-08-01T05:41:02.397 に答える
1

セットのclojureドキュメントから

 Note - you cannot assign to function params or local bindings. Only Java fields, Vars, Refs and Agents are mutable in Clojure.

通常、関数型言語を選択するコースでは、for ループと代入を使用しないことをお勧めします。代わりに、再帰と関数の構成を優先する必要があります。

したがって、リストの各要素に 2 を追加したい場合、命令型言語では for ループを実行するだけですが、関数型言語では再帰を使用します。

user=> (def add2
  (fn [mylist]
    (if 
      (empty? mylist) 
        nil
        (cons (+ (first mylist) 2) (add2 (rest mylist))))))

user=> (add2 (list 1 2 3))
(3 4 5)
于 2012-08-01T02:24:15.517 に答える