List 返回和的Lisp函数

List 返回和的Lisp函数,list,sum,lisp,iteration,clisp,List,Sum,Lisp,Iteration,Clisp,我正试图写一个奇怪的函数,所以请耐心听我说。此函数应将列表L作为参数,并具有sum变量。如果L不是列表,它应该返回nil。否则,它应该遍历列表的每个元素,并执行以下操作: 如果元素是一个数字且小于零,则应从和中减去1 如果元素是一个数字且大于零,则应在总和上加1 如果元素为0或不是数字,则应将0添加到总和中 下面是我的代码,但不管传入的参数如何,它都返回0: (defun sigsum (L) (let ((sum 0)) ;;variable sum

我正试图写一个奇怪的函数,所以请耐心听我说。此函数应将列表
L
作为参数,并具有
sum
变量。如果
L
不是列表,它应该返回
nil
。否则,它应该遍历列表的每个元素,并执行以下操作:

  • 如果元素是一个数字且小于零,则应从和中减去1
  • 如果元素是一个数字且大于零,则应在总和上加1
  • 如果元素为0或不是数字,则应将0添加到总和中
下面是我的代码,但不管传入的参数如何,它都返回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
)
(定义sigsum(L)
(设((和0));;变量和
如果我不是一个列表
零;返回零
(dotimes(i(长度L));否则通过L循环
如果每个元素都是一个数字
(如果(<(elt L i)0);;如果小于0,则从和中减去1
(-和1)
(如果(>(elt L i)0);;如果大于0,则加1求和
(+总和1))
(+sum 0));否则加0到sum
(+sum 0));;不是一个数字,所以给sum加上0
)
总数;返回总数
)
一如既往,我们非常感谢您的帮助。

表达式
(+sum 1)
sum
返回一个以上的值。它不会更改
总和
。您可以使用修改位置的
incf
decf
获得所需位置

您还可以使用
循环

(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
也很有用,但它具有浮点传染(如果其中任何一个数字是浮点,结果也将是浮点)

(-sum 1)
不更新任何变量。因为你不使用结果,它就消失了。这就像是所有的编程语言sum+1
不会更新
sum

如果要更新变量,可以使用
setf

(setf sum (+ sum 1))
(setf sum (- sum 1))
或者您可以使用
incf
decf
这两个宏,它们是扩展到与上述表达式相同的宏

在CL中还有很多其他的方法,你可以使用reduce

(解除mysignum(列表)
(如果(列表列表)
(减少(λ)(e acc)
(+acc)
(cond((或(非(numberp e))(zerope))0)
(
您可以使用循环:

(解除mysignum(列表)
(如果(列表列表)
(循环:用于列表中的e:
:何时(和(数字e)(e 0))
:求和+1
(完)
(零)

其他答案描述了代码中的问题,但看看其他解决问题的方法可能会有所帮助。这是使用键函数进行缩减的一个非常典型的例子(请参阅)。您可以使用(reduce'+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)))
从概念上讲,这种方法与将加法运算符应用于(mapcar'到数字列表)的结果相同,但通常首选reduce,因为函数可以使用最大数量的参数调用,因此(如果(mapcar…)应用“+(mapcar…)会中断返回的列表比该列表长。另一个问题是,mapcar将分配一个全新的列表来保存中间值(将的结果分配给编号),这是一种不必要的空间使用。

(defun discrete(lis)
(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))))
(续) ((和(listp lis)(不等于lis nil))) (让((和0)) (lis do中项目的循环) (条件((或(非(数字项))(等于0项))t) ((和(编号项目)(>项目1))(setf总和(+1总和))) ((和(编号项目)(<项目1))(setf总和(-总和1())) 总数))
用法:
(离散'-12302-12340-1))
=>3

(离散'-4ab))
=>-1

(离散'())
=>零


(离散(a s d f))
=>0

好的解决方案。您可以将(if(not(listp thing))…)替换为(when(listp thing)(reduce…),以使其更简洁。@DanielKochmański它会更简洁,但不那么明确。我认为它归结为首选样式。我倾向于在做有副作用的事情时使用(例如,
(when(minusp x)(setf x(-x))
)我知道
(when nil…
的值被明确定义为
nil
,但是看到
(defun foo…(when…)
让我觉得
foo
的返回值应该是可忽略的,而不是它应该总是“空列表或一个数字”,其中as
(如果…’()(减少“+…))
更清楚地说是一个空列表或数字。我想你对首选样式的看法是对的。很抱歉这么挑剔。@DanielKochmański并非所有;我并不声称我首选的样式在任何客观方面都是正确的;这只是我个人的偏好。我受到了纯函数式编程的一些影响,所以我觉得不能这样做很有吸引力o说“这个函数的输入可以是任何东西;如果它不是一个列表,则返回空列表;如果输入是一个列表,则结果是…”。我喜欢这个函数几乎是分段定义的。
(defun sum (thing)
  (if (not (listp thing))
      nil
      (reduce '+ thing :key 'to-number)))
(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))))