美文网首页
欢迎来到Lisp

欢迎来到Lisp

作者: Saru様 | 来源:发表于2016-12-13 21:52 被阅读0次

    0. Lisp系统的交互式前端(REPL)


    刚接触Lisp,会发现每种Lisp的实现都会带有REPL(read - eval - print -loop)。
    这里记录一下REPL相关的说明及问题。

    • 顶层(toplevel)
      在任何lisp系统中的交互式前端(repl),叫做顶层

    • 中断循环(break loop)
      如果输入了一些Lisp无法理解的东西,它会打印出一个错误信息,然后进入一个叫做
      中断循环的顶层。
      中断循环给予有经验的程序员一个找出错误原因的大集会,不过对于初学者可能只能
      跳出这个循环了。
      每个Lisp实现的REPL的中断循环跳出的方式不通,请根据提示使用。

    1. 形式(Form)


    Lisp中使用S表达式进行程序的书写。通过左括号( 表示表达式开始,右括号 )表示表达式结束。
    正式因为使用了这种表达式,所以Lisp的语法十分简单,因为除了像1这种原子外,都是括号中的表达式。

    2. 求值(Evaluation)


    Lisp使用前缀表达式,类似(+ 1 2)这种形式。括号内的第一个元素是函数,后面的就是函数的参数。

    Lisp中使用quote作为一种保护表达式不被求值的方式,quote也可以缩写为'

    3. 数据(Data)


    Lisp提供了在其他预言中找的到的及找不到的数据类型。如,整数(integer)、字符串(string)等其他语言中可以找到的数据类型。还有如,符号(symbol)与列表(lists)等,在其他语言中找不到的数据类型。

    • 符号(通常) 不对自己身求值,所以需要使用quote引用它。

    • 列表是由被括号包裹住的零个或多个元素来表示的。括号内的元素可以是任何类型。
      使用列表不许要引用(quote或者其缩写'),不然Lisp会以为是一个函数调用。

    • list函数可以用来创建列表。但不同的是,由于list是函数,所以它的实参自然会被求值。

    4. 列表操作(List Operations)


    • cons函数用来构造列表

    • car函数获取列表的第一个元素

    • cdr函数获取列表除第一个元素外的所有元素

    • third函数可以获取列表的第三个函数,通过的还有first、second等等。

    5. 真与假(Truth)


    在Common Lisp中,t表示逻辑真nil表示逻辑假
    将返回值逻辑真逻辑假的函数称为谓词(predicate)。所以谓词的名字通常以p结尾。

    • listp函数判断是否为列表。
    • null函数判断是否为空(假)。
    • not函数对参数进行逻辑非判断。
    • if条件,通常接受三个表达式,一个test表达式,一个then表达式和一个else表达式。若test为逻辑真则对then表达式求值,并返回表达式的值。
      否则,对else表达式求值,并返回表达式的值。
      if的else实参是选择性的,如果不写,默认是nil。
    • and宏对任意参数进行逻辑与判断。
    • or宏对任意实参进行逻辑或判断

    6. 函数(Functions)


    使用defun定义新函数,Lisp不对程序(program)过程(procedure)函数(function)做区别。

    7. 递归(Recursion)


    函数自己调用自己称为递归

    • eql函数(谓词)测试两个实参是否相等
    • member测试第一个实参(元素)是否为第二个实参(列表)中的成员。

    8.阅读Lisp(Reading Lisp)


    使用缩进来阅读及编写程序,不要只盯着括号看。任何好的编辑器都会又括号匹配的功能。

    9. 输入输出(Input and Output)


    • format格式化输出
    • read标准输入函数

    10. 变量(Variables)


    • let可以创建新的局部变量,在特定的上下文中使用。
      let表达式有两个部分
      第一部分为一组创建新变量的指令,形式为(variable expression)。
      第二部分为一个有表达式的函数体,在函数体内最后一个表达式的求值结果为let的返回值。

    • numberp(谓词)用来测试实参是否为一个数字

    • defparameter用来创建一个全局变量,全局变量的命名通常以开始,并以结束。

    • defconstant用来创建全局常量。不需要给全局常量一个独一无二的名字,因为如果相同名字存在,会产生重复定义的错误。

    • boundp函数用来检查实参是否为全局变量全局常量

    11. 赋值(Assignment)


    • setf是赋值操作符,可以给全局或局部变量赋值。
      setf的第一个实参可以是表达式变量名
      如果setf的第一个实参是符号(symbol),并且不是某个局部变量的名字,则setf会把这个符号设为全局变量。也就是隐式的创建全局变量。
      但是还是推荐使用defparamter明确地创建全局变量。

    12.函数式编程(Functional Programming)


    函数式编程意味着撰写利用返回值工作的程序,而不是修改程序中的某些东西。换言之,只使用函数的返回值,而不使用函数的副作用。

    函数式编程允许使用交互式测试(interactive testing)

    13.迭代(Iteration)


    当想要重复做一些事情的时候,迭代比递归来的更自然。

    • do宏是Common Lisp中最基本的迭代操作符。

      • 第一个实参是一组变量说明列表,(variable initial update)。
        其中variable是符号,initial与update是表达式。变量会被赋予initial表达式的值 作为初始值。每一次迭代,都会被赋予update表达式的值。
      • 第二个实参包含一个货多个表达式。其中,第一个表达式用来测试迭代是否结束。
        而之后的表达式则被依次求值,直到迭代结束。其中最后一个值会被当作do的返回值返回。
      • 其余的实参则组成了循环的函数体。每次迭代,都会被依序求值。
    • progn接受任意数量的表达式,依序求值,并返回最后一个表达式的值。

    • dolist接受该形式的实参(variable expression),并在后边跟着一个具有表达式的函数主体。函数主体会被求值,而变量相继与表达式所返回的列表元素绑定。

    14. 函数作为对象(Functions as Objects)


    Lisp中,函数与符号、字符串一样是第一类对象。

    • function特殊操作符返回接受实参关联的对象,缩写为#',称为升引号(sharp-quote)。
      升引号后接函数名便可以将函数当作实参传给函数,function +#'+

    • apply接受一个函数和一个实参列表,并返回把传入函数应用到实参列表的结果。
      apply接受任意数量的实参,最后一个是列表即可。

    • funcall与apply的效果相同,只是不需要把实参包装成列表。

    • lambda创建匿名函数使用 (funcall #'(lambda (x) (+ x 100)) 1)

    15. 类型(Types)


    在Common Lisp中,数值才是又类型的,而变量没有。
    类型又fixnumintegerrationalreal、numberatomt
    其中t是所有类型的基类(supertype)

    • typep函数接收一个对象和一个类型,然后判断对象是否为该类型

    习题(Exercises)


    • (a) 14
    • (b) '(1 5)
    • (c)7
    • (d)'(nil 3)
    • (cons 'a '(b c))
    • (cons 'a (cons 'b '(c)))
    • (cons 'a (cons 'b (cons 'c ())))
    (defun our-get-fourth (lst)
            (car (cdr (cdr (cdr lst)))))
    
    (defun our-max (first-num second-num)
          (if (> first-num second-num)
            first-num second-num))
    
    • (a)遍历x,遍历完毕后,返回一个nil
    • (b)在y中查找x,如果找到,则返回元素在列表中的位置,如果没有找到则返回nil。
    • (a)x为car就可以得到相同的结果
    • (b)x为or可以得到相同的结果
    • (c)x为apply可以得到相同的结果
      (defun our-list-element (lst)
        (if (null (car lst))
          nil
          (if (listp (car lst))
            t
            (our-list-element (cdr lst)))))
    
    • (a)
      • 递归版本
        (defun print-point-rec (num)
          (if (zerop num)
            'done
            (progn
              (format t "~A" ".")
              (print-point-rec (- num 1)))))
        
      • 迭代版本
        (defun print-point-ite (num)
          (do ((i num (- i 1)))
            ((zerop i) 'done)
            (format t "~A" ".")))
        
        
    • (b)
      • 递归版本
        (defun count-list-rec (lst)
          (if (null lst)
             0
            (+ 1
               (count-list (cdr lst)))))
        
      • 迭代版本
        (defun count-list-ite (lst)
          (let ((len 0))
            (dolist (obj lst)
              (setf len (+ 1 len)))
              len))
        
    • (a)该函数接受一个数字列表是没问题,但是如果接受的是空列表或字符列表,则会报错。

    • (b)该函数没有结束条件,不论传入何种列表都会死循环。

    • 更改后为

        (defun summit-second (lst)
           (let ((x (car lst)))
             (if (null x)
             0
             (if (numberp x)
               (+ x (summit-second (cdr lst)))
               (summit-second (cdr lst))))))
    

    相关文章

      网友评论

          本文标题:欢迎来到Lisp

          本文链接:https://www.haomeiwen.com/subject/lbavmttx.html