3

在 lisp 中,我可以将参数传递给函数并在函数内对其进行更改。(AKA 破坏性功能)。但是,在 Clojure 中,我在某处读到不允许更改同一函数中的给定参数。例如:

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

这是一个普通函数,它的输出是两个相同列表的相加。但是,我想要这样的东西:

(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),这是最佳的 - 问题中的版本实际上是 O(n^2),因为nth它本身就是对列表的 O(n) 操作。
  • 很好很简洁:-)
于 2012-08-01T03:14:00.157 回答
6

Clojure 提供了几种在这种情况下可以很好地工作的可变类型,例如,您可以将 an 传递atom给函数并让它在该原子中设置值。

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

然后在你调用它之后,你可以使用@/deref 编辑从原子中获取值:如果效率是目标,那么使用瞬态集合可能会有所帮助

于 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)

编辑:

在风格上,您可以使用map添加两个列表,而无需使用第 n 个函数来随机访问每个列表中的每个索引:

(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 回答