📅  最后修改于: 2023-12-03 14:44:00.747000             🧑  作者: Mango
在 LISP 中,由于其函数式编程的特性,没有像其他语言中的 for
或 while
循环这样的语句。LISP 中的循环构造均通过将函数嵌套调用来实现。
loop
是 LISP 中最基本的循环构造,可用于实现各种循环。其基本格式如下:
(loop
[initialization]
[termination]
[increment]
body)
其中:
initialization
:初始化表达式,用于对循环变量进行初始化,可省略。termination
:终止表达式,用于定义循环的终止条件。每次循环前都会计算该表达式,并检查其是否为真,若为真则继续循环,否则退出循环。increment
:增量表达式,用于定义循环变量的变化方式。每次循环末都会计算该表达式,并将其结果赋值给循环变量。body
:循环体,包含循环中需要执行的语句。可以是一个或多个表达式,最后一个表达式的返回值就是整个循环的返回值。下面是一个简单的例子,用 loop
计算 $1+2+3+...+10$:
(let ((sum 0))
(loop for i from 1 to 10 do
(incf sum i))
sum)
其中,for i from 1 to 10
定义了循环变量 i
的取值范围为 1 到 10(包括边界值),do
后面跟着需要执行的语句。
do
是 LISP 中更加灵活的循环构造,可以支持多个循环变量,以及循环变量的不同取值方式。其基本格式如下:
(do ((var1 init1 step1)
(var2 init2 step2)
...
(varn initn stepn))
(termination)
body)
其中:
var1
、var2
、...、varn
:循环变量,可以有一个或多个,用括号括起来,中间用空格隔开。init1
、init2
、...、initn
:循环变量的初始化表达式,用于对循环变量进行初始化。step1
、step2
、...、stepn
:循环变量的增量表达式,用于定义循环变量的变化方式。termination
:循环终止条件,与 loop
中的终止表达式类似,用于定义循环何时结束。body
:循环体,包含循环中需要执行的语句。下面是一个计算 $1^2+2^2+3^2+...+n^2$ 的例子:
(defun sum-of-squares (n)
(do ((i 1 (1+ i))
(sum 0 (+ sum (* i i))))
((> i n) sum)))
其中,(1+ i)
表示对 i
做自增操作,(> i n)
表示当 i
大于 n
时循环结束,(+ sum (* i i))
表示将 i^2
加到 sum
中。
除了 loop
和 do
,LISP 还提供了许多其他的循环构造。其中,mapcar
是一个常用的高阶函数,可以用于将一个函数作用于列表的每个元素上,并返回结果列表。其基本格式如下:
(mapcar #'function sequence)
其中:
function
:需要作用于列表中每个元素的函数。sequence
:需要操作的列表。可以是一个普通列表,也可以是向量等序列容器。下面是一个将列表中所有元素平方的例子:
(mapcar #'(lambda (x) (* x x)) '(1 2 3 4 5))
其中,#'
表示将指定的符号转换成对应的函数,(lambda (x) (* x x))
是一个匿名函数,用来计算平方,'(1 2 3 4 5)
是一个包含 5 个元素的列表。
LISP 常用的循环构造还包括 do*
、dolist
、dotimes
等。其中,do*
与 do
的区别是,do*
中定义的变量会被互相影响,前一个变量的值会影响后一个变量的初始化表达式;dolist
用于对列表中的每个元素做相同的操作;dotimes
用于对整数进行迭代。
此外,LISP 还提供了一些类似于迭代器的函数,可以用于对序列进行遍历或过滤。比如,map
、reduce
、filter
等函数都是常用的序列操作函数。
虽然 LISP 中没有像其他语言中的 for
或 while
循环语句,但是它仍然提供了许多强大的循环构造和序列操作函数,可以满足各种循环和迭代的需求。对于习惯了传统编程模型的程序员来说,可能会觉得 LISP 中的循环语法有些不直观,但是一旦适应了函数式编程的思维方式,就会发现 LISP 的循环构造也同样灵活和方便。