Lisp 匿名递归函数 v2:在 Common Lisp 中实现 Clojure 的 fn

简介: 我在之前的文章《Lisp 匿名递归函数》中提及 Emacs Lisp、Scheme 和 Common Lisp 中默认都没提供定义可递归的 lambda 函数的方法。并在文章里提供了我自己实现的 Emacs Lisp 版本和 Common Lisp 版本。在那之后,我学习了 Clojure,发现 Clojure 中的 fn 在定义 lambda 函数的同时还允许给它取一个临时的名字,这样就能

我在之前的文章《Lisp 匿名递归函数》中提及 Emacs Lisp、Scheme 和 Common Lisp 中默认都没提供定义可递归的 lambda 函数的方法。并在文章里提供了我自己实现的 Emacs Lisp 版本和 Common Lisp 版本。

在那之后,我学习了 Clojure,发现 Clojure 中的 fn 在定义 lambda 函数的同时还允许给它取一个临时的名字,这样就能在函数体中递归地调用自己了,比如下面用来临时求第12个斐波那契数的匿名函数:

((fn fibonacci [n]
   (if (<= 2 n)
     (+ (fibonacci (- n 1))
        (fibonacci (- n 2)))
     1))
 12)

这个方法比我之前实现的要高明的多!我的方法会额外“霸占”一个名字“this”来代表自己,这样很容易有命名冲突的问题。但像 fn 这样,名字由开发者自己提供,就能避免这样的问题。因此,我开始琢磨怎么把 fn 迁移到 Common Lisp 中。

有了之前开发的经验,这一次实现起来顺手多了。观察 fn 的语法,与 lambda 相比它多了一个可选的名字。所以,当函数名未提供时和 lambda 无区别:

(defmacro fn (&rest body)
  (if (listp (car body))
    `(lambda ,@body)
    ))

然后是 else 块,这部分和之前文章里介绍的一样,都是需要让返回的匿名函数能识别一个额外的函数名,并且那个函数名指向函数本身。区别仅是之前 hard code 成 this,而这回名字由开发者指定。所以,也是用“局部函数”来实现,即 Common Lisp 中的 labels,或 Emacs Lisp 中的 flet:

(defmacro fn (&rest body)
  (if (listp (car body))
    `(lambda ,@body)
    `(lambda (&rest args)
       (labels (,body)
         (apply (function ,(car body)) args)))))

来写个函数测试一下。比如输出一棵树的所有子节点:

(funcall
  (fn dump-list (o)
    (if (consp o)
      (dolist (item o)
        (dump-list item))
      (format t "~A~%" o)))
  '(1 (2 (3 (4) 5) 6) 7 (8 9)))

在 clisp 中执行结果如下:

λ clisp -q
[1]> (defmacro fn (&rest body)
  (if (listp (car body))
    `(lambda ,@body)
    `(lambda (&rest args)
       (labels (,body)
         (apply (function ,(car body)) args)))))
FN
[2]> (funcall
  (fn dump-list (o)
    (if (consp o)
      (dolist (item o)
        (dump-list item))
      (format t "~A~%" o)))
  '(1 (2 (3 (4) 5) 6) 7 (8 9)))
1
2
3
4
5
6
7
8
9
NIL
[3]>

目录
相关文章
|
11月前
|
存储 缓存 分布式计算
【赵渝强老师】基于RBF的HDFS联邦架构
最新版Hadoop实现了基于Router的联盟架构,增强了集群管理能力。Router将挂载表从客户端中分离,解决了ViewFS的问题。RBF架构包括Router和State Store两个模块,其中Router作为代理服务,负责解析ViewFS并转发请求至正确子集群,State Store则维护子集群的状态和挂载表信息。
253 0
|
SQL 安全 Windows
SQL安装程序规则错误解析与解决方案
在安装SQL Server时,用户可能会遇到安装程序规则错误的问题,这些错误通常与系统配置、权限设置、依赖项缺失或版本不兼容等因素有关
|
弹性计算 关系型数据库 MySQL
rds子网配置
在阿里云中配置RDS子网涉及五个关键步骤:1) 创建或选择VPC作为私有网络环境;2) 在VPC内创建子网并确保IP地址不重叠;3) 关联路由表和安全组以控制流量及访问权限;4) 创建RDS实例时指定VPC和子网;5) 确保ECS实例与RDS在同一VPC或配置相应跨VPC访问,并调整安全组规则。这样可保障RDS与其他资源的通信及网络性能。
295 6
|
算法 Python 计算机视觉
python五行代码解决滑块验证的缺口距离识别,破解滑块验证
目前网上关于滑块的缺口识别的方法很多,但是都不极简,看起来繁杂,各种算法的都有,有遍历的有二分法的,今天写个最简单,准确率最高的。 直接看代码: def FindPic(target, template):     """     找出图像中最佳匹配位置     :param target: 目...
4112 0
|
运维 关系型数据库 MySQL
MySQL5.7 GTID 运维实战
GTID 和 START SLAVE START SLAVE 语法 START SLAVE [thread_types] [until_option] [connection_options] thread_types: [thread_type [, thread_type] .
8695 0
|
3天前
|
云安全 数据采集 人工智能
古茗联名引爆全网,阿里云三层防护助力对抗黑产
阿里云三层校验+风险识别,为古茗每一杯奶茶保驾护航!
古茗联名引爆全网,阿里云三层防护助力对抗黑产
|
3天前
|
存储 机器学习/深度学习 人工智能
大模型微调技术:LoRA原理与实践
本文深入解析大语言模型微调中的关键技术——低秩自适应(LoRA)。通过分析全参数微调的计算瓶颈,详细阐述LoRA的数学原理、实现机制和优势特点。文章包含完整的PyTorch实现代码、性能对比实验以及实际应用场景,为开发者提供高效微调大模型的实践指南。
499 1