基本 Lisp 函数 - 偶数和减去奇数和

Basic Lisp function - sum of even minus sum of odd

我正在尝试编写一个函数,它将 List 作为参数并计算 sum of even numbers 减去 sum of odd numbers。 这是我的实现,但我不知道为什么它没有按预期工作,你能给我任何关于错误的提示吗?

(defun sumEvenOdd (R)
  (cond
    ((NULL R) 0)
    ((and (ATOM (CAR R)) (= (mod (CAR R) 2) 0))
              (+ (CAR R) (sumEvenOdd (CDR R))))
    ((and (ATOM (CAR R)) (not (= (mod (CAR R) 2) 0)))
              (- (CAR R) (sumEvenOdd (CDR R)) ))
    ((LISTP (CAR R)) (sum (sumEvenOdd  (CAR R)) (sumEvenOdd  (CDR R))))
    (T  (sumEvenOdd  (CDR R)))
  )
)

关于代码算法,它失败了,因为数学是如何完成的。 现在的代码是这样的,这个对列表 (list 1 2 3 4 5) 进行的评估是 (- 1 (+ 2 (- 3 (+ 4 (- 5 0))))) 等于 5。 我们期望的是 (2+4)-(1+3+5) 等于 -3。怎么了? 基本上数学中的加法运算是可交换的,而减法运算则不是。 1+5和5+1是一样的。 1-5 和 5-1 不是。 这反映了最后一个操作的代码,其中 5 被减去 0。

最简单的解决方案是调整操作顺序,切换参数。

(defun sumEvenOdd (R)
  (cond
    ((NULL R) 0)
    ((and (ATOM (CAR R)) (= (mod (CAR R) 2) 0))
              (+ (sumEvenOdd (CDR R)) (CAR R)))
    ((and (ATOM (CAR R)) (not (= (mod (CAR R) 2) 0)))
              (- (sumEvenOdd (CDR R)) (CAR R)))
  )
)

这样计算将是:(- (+ (- (+ (- 0 1) 2) 3) 4) 5) 等于 -3。

PS:您可以在此处检查和测试代码:http://goo.gl/1cEA5i

你快到了。这是您的代码的编辑版本:

(defun sumEvenOdd (R)
  (cond
    ((NULL R) 0)
    ((and (ATOM (CAR R)) 
          (= (mod (CAR R) 2) 0))
     (+ (sumEvenOdd (CDR R)) (CAR R))) ; switched places for consistency
    ((and (ATOM (CAR R)) (not (= (mod (CAR R) 2) 0)))
     (- (sumEvenOdd (CDR R)) (CAR R))) ; operands needed to be switched
    ((LISTP (CAR R)) (+ (sumEvenOdd  (CAR R)) ; what is sum? replaced with +
                        (sumEvenOdd  (CDR R))))
    (T  (sumEvenOdd  (CDR R)))))

这是一个使用 reduce 的解决方案:

(defun sum-even-odd (list)
  (reduce (lambda (acc e)
            (cond ((consp e) (+ acc (sum-even-odd e)))
                  ((not (numberp e)) acc) ; perhaps not needed
                  ((oddp e) (- acc e))
                  (t (+ acc e))))
          list
          :initial-value 0))

 (sum-even-odd '(1 2 (3 4 (5 6) 7) 8 9 10)) ; ==> 5

如果您确定该列表只有数字或其他带有数字的列表,则检查不是 conspnumberp 的内容将是多余的。这不适用于点列表。

有关于如何修复代码的答案,但让我们看一下不同的实现。 您没有指定您的函数需要在树上工作,所以这是一个简单的数字列表。

(defun sum-even-odd (r)
  (- (apply #'+ (remove-if-not #'evenp r))
     (apply #'+ (remove-if-not #'oddp r))))

remove-if-not 采用列表和谓词函数。它 运行 列表中每个元素的谓词,并创建一个新列表,只包含谓词没有 return nil 的元素。

apply 接受一个函数和一个列表,并调用函数,参数是列表的元素。所以 (apply #'+ '(1 2 3 4)) 等同于 (+ 1 2 3 4)

Common lisp 具有处理列表(和许多其他数据类型)的良好功能,检查它们,您的代码最终会变得更加清晰。

也不要在常见的 lisp 中使用驼峰式大小写(或任何基于大小写的命名),并且符号不区分大小写。符号HeLloThErEhellotherehelloThere相同的符号。这就是您会在名称中看到连字符的原因。