如何在Common LISP中执行while循环?



嗨,我是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)))

最新更新