初识LISP(2)——结构、循环与函数

2024-02-08 12:32
文章标签 初识 函数 结构 循环 lisp

本文主要是介绍初识LISP(2)——结构、循环与函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


一、LISP—决策

Construct 描述
cond 这个结构是用于用于检查多个测试行动作条件。它可以嵌套if或其他编程语言语句。
if if结构有多种形式。在最简单的形式,它后面跟着一个测试条,测试操作和一些其它相应措施(次)。如果测试子句的值为true,那么测试的动作被执行,否则,由此产生的子句求值。
when 在最简单的形式,它后面跟着一个测试条和测试操作。如果测试子句的值为true,那么测试的动作被执行,否则,由此产生的子句求值。
case 这种结构实现了像cond 构造多个测试行动语句。但是,它会评估的关键形式,并允许根据该键的形式评价多个行动语句。

        cond结构允许实现分支,可以看出其中的条件测试和执行动作是一个整体,条件测试为真,则执行对应的动作。反之,~~

;决策——cond结构(setq a 30)
(cond ((> a 20) (format t "~%a is less than 20"))(t (format t "~%value of a is ~d" a)));如果前一句执行,则不执行这一句,直接默认条件成立


       if语句就是最基本的条件判断了,这里需要注意的是if-then和if-then-else的结构情况,具体看下面的例子。

;决策——if结构
(setq b 15)
(if (> b 20)(format t "~%b is more than 20");if语句满足时执行(format t "~%value of b is ~d" b))
; ;——if-then结构  
(setq b 15)
(if (> b 20)then (format t "~%b is less than 20"));if语句不满足时,执行then后的语句~~(format t "~%value of b is ~d" b)
;——if-then-else结构
(setq c 30)
(if (> c 20 );这里的结构并没有then和else的关键字(format t "~%c is greater than 20")(format t "~%c is less than 20"))
(format t "~%value of c is ~d" c)

      当没有循环时,when的作用类似于if的功能。
; ;决策——when结构
(setq d 100)
(when (> d 20)(format t "~%d is greater than 20"))
(format t "~%value of d is ~d" d)

     这里没有提到缺省情况下的结果,尝试了(setq day 8),结果没有输出也不报错~~
; ;决策——case结构
(setq day 6)
(case day
(1 (format t "~% Monday"))
(2 (format t "~% Tuesday"))
(3 (format t "~% Wednesday"))
(4 (format t "~% Thursday"))
(5 (format t "~% Friday"))
(6 (format t "~% Saturday"))
(7 (format t "~% Sunday")))

二、LISP—循环

Construct 描述
loop 循环loop结构是迭代通过LISP提供的最简单的形式。在其最简单的形式,它可以重复执行某些语句(次),直到找到一个return语句。
loop for loop结构可以实现一个for循环迭代一样作为最常见于其他语言。
do do 结构也可用于使用LISP进行迭代。它提供了迭代的一种结构形式。
dotimes dotimes构造允许循环一段固定的迭代次数。
dolist dolist来构造允许迭代通过列表的每个元素。

        loop就是最基本的迭代形式,但它自身没有终止的功能,需要借助when 、for和if等来实现迭代的终止。下面的这段代码,其中的when可以用if替换,正如我之前所说,when的功能类似于if。

;loop结构,其中利用when作为循环判断
(setq a 10)
(print a)
(loop(setq a (+ a 1))(print a);(terpri);作为一次换行(when (> a 20) (return a))
)


        loop-for可以理解为其他高级语言的for语句。下面的代码可以看出来,for-in语句是Python中的语法,所以说LISP是Python的祖师爷一点也不过分,Python还参考了LISP很多特性,就我目前发现的举一个例子。LISP中定义一个函数,可以在函数内部用两个双引号“ ”来包围该函数的特性及功能说明,这种设计在Python中也存在,即Python的__doc__方法,在IDLE中可以调用某类的__doc__方法,打印出对该类的说明。
;loop-for结构
;利用loop-for和do的循环结构
(loop for x in ' (study hard!)do(format t "~s" x))
;使用from-to结构
(loop for a from 10 to 20do(print a));print会自动换行,write不会
;假如if语句,增加条件
(loop for b from 10 to 30if(evenp b)do(print b))

    

         这里看上去似乎没有终止的关键字,实际上,do之后的第二部分就是条件测试部分,(= x y),判断x和y是否相等。

;do结构
(do ((x 0 (+ x 2))(y 20 (- y 2)));变量与变量的迭代过程需要再用括号括起来,以便区分测试中断的代码((= x y)(- x y));似乎后面的(- x y)并没有什么作用~~(format t "~%x = ~d  y = ~d" x y))
        既然给出了固定的迭代次数,也就没必要有终止的条件测试了。
;dotimes结构,给出固定的循环次数
(dotimes (n 10)(write n)(format t "  ")(write (* n n))(terpri))

     dolist构造的目的是将列表中元素逐一进行运算。不知道有没有注意到下面的注释,元素之间区分用空格,为什么会是这样呢?其他语言不都是用逗号吗?不要用“约翰·麦卡锡就是这样设计的”这种无脑的答案。好吧,其实这样的答案也没错,但再回想一下,之前定义名字(变量名、函数名等)时说了,可以使用特殊字符,那么逗号“,”也算咯,所以不小心用Python的编程思维将元素用逗号隔开,对于LISP编译器来说其实是一个原子(元素),这样想,以后应该也就不会再犯这样的错误了。
;dolist结构,逐个迭代列表中的元素,元素之间的区分用空格
(dolist (a ' (1 2 3 4 5 6 7 8 9))(format t "~%Number = ~d , Square = ~d" a (* a a)))

         下面的这段程序刚开始看感觉有点懵,不过认真一读,其实就是几个知识点的融合,block 、if 和函数定义defun。
;块返回,从允许从正常情况下的任何错误的任何嵌套块退出。
(defun demo-function (flag)(print 'entering-outer-block)(block outer-block(print 'entering-inner-block)(print (block inner-block(if flag(return-from outer-block 3);返回到外部块,返回3,但并不输出(return-from inner-block 5));退出内部块,返回5,其外层为print,故将5打印出来(print 'This-wil--not-be-printed)))(print 'left-inner-block)(print 'leaving-outer-block)t))
(demo-function t)
(terpri)
(terpri)
(demo-function nil)

输出结果为:

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 
5 
LEFT-INNER-BLOCK 
LEAVING-OUTER-BLOCK 
     

        正如第七行的注释所说,虽然返回到外部块为3,但并没有打印,所以输出不会显示3,如果修改为如下代码,增加了print,自然会打印出3。但是又会在最后一行增加打印了一个“T”。其实原本的代码中,外部块的最后一个“t”,我的理解是类似于“return 0”的功能,之后外部块被print包围,那么“t”就相当于一个字符被打印出来。其实这里有点模糊,打印出的字面字符,需要在前面加上一个单引号,但是实际上没有,尝试着加上单引号,结果还是一样。那么可以认为这个打印出的“T”不是字面意义上的字符,而是“TRUE”的意思。

(defun demo-function (flag)(print 'entering-outer-block)(print (block outer-block(print 'entering-inner-block)(print (block inner-block(if flag(return-from outer-block 3)(return-from inner-block 5))(print 'This-wil--not-be-printed)))(print 'left-inner-block)(print 'leaving-outer-block)t)))
(demo-function t)
(terpri)
(demo-function nil)

输出结果为:

ENTERING-OUTER-BLOCK
ENTERING-INNER-BLOCK
3

ENTERING-OUTER-BLOCK
ENTERING-INNER-BLOCK
5
LEFT-INNER-BLOCK
LEAVING-OUTER-BLOCK
T


三、LISP—函数

        这里给出的&optional,&rest,&key都是基于实参的灵活设定。函数最后一个表达式的结果作为函数的返回值,实际就是简化了函数的运算量,return-from的偏执我先不聊,以后发现了它的优点再说。

        lambda函数在我看来应该很重要,如果没记错的话,Java的jdk1.8中就添加了lambda这一特性,Python中当然也有。Python中的解释就是匿名函数,只是简单或是不常重复使用的函数运算,那么用匿名函数来代替函数的定义显得更加高效。时间和空间上的简化肯定是每个人都期望的。在维基百科中,LISP的闭包概念(词法闭包,或函数闭包)中,有一种说法是“闭包用来指代某些其开放绑定(自由变量)已经由其语法环境完成闭合(或者绑定)的lambda表达式,从而形成闭合的表达式”。所以单纯以匿名函数的概念来定义lambda函数,似乎有些肤浅了。

        而映射函数,我的注释应该可以说清楚了。

;函数——可选参数,&optional之后的形参对应的实参可有可无
(defun show-members (a b &optional c d) (write (list a b c d)));这里&optional的作用是如若之后的实参没有,则返回NIL
(show-members 1 2 3)
(terpri)
(show-members 'a 'b 'c 'd)
(terpri)
(show-members 'a 'b)
(terpri)
(show-members 1 2 3 4)
(terpri)
;(show-members 'a );这个就会报错,说至少应该有两个参数

;函数——其余部分参数,&rest之后的形参对应的实参可以为空,或者为单个、多个元素,总之以列表形式得出
(defun show-members(a b &rest c) (write(list a b c)))(show-members 1 2 3)(terpri)(show-members 'a 'b)(terpri)(show-members 'a 'b 'c 'd 'e)(terpri)(show-members 1 2 3 4 5 6 7 8 9)

;关键字参数,可以将实参与指定的形参对应,很强势
(defun show-members(&key a b c d) (write (list a b c d)))(show-members :a 3 :b "asa" :d 'k );单个字符值需要在前面加上单引号,不然编译器会以为是变量名;;(show-members :a (1 2) );尝试过了,编译器要求实参为符号,不能是列表

;函数的最后一个表达式作为函数返回值的值
(defun add-all(a b c d)(+ a b c d))
(setq sum (add-all 10 20 30 40))
(write sum)
(terpri)
(write (add-all 23.4 56.7 34.9 10.0))

;通过return-from强势返回其要求的返回值
(defun my-fun(num)(return-from my-fun 10)(write 20));当有return-from时,这一句就不会被执行(write (my-fun 30))

;lambda函数
(write ((lambda (a b c d)(+ (* a b) (/ c d)))2 3 10 5));lambda表达式和实参是分离的,各自用括号括起来

;映射函数,将实参列表逐一进行运算,若在函数内部运算的另一部分也是列表,
;那么运算结果以实参列表与内部运算列表的一一映射的结果为准
(write (mapcar '1+ '(1 2 3)))
(terpri)
(write (mapcar '+ '(1 2 3 4 5 6) '(1 2 3)))
(terpri)
(defun cube-my-list(num)(mapcar #' (lambda(x) (* x x x x)) num));强调,lambda与传入的参数是分离的,分离的,分离的
(write (cube-my-list '(1 2 3 4 5)))







这篇关于初识LISP(2)——结构、循环与函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/691054

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

poj3750约瑟夫环,循环队列

Description 有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。 Input 第一行输入小孩的人数N(N<=64) 接下来每行输入一个小孩的名字(人名不超过15个字符) 最后一行输入W,S (W < N),用

Linux操作系统 初识

在认识操作系统之前,我们首先来了解一下计算机的发展: 计算机的发展 世界上第一台计算机名叫埃尼阿克,诞生在1945年2月14日,用于军事用途。 后来因为计算机的优势和潜力巨大,计算机开始飞速发展,并产生了一个当时一直有效的定律:摩尔定律--当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。 那么相应的,计算机就会变得越来越快,越来越小型化。

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据