在Lisp中,如何将1000以下所有可被3或5整除的数字相加?
我昨天开始用CommonLisp编程。现在我想。我想到了:在Lisp中,如何将1000以下所有可被3或5整除的数字相加?,lisp,common-lisp,sbcl,Lisp,Common Lisp,Sbcl,我昨天开始用CommonLisp编程。现在我想。我想到了: (loop for n from 1 to 1000 when (or (eq (mod n 5) 0) (eq (mod n 3) 0)) (sum n))) 我知道循环部分工作(loop for n from 1 to 1000 sum n)对前1000个数字求和。我知道((eq(mod n 5)0)(mod n 3)0))部件工作正常。我知道(或者(eq(mod n5)0)(eq(
(loop for n from 1 to 1000 when
(or
(eq (mod n 5) 0)
(eq (mod n 3) 0))
(sum n)))
我知道循环部分工作(loop for n from 1 to 1000 sum n)
对前1000个数字求和。我知道((eq(mod n 5)0)(mod n 3)0))
部件工作正常。我知道(或者(eq(mod n5)0)(eq(mod n3)0))
是有效的。因此,对我来说,它看起来像一个健壮的程序,但当我运行它时,我得到了错误:
1=(SUM N)在WHEN当前循环上下文之后找到where关键字expected get循环子句:WHEN(或(EQ(MOD 1000 5)0)
(等式(MOD 1000 3)0)
1#. [SB-INT类型的条件:简单程序错误]
我怀疑or语句后面的(sum n)
有问题。但我不知道这是为什么,也不知道如何解决。有人能帮我把我的第一个Lisp程序运行起来吗?sum n
,而不是(sum n)
不要把sum n
放在括号里。循环
宏是它自己的领域特定语言,具有自己的语法。有了它,您将(循环…sum n)
。本产品中的HyperSpec条目中给出了语法:
numeric-accumulation::= {count | counting | sum | summing | }
maximize | maximizing | minimize | minimizing {form | it}
[into simple-var] [type-spec]
如果听起来更好的话,您还可以编写(循环…求和n)
。这可能更像是一个自然的英语句子
=
,eql
或zerop
,但不是eq
在HyperSpec中查找函数、宏等是一种很好的做法。正如雷纳·乔斯维格所指出的,你不应该用eq
来比较数字。为什么?让我们在HyperSpec中查找它。这些例子包括:
(eq 3 3)
=> true
OR=> false
(eq 3 3.0) => false
(eq 3.0 3.0)
=> true
OR=> false
(eq #c(3 -4) #c(3 -4))
=> true
OR=> false
注释部分说(增加强调):
打印时显示相同的对象不一定相等
彼此打印相同内容的符号通常彼此相等
因为使用了实习生功能但是,带有
相同的值不必为eq,两个相似的列表通常不需要
一模一样
允许实现“复制”字符和
任何时候都可以使用数字。其效果是Common Lisp不能保证
即使eq的两个参数“相同”,如果
那东西是一个字符或数字。
对于数字,你需要一些其他的东西=
是一个很好的通用数字比较,尽管它在这里做的工作比您需要的要多,因为它可以比较不同类型的数字。例如,(=5.0)
为真。因为您只关心0
,所以可以使用zerop
,但这仍然会比您需要做更多的工作,因为它还会检查其他数字类型。例如,(zerop#c(0.0 0.0))
是真的。在这种情况下,由于(mod n…
将给您一个整数,因此您可以使用eql
:
eql的值对于以下两个对象(x和y)为真
案例:
(或(eql(mod n 3)0)(eql(mod n 5)0))
其他方法
现在,你的问题是关于循环语法的一个特殊部分,关于等式运算符有一些要点需要说明。然而,由于其他一些答案已经研究了其他方法来实现这一点,我认为值得指出的是,有更有效的方法来实现这一点。首先,让我们看一种方法,将给定极限下某个数字的所有倍数相加。例如,对于数字3和包含限制26,我们有总和
?=3+6+9+12+15+18+21+24
=(3+24)+(6+21)+(9+18)+(12+15)
=27+27+27+27 一般来说,如果你试着用几个不同的数字,你可以算出,对于一个包含的极限l和一个数字n,你将把数对相加,如果有一个奇数的n的倍数小于l,你可以选择半对。我不打算计算出整个推导过程,但你可以用
(defun sum-of-multiples-below-inclusive (limit divisor)
(multiple-value-bind (quotient remainder)
(floor limit divisor)
(let ((pair (+ (- limit remainder) divisor)))
(multiple-value-bind (npairs half-pair)
(floor quotient 2)
(+ (* npairs pair)
(if (oddp half-pair)
(floor pair 2)
0))))))
然后,要找出小于给定数字的倍数之和,您可以从“限制”中减去一:
(defun sum-of-multiples-below (limit divisor)
(sum-of-multiples-below (1- limit) divisor))
然后,为了扩展到有多个除数的情况,需要将这些数相加,然后减去两次计数的数。例如,在您的情况下:
(+ (sum-of-multiples-below 1000 3)
(sum-of-multiples-below 1000 5)
(- (sum-of-multiples-below 1000 15)))
;=> 233168
(loop for i from 1 below 1000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i)
;=> 233168
现在,天真地使用time
可能会导致误导性结果,但SBCL会在对表单求值之前编译表单,所以这并不可怕。这是一个非常非常小的微型基准测试,但看看每种形式中使用的周期数:
(time (+ (sum-of-multiples-below 1000 3)
(sum-of-multiples-below 1000 5)
(- (sum-of-multiples-below 1000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
11,327 processor cycles
0 bytes consed
使用封闭形式要快得多。如果我们使用更高的限值,则差异更为明显。让我们看看100000:
(time (+ (sum-of-multiples-below 100000 3)
(sum-of-multiples-below 100000 5)
(- (sum-of-multiples-below 100000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,378 processor cycles
0 bytes consed
对于10000000人来说,数字甚至更令人震惊:
(time (+ (sum-of-multiples-below 10000000 3)
(sum-of-multiples-below 10000000 5)
(- (sum-of-multiples-below 10000000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,797 processor cycles
0 bytes consed
其中一些项目Euler问题非常有趣。他们中的一些人有一些非常简单的天真解决方案,这些解决方案只适用于小的输入,但根本不能很好地扩展 sum n
,而不是(sum n)
不要把sum n
放在括号里。循环
宏是它自己的领域特定语言,具有自己的语法。有了它,您将(循环…sum n)
。本产品中的HyperSpec条目中给出了语法:
numeric-accumulation::= {count | counting | sum | summing | }
maximize | maximizing | minimize | minimizing {form | it}
[into simple-var] [type-spec]
如果听起来更好的话,您还可以编写(循环…求和n)
。这可能更像是一个自然的英语句子
=
,eql
或zerop
,但不是eq
在HyperSpec中查找函数、宏等是一种很好的做法。正如雷纳·乔斯维格所指出的,你不应该用eq
来比较数字。为什么?让我们在HyperSpec中查找它。例子
(time (+ (sum-of-multiples-below 10000000 3)
(sum-of-multiples-below 10000000 5)
(- (sum-of-multiples-below 10000000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,797 processor cycles
0 bytes consed
(time (loop for i from 1 below 10000000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.712 seconds of real time
0.712044 seconds of total run time (0.712044 user, 0.000000 system)
100.00% CPU
1,916,513,379 processor cycles
0 bytes consed
(loop for n from 1 below 1000
when (or (zerop (mod n 3))
(zerop (mod n 5)))
sum n))
(defun sum-to-thousand (count result)
(cond ((> count 1000) result)
((= (mod count 3) 0) (sum-to-thousand (+ count 1) (+ count result)))
((= (mod count 5) 0) (sum-to-thousand (+ count 1) (+ count result)))
(t (sum-to-thousand (+ count 1) result))))
CL-USER> (defun my-sum (&key (from 1) to dividers (sum 0))
(if (>= from to)
sum
(my-sum :from (1+ from)
:to to
:dividers dividers
:sum (if (some (lambda (x) (zerop (mod from x))) dividers)
(+ sum from)
sum))))
MY-SUM
CL-USER> (my-sum :to 1000 :dividers '(3 5))
233168