精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-07-04
循环和递归Emacs Lisp有两种方式循环执行语句:使用while循环,或者使用递归。 whilewhile测试它的第一个参数的值,如果为false,解释器将不会执行语句的body部分。如果为true,解释器将执行语句的body部分,然后重新测试第一个参数的值,开始下一轮循环。 while语句模板如下: (while true-or-false-test 使用while循环如果while语句的true-or-false返回为true则body部分被执行。 对while求值的返回值是true-or-false-test的值。有趣的是while循环执行时如果没有发生错误将返回nil或false,而不管循环执行了多少次。while语句执行成功也不会返回true。 while循环和list通常使用while循环来测试一个list是否包含了元素。如果有循环就执行,如果没有了循环就结束。这是一项重要的技术,下面将举例说明。 最简单的测试list是否有元素的方法是执行这个list:如果没有元素,则会返回空list,(),它与nil或false同义。如果有元素则将 返回这些元素。因为Emacs Lisp把任何蜚nil值当作true,如果把有元素的list作为while的判断条件,将使循环执行。 例: (setq empty-list ())对empty-list求值将返回nil。 (setq animals '(gazelle giraffe lion tiger))如果把animals作为while循环的条件,如: (while animals当while检查它的第一个参数时,变量animals被执行,它将返回一个list。由于这个list不为nil,while将把这个值当作true。 为了防止while进入无限循环,需要一些机制来逐渐的清空list。一个常用的方法就是将传递给while语句的list替换为原来的list的CDR。每次都使用cdr函数,这样list将变短,最后list将变为空的list。这时while循环结束。 例如,上面的绑定到animals变量可以用下面的语句设置为原始list的CDR。 (setq animals (cdr animals)) 使用while和cdr函数的模板如下: (while test-whether-list-is-empty 例:print-elements-of-list(setq animals '(gazelle giraffe lion tiger))执行上面的代码,回显区将显示: giraffe 在循环中使用自增计数器模板: set-count-to-initial-value 自增计数的例子计算三角型中星号的数量,参数为层数,比如四层的三角型: *函数定义如下: (defun triangle (number-of-rows) ; Version with使用: (triangle 4)第一行的结果为10,第二行的结果为28。 在循环中使用自减计数器模板: (while (> counter 0) ; true-or-false-test 自减计数的例子仍以上面的三角型为例,计算1到任意层的星号总数。 函数定义的第一版: ;;; First subtractive version.然而,我们并不需要number-of-pebbles-in-row。 当执行triangle函数时,符号number-of-rows将被绑定到初始的值上。这个数值可以在函数体内作为局部变量被修改,而不用担心会 影响函数外部的值。这是Lisp中一个非常重要的特性;这意味着变量number-of-rows可以用于任何使用了number-of-pebbles -in-row的地方。 函数第二版如下: (defun triangle (number) ; Second version. 简单来说,正常情况下while循环包含三个部分:
使用dolist和dotimes节约时间dolist和dotimes都是为循环提供的宏。在某些情况下比直接使用while循环简单一些。 dolist与在while中循环取list的CDR的方法类似,它在每次循环中自动取CDR截短list,并将截短后的list的CAR绑定到它的第一个参数上。 dotimes循环可以指定循环的次数。 dolist宏举例来说,如果你想将一个list倒序排列,可以用reverse函数,例如: (setq animals '(gazelle giraffe lion tiger)) 这里演示了如何使用while循环实现倒序: (setq animals '(gazelle giraffe lion tiger)) 也可以用dolist宏实现: (setq animals '(gazelle giraffe lion tiger)) 在这个例子中,使用已存的reverse函数当然是最好的。第一个使用while循环的例子里。while先检查list是否有元素;如果有,它将 list的第一个元素添加到另一个list(它的第一个元素是nil)的第一个位置。由于添加元素的顺序是反的,因此原来的list被倒序排列了。 在使用while循环的语句中,(setq list (cdr list))语句截短了list,因此while循环最后停止了。在循环体中用cons语句创建了一个新的list。 dolist语句与while语句类似,dolist宏自动完成了在while语句中所写的一些工作。 while循环与dolist实现的两个方法不同之处在于dolist自动截短了list。'CDRs down the list'。并且它自动将CAR截短了的list的CAR赋给dolist的第一个参数。 dotimes宏dotimes宏与dolist类似,但它可以指定循环次数。 dotimes的第一个参数是每次循环的计数器,第二个参数是循环次数,第三个参数是返回值。 举例来说,下例将number绑定到从0开始的数字,但不包含3,然后构造出一个包含3个数字的list。 (let (value) ; otherwise a value is a void variabledotimes的返回值是value。 下面是一个使用defun和dotimes实现的triangle函数: (defun triangle-using-dotimes (number-of-rows) 递归递归函数使用不同的参数来调用自身。尽管执行的代码是相同的,但它们不是在同一线程执行。(不是同一个实例) 递归的组成一个递归函数通常包含下面三个部分: 1. 一个true-or-false-test决定是否再次调用函数,在这里被称为do-again-test。 2. 函数名称。当这个函数被调用时,一个新的函数实例产生了,并被分配任务。 3. 一个函数语句,它在每次执行时返回不同的值。这里称为next-step-expression。这样,传递到新的函数实例的参数前与传递给前一个函数实例的参数不同。这将使得在执行了正确有循环次数后,条件语句do-again-test的值为false。 使用递归函数的简单模式如下: (defun name-of-recursive-function (argument-list) 递归函数每次执行时将产生一个新的函数实例,参数告诉了实例要做什么。一个参数被绑定到next-step-expression。每个实例执行时都有一个不同的next-step-expression。 next-step-expression的值被用于do-again-text。 next-step-expression的返回值被传递给新的函数实例,由它来决定是否停止或继续。next-step-expression被设计为在不需要循环后它能使do-again-test返回false。 do-again-test有时被称为停止条件(stop condition),因为它将在测试值为false时停止循环。 在list上使用递归下面的例子使用了递归打印list中的各个元素。 (setq animals '(gazelle giraffe lion tiger)) 用递归代替计数器前面章节说过的triangle函数可以用递归修改为: (defun triangle-recursively (number) 在递归中使用cond前一节中的triangle-recursively使用了if。它也可以使用cond,cond是conditional的缩写。 尽管cond不像if那样使用得很普遍,但它还是比较常见的。 使用cond的模板如下: (condbody部分是一连串的list。 更完整的模板如下: (cond 当解释器执行cond语句时,它先执行body区的第一个语句的第一个元素。 如果true-or-false-test返回nil,则那个list的其它部分将不会执行。程序转到list串中的下一个list。当一个 true-or-false-test的返回值不为nil,则那条语句的其它部分将会执行。如果list串包含多个list,则它们依次执行并返回最后一 个语句的值被返回。 如果没有一个true-or-false-test的返回值为true,则cond语句返回nil。 使用cond实现的triangle函数: (defun triangle-using-cond (number) 递归模式下面是3个常用的递归模式。 every在every模式的递归中,动作将在list的每个元素上执行。 基本模型如下:
例如: (defun square-each (numbers-list)如果number-list为空,则什么也不做。如果它有内容,则通过递归构造一个list各个元素乘方值的list。 前面介绍过的print-elements-recursively函数,是另一个every模式的递归,不同的是这里使用了cons合并元素。 (setq animals '(gazelle giraffe lion tiger))print-elements-recursively函数的处理流程:
accumulateaccumulate递归模式,在每个元素上都执行动作,动作的执行结果与对下一个元素执行操作的结果进行累积。 这与在every模式中使用cons类似,只是不是使用cons,而是使用其它的方式合并。 工作模式如下:
例如: (defun add-elements (numbers-list) keep在keep递归模式中,list中的每个元素被测试,如果被操作的元素符合要求或者对元素的计算结果符合要求则保存该元素。 这与every模式也很类似,只是在这里如果元素不符合要求则被忽略。 这种模式的三个部分:
例如: (defun keep-three-letter-words (word-list) 无延时的递归这部分讲解了如何将递归函数拆分成多个函数部分(比如:初始化函数、辅助函数),减少递归函数body部分的判断,使得递归函数本身只需要处理好递归操作,提高了递归函数的执行速度。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-07-19
整理一下哦
可以合成一篇......................方便下载 -_-# |
|
返回顶楼 | |
发表时间:2007-07-19
准备在读完后再整理一下合成一篇,与大家分享!
|
|
返回顶楼 | |
浏览 5963 次