Common lisp 3的倍数之和&;5使用LISP

Common lisp 3的倍数之和&;5使用LISP,common-lisp,Common Lisp,我试图得到给定数字下3和5的倍数之和。但是代码总是返回到0。有什么问题吗 (defun modsum2 (n) (let ((summ 0)) (if (>= n 3) (if (or (zerop (mod n 3)) (zerop (mod n 5))) (progn (setq summ (+ n summ)) (modsum2 (1- n))) (modsum2 (1- n)

我试图得到给定数字下3和5的倍数之和。但是代码总是返回到0。有什么问题吗

(defun modsum2 (n)
  (let ((summ 0))
    (if (>= n 3)
        (if (or (zerop (mod n 3)) (zerop (mod n 5)))
           (progn (setq summ (+ n summ))
                  (modsum2 (1- n)))
           (modsum2 (1- n)))
        (print summ))))
对,现在你把它缩进了。让我们追踪一下:

(defun modsum2 (n)
  (let ((summ 0))
    (if (>= n 3)
        (if (or (zerop (mod n 3)) (zerop (mod n 5)))
           (progn (setq summ (+ n summ))
                  (modsum2 (1- n)))
           (modsum2 (1- n)))
        (print summ))))
n
的参数为
2
时,可以看到打印
0
。由于
print
表单也是最后一个表单,因此函数返回其值<代码>(打印0)返回
0
。因为返回值在您使用的函数中,所以每次递归调用都会返回它

修复它的一个典型方法是在
let
中使用
labels
的局部递归函数。然后需要调用该函数。稍后您需要返回
summ

* (trace modsum2)
(MODSUM2)
* (modsum2 4)
  0: (MODSUM2 4)
    1: (MODSUM2 3)
      2: (MODSUM2 2)

0       2: MODSUM2 returned 0
    1: MODSUM2 returned 0
  0: MODSUM2 returned 0
0
对,现在你把它缩进了。让我们追踪一下:

(defun modsum2 (n)
  (let ((summ 0))
    (if (>= n 3)
        (if (or (zerop (mod n 3)) (zerop (mod n 5)))
           (progn (setq summ (+ n summ))
                  (modsum2 (1- n)))
           (modsum2 (1- n)))
        (print summ))))
n
的参数为
2
时,可以看到打印
0
。由于
print
表单也是最后一个表单,因此函数返回其值<代码>(打印0)返回
0
。因为返回值在您使用的函数中,所以每次递归调用都会返回它


修复它的一个典型方法是在
let
中使用
labels
的局部递归函数。然后需要调用该函数。稍后您需要返回
summ

您做的工作太多了。只需执行包含排除:

* (trace modsum2)
(MODSUM2)
* (modsum2 4)
  0: (MODSUM2 4)
    1: (MODSUM2 3)
      2: (MODSUM2 2)

0       2: MODSUM2 returned 0
    1: MODSUM2 returned 0
  0: MODSUM2 returned 0
0
;; your function has some flaws
(defun modsum2 (n)
  (let ((summ 0)) ;; in every call, `summ` is put to `0`!
    (if (>= n 3)  ;; for n = 2, the alternative `(print summ)` is executed
        (if (or (zerop (mod n 3)) (zerop (mod n 5)))
           (progn (setq summ (+ n summ))
                  (modsum2 (1- n)))
           (modsum2 (1- n)))
        (print summ)))) ;; for n = 2 already this is called
;; since summ is set to `0` for this last modsum2 call, it prints 0

;; tail call recursion with inner function
(defun modsum2 (n)
  (let ((summ 0))
    (labels ((.modsum2 (.n)
               (cond ((zerop .n) summ)
                     ((or (zerop (mod .n 3)) (zerop (mod .n 5)))
                      (setq summ (+ .n summ))
                      (.modsum2 (1- .n)))
                     (t (.modsum2 (1- .n))))))
      (print (.modsum2 n)))))

;; tail call recursion with optional accumulator for the proper start
(defun modsum2 (n &optional (acc 0))
  (cond ((zerop n) acc)
        ((or (zerop (mod n 3))
             (zerop (mod n 5)))
         (modsum2 (1- n) (+ acc n)))
        (t (modsum2 (1- n) acc))))

;; using loop
(defun modsum2 (n)
  (loop for x from 1 to n
        when (or (zerop (mod x 3)) (zerop (mod x 5)))
        sum x into res
        finally (return res)))
;; which is equivalent to (thanks @Rainer Joswig):
(defun modsum2 (n)
  (loop for x from 1 to n
        when (or (zerop (mod x 3)) (zerop (mod x 5)))
        sum x))

;; using reduce or apply
(defun modsum2 (n)
  (reduce #'+ (remove-if-not #'(lambda (x) (or (zerop (mod x 3)) 
                                               (zerop (mod x 5))))
                             (loop for x from 1 to n))))
;; instead of `reduce`, `apply` would work, too.
要将其扩展到3,5以上,请执行以下操作:

(defun modsum2 (max)
  (let ((a (floor max 3))
        (b (floor max 5))
        (c (floor max 15)))
    (/ (- (+ (* 3 a (1+ a))
             (* 5 b (1+ b)))
          (* 15 c (1+ c)))
       2)))

你做的工作太多了。只需执行包含排除:

;; your function has some flaws
(defun modsum2 (n)
  (let ((summ 0)) ;; in every call, `summ` is put to `0`!
    (if (>= n 3)  ;; for n = 2, the alternative `(print summ)` is executed
        (if (or (zerop (mod n 3)) (zerop (mod n 5)))
           (progn (setq summ (+ n summ))
                  (modsum2 (1- n)))
           (modsum2 (1- n)))
        (print summ)))) ;; for n = 2 already this is called
;; since summ is set to `0` for this last modsum2 call, it prints 0

;; tail call recursion with inner function
(defun modsum2 (n)
  (let ((summ 0))
    (labels ((.modsum2 (.n)
               (cond ((zerop .n) summ)
                     ((or (zerop (mod .n 3)) (zerop (mod .n 5)))
                      (setq summ (+ .n summ))
                      (.modsum2 (1- .n)))
                     (t (.modsum2 (1- .n))))))
      (print (.modsum2 n)))))

;; tail call recursion with optional accumulator for the proper start
(defun modsum2 (n &optional (acc 0))
  (cond ((zerop n) acc)
        ((or (zerop (mod n 3))
             (zerop (mod n 5)))
         (modsum2 (1- n) (+ acc n)))
        (t (modsum2 (1- n) acc))))

;; using loop
(defun modsum2 (n)
  (loop for x from 1 to n
        when (or (zerop (mod x 3)) (zerop (mod x 5)))
        sum x into res
        finally (return res)))
;; which is equivalent to (thanks @Rainer Joswig):
(defun modsum2 (n)
  (loop for x from 1 to n
        when (or (zerop (mod x 3)) (zerop (mod x 5)))
        sum x))

;; using reduce or apply
(defun modsum2 (n)
  (reduce #'+ (remove-if-not #'(lambda (x) (or (zerop (mod x 3)) 
                                               (zerop (mod x 5))))
                             (loop for x from 1 to n))))
;; instead of `reduce`, `apply` would work, too.
要将其扩展到3,5以上,请执行以下操作:

(defun modsum2 (max)
  (let ((a (floor max 3))
        (b (floor max 5))
        (c (floor max 15)))
    (/ (- (+ (* 3 a (1+ a))
             (* 5 b (1+ b)))
          (* 15 c (1+ c)))
       2)))

上周我为euler项目解决了同样的问题。我注意到我的写作方式并没有包含在答案中。把它放在这里,可能会有用

(defun multsum (k max)
  "The sum of multiples of `k' below `max'"
  (let ((a (floor max k)))
    (* k a (1+ a))))

(defun subsequences-reduce (f items)
  (unless items (return ()))
  (loop for (item . rest) on items
    collect (cons 1 item)
    nconc (loop for (len . val) in (subsequences-reduce f rest)
            collect (cons (1+ len) (funcall f item val)))))

(defun modsum (max &rest nums)
  (loop for (len . lcm) in (subsequences-reduce #'lcm nums)
    sum (* (if (oddp len) 1 -1) (multsum lcm max))))

(defun modsum2 (max) (modsum max 3 5))
;;查找数字n以下3和5的倍数
;;因为“or”变为t,所以每当它的一个参数返回t时。无需减去15的倍数。
(第2(n)段)
(条件(i n)总和)
(cond((或(zerop(mod i3))
(zerop(mod i 5)))
(setq summ(+SUMMI(()()())))

我上周为欧拉项目解决了同样的问题。我注意到我的写作方式并没有包含在答案中。把它放在这里,可能会有用

(defun multsum (k max)
  "The sum of multiples of `k' below `max'"
  (let ((a (floor max k)))
    (* k a (1+ a))))

(defun subsequences-reduce (f items)
  (unless items (return ()))
  (loop for (item . rest) on items
    collect (cons 1 item)
    nconc (loop for (len . val) in (subsequences-reduce f rest)
            collect (cons (1+ len) (funcall f item val)))))

(defun modsum (max &rest nums)
  (loop for (len . lcm) in (subsequences-reduce #'lcm nums)
    sum (* (if (oddp len) 1 -1) (multsum lcm max))))

(defun modsum2 (max) (modsum max 3 5))
;;查找数字n以下3和5的倍数
;;因为“or”变为t,所以每当它的一个参数返回t时。无需减去15的倍数。
(第2(n)段)
(条件(i n)总和)
(cond((或(zerop(mod i3))
(zerop(mod i 5)))
(setq summ(+SUMMI(()()())))

您需要做的第一件事是正确缩进代码,以便缩进基于代码的结构。目前,缩进看起来是随机的。例如,第一个IF和第二个IF缩进相同?第二个不是在第一个里面吗。一旦缩进正确,请查看代码并思考何时调用PRINT函数。要调试此代码,请使用
(trace modsum2)
并调用您的函数。我已经更正了缩进,请注意。您已经编辑了代码,并且没有完全缩进。打印语句需要如何缩进?如果您编写更多代码,那么了解编辑器如何缩进Lisp代码将非常有用。您需要做的第一件事是正确缩进代码,以便缩进基于代码的结构。目前,缩进看起来是随机的。例如,第一个IF和第二个IF缩进相同?第二个不是在第一个里面吗。一旦缩进正确,请查看代码并思考何时调用PRINT函数。要调试此代码,请使用
(trace modsum2)
并调用您的函数。我已经更正了缩进,请注意。您已经编辑了代码,并且没有完全缩进。打印语句需要如何缩进?如果您编写更多代码,那么了解编辑器如何缩进Lisp代码将非常有用。
sum to res finally…
不是必需的<代码>求和…足够了<代码>最后求和到res…不是必需的<代码>总和…足够了