函数(functions)


在 Lisp 中,函数分两种:有名函数和匿名函数(lambda函数)。


有名函数 defun

有名函数的标准定义格式为:

(defun <name> (list of arguments)
"docstring"
(function body))

在函数中,返回值是函数主体中的最后一个表达式的结果。与大部分语言不同的是,lisp 中的函数没有 "return xx" 这样的语句用来声明返回值。例如:

(defun hello-world ()
;;
(print "hello world!"))

调用 hello-world 函数:

(hello-world)
;; "hello world!" <-- output
;; "hello world!" <-- a string is returned.

参数

  • 必需参数

    与大部分语言相同,函数中需要定义一些必需参数:

    (defun hello (name)
    "Say hello to `name'."
    (format t "hello ~a !~&" name))
    ;; HELLO

    调用 hello

    (hello "me")
    ;; hello me ! <-- this is printed by `format`
    ;; NIL <-- return value: `format t` prints a string to standard output and returns nil.
  • 可选参数:&optional

    可选参数定义在 &optional 这个关键词后面,且这些参数是有序的。如:

    (defun hello (name &optional age gender) ...)

    调用时需要这样调用:

    (hello "me") ;; a value for the required argument, zero optional arguments
    (hello "me" "7") ;; a value for age
    (hello "me" 7 :h) ;; a value for age and gender
  • 关键词参数:&key

    通常情况下,要记住函数中参数的顺序很不方便,所以就引入了关键词参数 &key <name>。通过 :name <value> 这样的方式来传递参数。如果关键词 name 的值没有设置的话,默认为 nil

    (defun hello (name &key happy)
    "If `happy' is `t', print a smiley"
    (format t "hello ~a " name)
    (when happy
    (format t ":)~&"))

    所以,我们可以这样调用 hello

    (hello "me")
    (hello "me" :happy t)
    (hello "me" :happy nil) ;; useless, equivalent to (hello "me")

    但是 (hello "me" :happy) 是非法的。

    注:在关键词参数中,如果调用其他的关键词时,会报错,这是可以通过 &allow-other-keys 来修复。

    (defun hello (name &key happy)
    (format t "hello ~a~&" name)) (hello "me" :lisper t)
    ;; => Error: unknow keyword argument
    (defun hello (name &key happy &allow-other-keys)
    (format t "hello ~a~&" name)) (hello "me" :lisper t)
    ;; hello me
  • 默认参数

    有时,需要将函数的某个参数设置一个默认值,这样 hello 就可以这样定义

    (defun hello (name &key (happy t)) ...)

    这样,调用 hellohappy 的值默认就是 t 了。

    • 不定参数:&rest

    在不确定参数的个数时,可以使用 &rest <variable> 这样的方式来定义,其中 &rest 后面的参数会当作一个 list 来处理。

    (defun mean (x &rest numbers)
    (/ (apply #'+ x numbers)
    (1+ (length numbers))))
    (mean 1)
    (mean 1 2)
    (mean 1 2 3 4 5)

返回值

在 Lisp 中,函数的返回值就是函数主体中最后一个表达式执行的结果。也有非标准的 return-from <function name> <value> 这样的语句,但是大部分情况下用不到。同时,Common Lisp 支持返回多个值。

返回多个值有三个关键词:valuesmultiple-value-bindnth-value

返回多个值不是将所有的结果都放入一个元组或列表中,这是很常见的概念混淆。

  • values

    (defun foo (a b c)
    a)
    (foo :a :b :c)
    ;; :A
    (defvar *res* (foo :a :b :c))
    ;; :A
    (defun foo (a b c)
    (values a b c))
    (foo :a :b :c)
    ;; :A
    ;; :B
    ;; :C
    (setf *res* (foo :a :b :c))
    ;; :A

    从上面代码可以看出,如果 foo 返回的是一个列表的话,那么 res 的值将会是 (:a :b :c) 这样一个列表,而不是 :A 这个值。

  • multiple-value-list

    该关键词的作用是将返回的多个值组合成一个列表

    (multiple-value-list (values 1 2 3))
    ;; (1 2 3)
  • values-list

    values-listmultiple-value-list 相反,它返回的是列表中的每个元素

    (values-list '(1 2 3))
    ;; 1
    ;; 2
    ;; 3

匿名函数 lambda

匿名函数的声明如下:

(lambda (x) (print x))

匿名函数的调用:

((lambda (x) (print x)) "hello")
;; hello
  • funcallapply

    (funcall #'+ 1 2)
    (apply #'+ '(1 2))
  • 返回函数的函数

    (defun adder (n)
    (lambda (x) (+ x n)))
    ;; ADDER (adder 5)
    ;; #<CLOSURE (LAMBDA (X) :IN ADDER) {100994ACDB}> (funcall (adder 5) 3)
    ;; 8

    上面示例中,(adder 5) 返回的是一个匿名函数。但是需要使用 funcall 关键词来调用,不能想正常函数调用来调用。

    ((adder 3) 5)
    In: (ADDER 3) 5
    ((ADDER 3) 5)
    Error: Illegal

    Common Lisp 中提供了两个函数来查看变量或函数是否赋值/绑定:boundpfboundp

    ;; The symbol foo is bound to nothing:
    CL-USER> (boundp 'foo)
    NIL
    CL-USER> (fboundp 'foo)
    NIL
    ;; We create a variable:
    CL-USER> (defparameter foo 42)
    FOO
    * foo
    42
    ;; Now foo is "bound":
    CL-USER> (boundp 'foo)
    T
    ;; but still not as a function:
    CL-USER> (fboundp 'foo)
    NIL
    ;; So let's define a function:
    CL-USER> (defun foo (x) (* x x))
    FOO
    ;; Now the symbol foo is bound as a function too:
    CL-USER> (fboundp 'foo)
    T
    ;; Get the function:
    CL-USER> (function foo)
    #<FUNCTION FOO>
    ;; and the shorthand notation:
    * #'foo
    #<FUNCTION FOO>
    ;; We call it:
    (funcall (function adder) 5)
    #<CLOSURE (LAMBDA (X) :IN ADDER) {100991761B}>
    ;; and call the lambda:
    (funcall (funcall (function adder) 5) 3)
    8

    注:在 Lisp 中,变量名和函数名可以相同,因为 Common Lisp 中变量和函数并不是存储在一起的,而是分开存储的。

闭包(Closure)

闭包,就是让一个函数可以使用一个 词法绑定(lexcial bindings)On Lisp 中的定义为:函数和一组变量的绑定的组合(a combination of a function and a set of variable bindings)。 Let Over Lambda中对闭包的解读为:一个保存了词法的环境(a saved lexical environment)。 可以将闭包理解为 C 语言中的 结构体(struct)或者面向对象语言(Java/C++)中的 类(class)

(let ((limit 3)
(counter -1))
(defun my-counter ()
(if (< counter limit)
(incf counter)
(setf counter 0)))) (my-counter)
0
(my-counter)
1
(my-counter)
2
(my-counter)
3
(my-counter)
0

类似的

(defun repeater (n)
(let ((counter -1))
(lambda ()
(if (< counter n)
(incf counter)
(setf counter 0))))) (defparameter *my-repeater* (repeater 3))
;; *MY-REPEATER*
(funcall *my-repeater*)
0
(funcall *my-repeater*)
1
(funcall *my-repeater*)
2
(funcall *my-repeater*)
3
(funcall *my-repeater*)
0

Lisp-02: 函数的更多相关文章

  1. JavaScript进阶系列02,函数作为参数以及在数组中的应用

    有时候,把函数作为参数可以让代码更简洁. var calculator = { calculate: function(x, y, fn) { return fn(x, y); } }; var su ...

  2. Python函数02/函数的动态参数/函数的注释/名称空间/函数的嵌套/global以及nolocal的用法

    Python函数02/函数的动态参数/函数的注释/名称空间/函数的嵌套/global以及nolocal的用法 目录 Python函数02/函数的动态参数/函数的注释/名称空间/函数的嵌套/global ...

  3. JavaScript基础精华02(函数声明,arguments对象,匿名函数,JS面向对象基础)

    函数声明 JavaScript中声明函数的方式:(无需声明返回值类型) function add(i1, i2) {             return i1 + i2;//如果不写return返回 ...

  4. 02函数-05-generator(ES6)

    generator(生成器)是ES6标准引入的新的数据类型. generator看上去像一个函数,但可以返回多次,除了return语句,还可以用yield返回多次.定义方式如下: function* ...

  5. 02函数-04-箭头函数(ES6)

    ES6新增的函数:Arrow Function,定义方式就是一个箭头 箭头函数相当于匿名函数,并且简化了函数定义,和匿名函数最大的区别在于其内部的this不再"乱跑",而是由上下文 ...

  6. e lisp 自定义函数

    自定义函数 (defun multi-by-seven (number) "multi number by seven" (interactive "p") ( ...

  7. JavaScript语言精粹 笔记02 函数

    函数函数对象函数字面量调用参数返回异常给类型增加方法递归作用域闭包回调模块级联套用记忆   函数 1 函数对象 在JS中函数就是对象.对象是“名/值”对的集合并拥有一个连接到原型对象的隐藏连接.对象字 ...

  8. JS 02 函数

    函数 一.创建函数 1.function 函数名( 形参列表 ){ 函数体 } 2.var 函数名 = function( 形参列表 ) { 函数体 } 3.var 函数名 = new Functio ...

  9. Scala基础篇-02函数与代码块

    1.block 代码块也是表达式,其最终求得的值是最后一个表达式的值. {exp1;exp2} { exp1 exp2 } 2.function def funtionName(param:Param ...

  10. 4.Scala语法02 - 函数

随机推荐

  1. 安装archlinux的另辟蹊径的命令及心得

    先说说我为什么开始入坑archlinux的吧,我最喜欢这个系统的一点就是简洁,DIY程度高,可以定制真正属于自己的专用系统.(像gentoo的话,就为了日常使用也没必要那么折腾,除非你是想在折腾的过程 ...

  2. 数据结构之HashMap

    前言 在我们开发中,HashMap是我们非常常用的数据结构,接下来我将进一步去了解HashMap的原理.结构. 1.HashMap的实现原理 HashMap底层是基于Hash表(也称“散列”)的数据结 ...

  3. Simulink仿真入门到精通(三) Simulink信号

    3.1 Simulink信号概述 所谓信号,表示一种随着时间而变化的量,在时间轴上的采样时刻都对应有数值. 信号在Simulink中是相当重要的组成部分,有线(line)表示,在模型中穿针引线地将各模 ...

  4. python3.4.3 连接Oracle生成报表并发送邮件

    python很简单,又很实用.当有需求时用起来会更有方向,大可不必从语法.循环等基础看起. 由于工作需要,每天要拉一份报表发给业务的同事,先是用SSIS做了个包部署到服务器上,每天定时拉报表发邮件给同 ...

  5. Web Scraper 高级用法——利用正则表达式筛选文本信息 | 简易数据分析 17

    这是简易数据分析系列的第 17 篇文章. 学习了这么多课,我想大家已经发现了,web scraper 主要是用来爬取文本信息的. 在爬取的过程中,我们经常会遇到一个问题:网页上的数据比较脏,我们只需要 ...

  6. [日志分析]Graylog2进阶 通过正则解析Nginx日志

    之前分享的 [日志分析]Graylog2采集Nginx日志 主动方式 这篇文章介绍了Graylog如何通过Graylog Collector Sidecar来采集nginx日志. 由于日志是未经处理的 ...

  7. window 查看端口 杀端口

    最近写项目,总是出现端口被占用的问题,原来傻傻的把电脑重启一下,终于有一天受不了了,想要想办法解决.刚开始从网上找了好多教程,发现不行.开始自己尝试,终于,成功的将占用端口的进程杀掉.在此记录下过程( ...

  8. Zend Studio 13.6.1 汉化及安装方法详解

    Zend Studio 13.6.1是一套专业开发人员使用的集成开发环境 (IDE),具备功能强大的专业编辑工具和调试工具,支持PHP语法加亮显示,支持语法自动填充功能,支持书签功能,支持语法自动缩排 ...

  9. Python Django撸个WebSSH操作Kubernetes Pod

    优秀的系统都是根据反馈逐渐完善出来的 上篇文章介绍了我们为了应对安全和多分支频繁测试的问题而开发了一套Alodi系统,Alodi可以通过一个按钮快速构建一套测试环境,生成一个临时访问地址,详细信息可以 ...

  10. IOS抓包工具Stream——让移动端的抓包变得轻而易举

    有一天下晚班回家,在地铁上的时候,开发发来信息说,能不能把之前创建的bug再抓包看下数据.顿时心里就想,在地铁上,我上哪抓包去.之后百度了下,发现ios有一款非常实用的抓包工具,大家可以上App St ...