基本Lisp函数 - 偶数减去和的总和

时间:2015-02-12 09:28:42

标签: sum lisp common-lisp

我尝试编写一个函数,该函数将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)))
  )
)

3 个答案:

答案 0 :(得分:1)

关于代码算法,它失败了,因为数学是如何完成的。 现在的代码如何,这个用列表(列表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

答案 1 :(得分:1)

你快到了。以下是代码的编辑版本:

(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的内容将是多余的。这不适用于虚线列表。

答案 2 :(得分:0)

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

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

remove-if-not采用列表和谓词函数。它在列表的每个元素上运行谓词,并创建一个仅包含谓词未返回的元素的新列表。

apply接受一个函数和一个列表,并使用参数作为列表元素来调用该函数。因此(apply #'+ '(1 2 3 4))相当于(+ 1 2 3 4)

普通的lisp具有很好的功能,可以使用列表(以及许多其他数据类型)检查它们,并且您的代码可以更加清晰。

也不要在常见的lisp中使用camel-case(或任何基于案例的命名),并且符号不区分大小写。符号HeLloThErEhellothere以及helloThere相同的符号。这就是为什么你会看到名字中使用的连字符。