Lisp的平均使用量和剩余量 [英] Average using &rest in lisp

查看:81
本文介绍了Lisp的平均使用量和剩余量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我被要求做一个LISP函数,该函数计算任何给定数字的平均值.要求我执行此操作的方法是使用& rest参数.所以我想出了这个:

So i was asked to do a function i LISP that calculates the average of any given numbers. The way i was asked to do this was by using the &rest parameter. so i came up with this :

(defun average (a &rest b)
   (cond ((null a) nil)
         ((null b) a)
         (t (+ (car b) (average a (cdr b))))))

现在我知道这是不正确的,因为(cdr b)返回一个列表,里面有一个列表,所以当我这样做(car b)时,它从不返回原子,因此也从不添加(+)

Now i know this is incorrect because the (cdr b) returns a list with a list inside so when i do (car b) it never returns an atom and so it never adds (+)

这是我的第一个问题:

  • 如何调用& rest参数的CDR并仅获取一个列表,而不是列表中的列表?

现在还有另一件事:当我运行此函数并将值赋给& rest时,说(平均1 2 3 4 5),这给了我stackoverflow错误.我跟踪了功能,发现它卡在了一个循环中,总是用(cdr b)witch调用该函数为null,因此它在那里循环了.我的问题是:

Now there is other thing : When i run this function and give values to the &rest, say (average 1 2 3 4 5) it gives me stackoverflow error. I traced the funcion and i saw that it was stuck in a loop, always calling the function with the (cdr b) witch is null and so it loops there. My question is:

  • 如果我有一个停止条件:((null b)a),当b为null时程序不应该停止并将"a"添加到+操作中吗?为什么会启动无限循环?

我知道函数只执行+操作,我知道我必须除以b列表的长度+ 1,但是由于出现此错误,我想先解决它.

I know the function only does the + operation, i know i have to divide by the length of the b list + 1, but since i got this error i'd like to solve it first.

推荐答案

(defun average (a &rest b)
  ; ...
  )

当您使用(平均1 2 3 4)进行调用时,在函数内部,符号 a 将绑定到 1 ,并且正确列表的符号 b (2 3 4).

When you call this with (average 1 2 3 4) then inside the function the symbol a will be bound to 1 and the symbol b to the proper list (2 3 4).

因此,在 average 内,(car b) 给您剩下的第一个参数,而(cdr b)将为您提供其余的其余参数.

So, inside average, (car b) will give you the first of the rest parameters, and (cdr b) will give you the rest of the rest parameters.

但是,当您递归地调用(平均a(cdr b))时,则仅用两个自变量调用它,无论给定多少个参数首先发挥作用.在我们的示例中,它与(平均1'(3 4)).

But when you then recursively call (average a (cdr b)), then you call it with only two arguments, no matter how many parameters where given to the function in the first place. In our example, it's the same as (average 1 '(3 4)).

更重要的是,第二个参数现在是一个列表.因此,在第二次调用 average 时,符号将如下绑定:

More importantly, the second argument is now a list. Thus, in the second call to average, the symbols will be bound as follows:

  • a = 1
  • b =((3 4))

b 是仅包含单个元素的列表:另一个列表.这就是为什么将(car b)作为参数传递给 + 时会出错的原因.

b is a list with only a single element: Another list. This is why you'll get an error when passing (car b) as argument to +.

现在还有另一件事:当我运行此函数并将值赋给& rest时,说(平均1 2 3 4 5),这给了我stackoverflow错误.我跟踪了功能,发现它卡在了一个循环中,总是用(cdr b)witch调用该函数为null,因此它在那里循环了.我的问题是:

Now there is other thing : When i run this function and give values to the &rest, say (average 1 2 3 4 5) it gives me stackoverflow error. I traced the funcion and i saw that it was stuck in a loop, always calling the function with the (cdr b) witch is null and so it loops there. My question is:

如果我有一个停止条件:((null b)a),当b为null时程序不应该停止并将"a"添加到+操作中吗?为什么会启动无限循环?

If i have a stopping condition: ( (null b) a) , shouldnt the program stop when b is null and add "a" to the + operation ? why does it start an infinite loop ?

仅当 b 为空列表时,

(null b)才是真实的.但是,当您调用(平均为​​'())时, b 将绑定到(()),即包含空列表.

(null b) will only be truthy when b is the empty list. But when you call (average a '()), then b will be bound to (()), that is a list containing the empty list.

要解决在以下调用中仅传递两个参数的问题,可以使用

Solving the issue that you only pass exactly two arguments on the following calls can be done with apply: It takes the function as well as a list of parameters to call it with: (appply #'average (cons a (cdr b)))

现在要实现编写平均值函数的最初目标:计算平均值包括两个任务:

Now tackling your original goal of writing an average function: Computing the average consists of two tasks:

  1. 计算所有元素的总和.
  2. 将其除以所有元素的数量.

您可以编写自己的函数来递归地添加所有元素来解决第一部分(做到这一点!),但是已经有这样的函数了:

You could write your own function to recursively add all elements to solve the first part (do it!), but there's already such a function:

(+ 1 2) ; Sum of two elements
(+ 1 2 3) ; Sum of three elements
(apply #'+ '(1 2 3)) ; same as above
(apply #'+ some-list) ; Summing up all elements from some-list

因此,您的平均值仅仅是

Thus your average is simply

(defun average (&rest parameters)
  (if parameters  ; don't divide by 0 on empty list
      (/ (apply #'+ parameters) (length parameters))
      0))

作为最后的提示:处理列表时,不应使用 car cdr .最好使用更具描述性的名称 first rest .

As a final note: You shouldn't use car and cdr when working with lists. Better use the more descriptive names first and rest.

如果性能对您至关重要,则最好折叠参数(使用 reduce (可能已优化):

If performance is critical to you, it's probably best to fold the parameters (using reduce which might be optimized):

(defun average (&rest parameters)
    (if parameters
        (let ((accum
                (reduce #'(lambda (state value)
                            (list (+ (first state) value) ;; using setf is probably even better, performance wise.
                                (1+ (second state))))
                        parameters
                        :initial-value (list 0 0))))
            (/ (first accum) (second accum)))
        0))

(实时演示)

#'是阅读器宏,特别是s

#' is a reader macro, specifically one of the standard dispatching macro characters, and as such an abbreviation for (function ...)

这篇关于Lisp的平均使用量和剩余量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆