嗨,我是Common Lisp的新手,我找不到任何与我当前的问题相关的教程,我在Java方面有很好的知识,我尝试将简单的程序从Java转换为Common LISP作为练习,我不能做的一件事是while循环,我该怎么做? 它总是导致未定义的函数
TL;博士
如何在常见的 LISP 中执行 while 循环,并在某些条件下执行,例如在 Java 中,如下所示:
while(UserIn > 0)
{
LastD = UserIn % 10;
Sum = Sum + LastD;
Product = Product * LastD;
UserIn = UserIn / 10;
}
if (Sum == Product)
{
System.out.println("nGiven number is a Spy number");
}
else
{
System.out.println("nGiven number is not a Spy number");
}
我在通用 LISP 中的尝试如下
(while (> userIn 0)
(setq LastD(mod 10 UserIn))
(setq Product(* LastD Product))
(setq Sum(+ LastD Sum))
(setq UserIn(/ UserIn 10)))
(terpri)
(if (= a b)
(format t "is a spy number")
(format t "is not a spy number"))
)
(Spynumber)
它一直在说:调试器在未定义的函数上调用,谢谢!
Common Lisp 没有while
形式,但它有一个更强大的loop
具有所需while
关键字的宏:
(loop while ... do ...)
参见 如何在 LISP 中执行 while 循环
正如其他人所说,您可以使用loop
来做到这一点,这将是惯用的方法。
但是Lisp是可编程的编程语言:如果你想要while
,你可以有while
:
(defmacro while (test &body decls/tags/forms)
`(do () ((not ,test) (values))
,@decls/tags/forms))
现在
> (let ((i 0))
(while (< i 10)
(print i)
(incf i)))
0
1
2
3
4
5
6
7
8
9
>
Java 中的示例程序分配的变量未在当前作用域中声明。在 Lisp 中,你也有同样的问题,在 Lisp 中,你对未声明的变量调用setq
。你需要在 Lisp 中声明变量,否则行为是未指定的。
声明变量的一种方法是有一个let
块:
(let ((product 1)
(sum 0))
...
(setq sum (+ sum d)) ;; <-- here SETQ is well-defined
...)
此外,您还可以计算商和除法的余数:在 Common Lisp 中,函数可以返回多个值,特别是,truncate
除法并将余数作为辅助值返回:
(multiple-value-bind (q r) (truncate u 10)
...)
递归方法
可以将循环编写为递归过程,您的示例是我发现递归方法更容易理解的情况之一。让我们定义spy-compute
三个参数的函数:一个数字、当前总和和余数的当前乘积:
(defun spy-compute (u s p)
...)
基本情况对应于(= u 0)
,在这种情况下,函数将总和和乘积作为两个值返回:
(defun spy-compute (u s p)
(if (= u 0)
(values s p)
...))
递归的生成情况包括将u除以10,并使用修改后的sum和乘积递归调用spy-number
:
(defun spy-compute (u s p)
(if (= u 0)
(values s p)
(multiple-value-bind (q r) (truncate u 10)
(spy-compute q (+ s r) (* p r)))))
调用此函数时,总和初始化为 0,乘积初始化为 1。您可以为调用方提供更简单的接口:
(defun spy (n)
(spy-compute n 0 1))
(我在这里修复了一个错误,我有 0 和 1 颠倒了)
如果你想检查这个数字是否是一个间谍数字,你可以定义这个函数(p
后缀是"谓词",返回布尔值的函数的命名约定):
(defun spyp (n)
(multiple-value-bind (s p) (spy n)
(= s p)))
例有了上面定义的三个函数,让我们跟踪它们并检查 1124 是否是间谍号码(剧透警报,它是):
* (trace spy-compute spy spyp)
* (spyp 1124)
这是执行跟踪,我手动添加了注释:
;; root call to SPYP with 1124
0: (SO::SPYP 1124)
;; it calls SPY
1: (SO::SPY 1124)
;; ... which calls SPY-COMPUTE with sum 0 and product 1
2: (SO::SPY-COMPUTE 1124 0 1)
;; DIVIDE by TEN, REMAINDER is 4
;; RECURSE With SUM = SUM + 4 and PRODUCT = PRODUCT * 4
3: (SO::SPY-COMPUTE 112 4 4)
;; DIVIDE by TEN: 112 = 11 * 10 + 2, adjust counters
4: (SO::SPY-COMPUTE 11 6 8)
;; ETC.
5: (SO::SPY-COMPUTE 1 7 8)
;; BASE CASE OF RECURSION, RETURN BOTH COUNTERS
6: (SO::SPY-COMPUTE 0 8 8)
;; NO CHANGE IS MADE TO THE RESULT, IT BUBBLES UP
6: SPY-COMPUTE returned 8 8
5: SPY-COMPUTE returned 8 8
4: SPY-COMPUTE returned 8 8
3: SPY-COMPUTE returned 8 8
2: SPY-COMPUTE returned 8 8
1: SPY returned 8 8
;; CHECK if (= P S), which is T here
0: SPYP returned T
迭代
您的示例也可以使用循环编写。除了其他标准的循环方式,你也可以使用iterate
包,它与LOOP相反,允许将测试子句(while)与迭代子句(用于)混合:
(ql:quickload :iterate) ;; see https://www.quicklisp.org/beta/
(use-package :iterate)
(defun iter-spy (n)
(iter
(for u :initially n :then q)
(while (> u 0))
(for (values q r) = (truncate u 10))
(sum r :into s)
(multiply r :into p)
(finally (return
(values s p)))))
使用do
可以并行循环和分配变量,语法为:
(do ((<var1> <var1-initial-value> <var1-step>)
(<var2> <var2-initial-value> <var2-step>)
...)
((<exit-condition>)
(<final-statement1>)
(<final-statement2>)
...)
(<action1-during-loop>)
(<action2-during-loop>)
...)
因此,对于您的代码,或多或少:
(let* ((UserIn (read))
(UI UserIn))
(do* ((LastD (rem UserIn 10) (rem UserIn 10))
(Sum 0 (+ Sum LastD))
(Product 1 (* Product LastD))
(UserIn UserIn (truncate UserIn 10)))
((<= UserIn 0)
(format t "~&LastD: ~f, Sum: ~f, Product: ~f, UserIn: ~f"
LastD Sum Product UserIn)
(if (= Sum Product)
(format t "~&~A is Spy number" UI)
(format t "~&~A is Not Spy number" UI)))
(format t "~&LastD: ~f, Sum: ~f, Product: ~f, UserIn: ~f"
LastD Sum Product UserIn)))
> LastD: 4.0, Sum: 0.0, Product: 1.0, UserIn: 1124.0
> LastD: 4.0, Sum: 4.0, Product: 4.0, UserIn: 112.0
> LastD: 2.0, Sum: 6.0, Product: 8.0, UserIn: 11.0
> LastD: 1.0, Sum: 7.0, Product: 8.0, UserIn: 1.0
> LastD: 1.0, Sum: 8.0, Product: 8.0, UserIn: 0.0
> 1124 is Spy number
> LastD: 2.0, Sum: 0.0, Product: 1.0, UserIn: 12.0
> LastD: 2.0, Sum: 2.0, Product: 2.0, UserIn: 1.0
> LastD: 1.0, Sum: 3.0, Product: 2.0, UserIn: 0.0
> 12 is Not Spy number
对于某些代码片段,您可以访问 http://rosettacode.org/wiki/Rosetta_Code。
(defmacro while (condition &rest body)
`(loop while ,condition
do (progn
,@body)))