Returns 一个 Sum 的 Lisp 函数
Lisp Function that Returns a Sum
我正在尝试编写一个奇怪的函数,请耐心等待。此函数应将列表 L
作为参数并具有 sum
变量。如果 L
不是一个列表,它应该 return nil
。否则,它应该遍历列表的每个元素并执行以下操作:
- 如果元素是一个数字且小于零,则应从总和中减去 1。
- 如果元素是一个数字且大于零,则应将总和加 1。
- 如果元素为 0 或不是数字,则应将总和加 0。
这是我的代码,但无论传入的参数如何,它 returns 0:
(defun sigsum (L)
(let ((sum 0)) ;;variable sum
(if (not (listp L)) ;;if L is not a list
nil ;;return nil
(dotimes (i (length L)) ;;otherwise loop through L
(if (numberp (elt L i)) ;;if each element is a number
(if (< (elt L i) 0) ;;if is less than 0 subtract 1 from sum
(- sum 1)
(if (> (elt L i) 0) ;;if greater than 0 add 1 to sum
(+ sum 1))
(+ sum 0)) ;;else add 0 to sum
(+ sum 0))) ;;not a number so add 0 to sum
)
sum) ;;return sum
)
一如既往,非常感谢您的帮助。
表达式(+ sum 1)
returns比sum
多一个。它不会改变 sum
。您可以使用 incf
或 decf
来修改位置。
您也可以使用 loop
:
(loop :for element :in list
:count (and (numberp element) (plusp element)) :into plus
:count (and (numberp element) (minusp element)) :into minus
:finally (return (- plus minus)))
Signum
也很有用,但它具有浮点数传染性(如果任何数字是浮点数,结果也将是浮点数)。
(loop :for element :in list
:when (numberp element)
:sum (signum element))
(- sum 1)
不更新任何变量。由于您不使用结果,因此它会消失。好像所有的编程语言都是这样。 sum + 1
在 Algol 语言(如 C)中不会更新 sum
。
如果你想更新一个变量你可以使用setf
:
(setf sum (+ sum 1))
(setf sum (- sum 1))
或者您可以使用 incf
和 decf
,它们是扩展为与上述表达式相同的宏。
在 CL 中还有许多其他方法可以做到这一点。你可以使用 reduce
(defun mysignum (list)
(if (listp list)
(reduce (lambda (e acc)
(+ acc
(cond ((or (not (numberp e)) (zerop e)) 0)
((< e 0) -1)
(t 1))))
list)
nil))
您可以使用循环:
(defun mysignum (list)
(if (listp list)
(loop :for e :in list
:when (and (numberp e) (< e 0))
:summing -1
:end
:when (and (numberp e) (> e 0))
:summing +1
:end)
nil))
其他答案已经描述了您的代码中的问题,但查看其他解决问题的方法可能会有所帮助。这是一个非常典型的 reduction 和 key function(参见 reduce)。你可以用 (reduce '+ list) 来总结 list 中的元素。但是,您不想只对元素求和,其中一些可能不是数字,您希望将每个元素映射到一个数字(-1、0 或 1),然后将它们相加。这意味着您需要一个关键功能。首先,让我们定义将元素取为 -1、0 或 1 的函数:
(defun to-number (x)
(cond
((and (numberp x) (< x 0)) -1)
((and (numberp x) (> x 0)) 1)
((or (not (numberp x)) (zerop x)) 0)))
然后你的 sum 函数需要 return nil 如果它的参数不是列表,或者 (reduce '+ … :key 'to-number) 如果它的参数是一个列表:
(defun sum (thing)
(if (not (listp thing))
nil
(reduce '+ thing :key 'to-number)))
从概念上讲,这种方法与将加法运算符应用于(mapcar 'to-number list), but reduce is generally preferred, because there can be a maximum number of arguments a function can be called with, so (apply '+ (mapcar …)) 如果 (mapcar …) return 的列表比这更长,则 =35=] 会中断。另一个问题是 mapcar 会分配一个全新的列表来保存中间值(to-number 的结果),这是不必要的space 用法。
(defun discrete (lis)
(cond
((and (listp lis) (not (equal lis nil)))
(let ((sum 0))
(loop for item in lis do
(cond ((or (not (numberp item)) (equal 0 item)) t)
((and (numberp item) (> item 1)) (setf sum (+ 1 sum)))
((and (numberp item) (< item 1)) (setf sum (- sum 1)))))
sum))))
用法:(discrete '(-1 2 3 0 2 2 -1 2 34 0 -1))
=> 3
(discrete '(-4 a b))
=>-1
(discrete '())
=> 零
(discrete '(a s d f))
=> 0
我正在尝试编写一个奇怪的函数,请耐心等待。此函数应将列表 L
作为参数并具有 sum
变量。如果 L
不是一个列表,它应该 return nil
。否则,它应该遍历列表的每个元素并执行以下操作:
- 如果元素是一个数字且小于零,则应从总和中减去 1。
- 如果元素是一个数字且大于零,则应将总和加 1。
- 如果元素为 0 或不是数字,则应将总和加 0。
这是我的代码,但无论传入的参数如何,它 returns 0:
(defun sigsum (L)
(let ((sum 0)) ;;variable sum
(if (not (listp L)) ;;if L is not a list
nil ;;return nil
(dotimes (i (length L)) ;;otherwise loop through L
(if (numberp (elt L i)) ;;if each element is a number
(if (< (elt L i) 0) ;;if is less than 0 subtract 1 from sum
(- sum 1)
(if (> (elt L i) 0) ;;if greater than 0 add 1 to sum
(+ sum 1))
(+ sum 0)) ;;else add 0 to sum
(+ sum 0))) ;;not a number so add 0 to sum
)
sum) ;;return sum
)
一如既往,非常感谢您的帮助。
表达式(+ sum 1)
returns比sum
多一个。它不会改变 sum
。您可以使用 incf
或 decf
来修改位置。
您也可以使用 loop
:
(loop :for element :in list
:count (and (numberp element) (plusp element)) :into plus
:count (and (numberp element) (minusp element)) :into minus
:finally (return (- plus minus)))
Signum
也很有用,但它具有浮点数传染性(如果任何数字是浮点数,结果也将是浮点数)。
(loop :for element :in list
:when (numberp element)
:sum (signum element))
(- sum 1)
不更新任何变量。由于您不使用结果,因此它会消失。好像所有的编程语言都是这样。 sum + 1
在 Algol 语言(如 C)中不会更新 sum
。
如果你想更新一个变量你可以使用setf
:
(setf sum (+ sum 1))
(setf sum (- sum 1))
或者您可以使用 incf
和 decf
,它们是扩展为与上述表达式相同的宏。
在 CL 中还有许多其他方法可以做到这一点。你可以使用 reduce
(defun mysignum (list)
(if (listp list)
(reduce (lambda (e acc)
(+ acc
(cond ((or (not (numberp e)) (zerop e)) 0)
((< e 0) -1)
(t 1))))
list)
nil))
您可以使用循环:
(defun mysignum (list)
(if (listp list)
(loop :for e :in list
:when (and (numberp e) (< e 0))
:summing -1
:end
:when (and (numberp e) (> e 0))
:summing +1
:end)
nil))
其他答案已经描述了您的代码中的问题,但查看其他解决问题的方法可能会有所帮助。这是一个非常典型的 reduction 和 key function(参见 reduce)。你可以用 (reduce '+ list) 来总结 list 中的元素。但是,您不想只对元素求和,其中一些可能不是数字,您希望将每个元素映射到一个数字(-1、0 或 1),然后将它们相加。这意味着您需要一个关键功能。首先,让我们定义将元素取为 -1、0 或 1 的函数:
(defun to-number (x)
(cond
((and (numberp x) (< x 0)) -1)
((and (numberp x) (> x 0)) 1)
((or (not (numberp x)) (zerop x)) 0)))
然后你的 sum 函数需要 return nil 如果它的参数不是列表,或者 (reduce '+ … :key 'to-number) 如果它的参数是一个列表:
(defun sum (thing)
(if (not (listp thing))
nil
(reduce '+ thing :key 'to-number)))
从概念上讲,这种方法与将加法运算符应用于(mapcar 'to-number list), but reduce is generally preferred, because there can be a maximum number of arguments a function can be called with, so (apply '+ (mapcar …)) 如果 (mapcar …) return 的列表比这更长,则 =35=] 会中断。另一个问题是 mapcar 会分配一个全新的列表来保存中间值(to-number 的结果),这是不必要的space 用法。
(defun discrete (lis)
(cond
((and (listp lis) (not (equal lis nil)))
(let ((sum 0))
(loop for item in lis do
(cond ((or (not (numberp item)) (equal 0 item)) t)
((and (numberp item) (> item 1)) (setf sum (+ 1 sum)))
((and (numberp item) (< item 1)) (setf sum (- sum 1)))))
sum))))
用法:(discrete '(-1 2 3 0 2 2 -1 2 34 0 -1))
=> 3
(discrete '(-4 a b))
=>-1
(discrete '())
=> 零
(discrete '(a s d f))
=> 0