问题描述
因此,我被要求做一个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 +
.
如果我有一个停止条件:((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.
要解决在以下调用中仅传递两个参数的问题,可以使用 apply
:它需要函数和参数列表来调用:(appply#'average(cons a(cdr b)))代码>
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:
- 计算所有元素的总和.
- 将其除以所有元素的数量.
您可以编写自己的函数来递归地添加所有元素来解决第一部分(做到这一点!),但是已经有这样的函数了:
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的平均使用量和剩余量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!