📜  LISP 中的循环构造(1)

📅  最后修改于: 2023-12-03 14:44:00.747000             🧑  作者: Mango

LISP 中的循环构造

在 LISP 中,由于其函数式编程的特性,没有像其他语言中的 forwhile 循环这样的语句。LISP 中的循环构造均通过将函数嵌套调用来实现。

loop

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

do 是 LISP 中更加灵活的循环构造,可以支持多个循环变量,以及循环变量的不同取值方式。其基本格式如下:

(do ((var1 init1 step1)
     (var2 init2 step2)
     ...
     (varn initn stepn))
     (termination)
   body)

其中:

  • var1var2、...、varn:循环变量,可以有一个或多个,用括号括起来,中间用空格隔开。
  • init1init2、...、initn:循环变量的初始化表达式,用于对循环变量进行初始化。
  • step1step2、...、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 中。

mapcar

除了 loopdo,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*dolistdotimes 等。其中,do*do 的区别是,do* 中定义的变量会被互相影响,前一个变量的值会影响后一个变量的初始化表达式;dolist 用于对列表中的每个元素做相同的操作;dotimes 用于对整数进行迭代。

此外,LISP 还提供了一些类似于迭代器的函数,可以用于对序列进行遍历或过滤。比如,mapreducefilter 等函数都是常用的序列操作函数。

总结

虽然 LISP 中没有像其他语言中的 forwhile 循环语句,但是它仍然提供了许多强大的循环构造和序列操作函数,可以满足各种循环和迭代的需求。对于习惯了传统编程模型的程序员来说,可能会觉得 LISP 中的循环语法有些不直观,但是一旦适应了函数式编程的思维方式,就会发现 LISP 的循环构造也同样灵活和方便。