如何在 Common LISP 中执行 While 循环?
How to do a While loop in Common LISP?
嗨,我是 Common Lisp 的新手,我找不到任何与我当前问题相关的教程,我在 Java 方面有不错的知识,我尝试从 [=24= 转换简单的程序] Common LISP 作为练习,我不能做的一件事是 while 循环,我该怎么做?它总是导致 UNDEFINED-FUNCTION
TL;DR
如何在常见的 LISP 中使用某些条件(例如 Java 中的 while 循环,如下所示:
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");
}
我对common 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
macro which has the while
您想要的关键字:
(loop while ... do ...)
另见 How to do a while loop in LISP
正如其他人所说,您可以使用 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 中有同样的问题,你在没有声明的变量上调用 setq
。您需要在 Lisp 中声明变量,否则行为未指定。
声明变量的一种方法是使用 let
块:
(let ((product 1)
(sum 0))
...
(setq sum (+ sum d)) ;; <-- here SETQ is well-defined
...)
此外,您计算除法的商和余数:在 Common Lisp 函数中可以 return 多个值,特别是 truncate
除法和 returns余数作为次要值:
(multiple-value-bind (q r) (truncate u 10)
...)
递归方法
可以将循环编写为递归过程,您的示例是我发现递归方法更容易理解的案例之一。让我们定义 spy-compute
一个包含三个参数的函数:一个数字、当前总和和当前余数的乘积:
(defun spy-compute (u s p)
...)
基本情况对应于 (= u 0)
,在这种情况下,函数 return 是总和和乘积,作为两个值:
(defun spy-compute (u s p)
(if (= u 0)
(values s p)
...))
递归的一般情况包括将 u 除以 10,并使用修改后的总和和乘积递归调用 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
后缀是“predicate”,return是函数的命名约定布尔值):
(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) 与迭代子句 (for) 混合使用:
(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)))
嗨,我是 Common Lisp 的新手,我找不到任何与我当前问题相关的教程,我在 Java 方面有不错的知识,我尝试从 [=24= 转换简单的程序] Common LISP 作为练习,我不能做的一件事是 while 循环,我该怎么做?它总是导致 UNDEFINED-FUNCTION
TL;DR
如何在常见的 LISP 中使用某些条件(例如 Java 中的 while 循环,如下所示:
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");
}
我对common 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
macro which has the while
您想要的关键字:
(loop while ... do ...)
另见 How to do a while loop in LISP
正如其他人所说,您可以使用 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 中有同样的问题,你在没有声明的变量上调用 setq
。您需要在 Lisp 中声明变量,否则行为未指定。
声明变量的一种方法是使用 let
块:
(let ((product 1)
(sum 0))
...
(setq sum (+ sum d)) ;; <-- here SETQ is well-defined
...)
此外,您计算除法的商和余数:在 Common Lisp 函数中可以 return 多个值,特别是 truncate
除法和 returns余数作为次要值:
(multiple-value-bind (q r) (truncate u 10)
...)
递归方法
可以将循环编写为递归过程,您的示例是我发现递归方法更容易理解的案例之一。让我们定义 spy-compute
一个包含三个参数的函数:一个数字、当前总和和当前余数的乘积:
(defun spy-compute (u s p)
...)
基本情况对应于 (= u 0)
,在这种情况下,函数 return 是总和和乘积,作为两个值:
(defun spy-compute (u s p)
(if (= u 0)
(values s p)
...))
递归的一般情况包括将 u 除以 10,并使用修改后的总和和乘积递归调用 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
后缀是“predicate”,return是函数的命名约定布尔值):
(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) 与迭代子句 (for) 混合使用:
(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)))