HY Lisp 读取宏(reader macro)学习

news/2024/10/5 22:37:54/

学习HY lisp语言的时候HY编程快速入门实践课第三章 HY入门-CSDN博客,学习到了读取(reader macro),尝试将其概念弄明白。

首先,读取是Lisp语言中都有的一种概念,所以可以通过任意一种Lisp语言的文档来学习。因为HY的文档比较简单(篇幅少),所以准备结合Common Lisp、On Lisp等来进行学习

在 Lisp 表达式的一生中,有三个最重要的时刻,分别是读取期(read-time),编译期(compile-time) 和运行期(runtime)。运行期由函数左右。给了我们在编译期对程序做转换的机会。而读取(read-macro),它们在读取期发挥作用。本段文字来源:第 17 章 读取(read-macro)_w3cschool

读到这里,终于明白了它为什么叫读取,原来是在读取时起作用的啊!

这样看来,读取就有点类似于定义(替换)了,比如单引号'就是quote,(quote a) 可以简写成'a 。Lisp里的普通也可以完成类似的工作,我们熟知的C语言里的定义,也是做了类似的工作。

那么为什么还要单独定义一个叫读取的概念呢? 这正是Lisp的的强大而独特之处:读取是可以嵌套的。比如(quote (quote a)) 可以简写成''a ,用普通实现替换,在嵌套的时候可能就会出错,而无法稳定实现功能。同样的C语言的定义也无法实现自己对自己的嵌套。

当然HY里对读取的实现没有像其它Lisp那么完全,比如它不能实现任意字符定义,而是需要像C的定义那样,以#开头。例子:

(defreader do-twice(setv x (.parse-one-form &reader))`(do ~x ~x))#do-twice (print "This line prints twice.")

然后就会看到print语句执行了两次:

=> #do-twice (print "This line prints twice.")
This line prints twice.
This line prints twice.
 

结合前面讲到的,Lisp在读取期(read-time),编译期(compile-time) 和运行期(runtime)三个时期都可以实现循环和嵌套,这极大的增强了Lisp的自由度,可以说Lisp是能力最强大的语言,这样或许不太准确,因为只要是图灵完备的语言,基本都能完成所有的任务,应该说Lisp是最接近人类思维的一种语言。我们想像一下,不管我们是读一个计划书,制定一个计划书,执行一个计划书的时候,是不是都可以重复(嵌套)我们的行为? 比如反复读,反复写,反复实践? 任何一个步骤如果有问题,都可以定位到该问题并反复执行? 

计算机最强大的地方就是快速重复工作,Lisp语言最强大的地方就是在任何时候,不管是读取、编译还是运行期,让计算机快速重复工作!

Lisp语言是最接近接近人类思维方式的一种语言,但是它的缺点也很明显,那就是它的代码是反人类思维,是最难读懂的一种语言。Basic语言(最开始的basic语言),能读懂英文的人,就大致能读懂程序。到了C语言和Pascal,不熟悉该语言语法的人,就有点难读懂,但结合注释,大致知道这一段代码是干什么的。而Lisp的代码,没学过的几乎肯定看不懂。我不知道Lisp高手过一段时间是否还能看懂自己写的代码,因为我还没到高手那个段位。我目前是连手册里的例子代码看起来都磕磕绊绊..... 

没有比较就没有伤害, 读完Lisp代码再看其它语言,不管是C还是汇编,都感觉清新可爱了呢,这也算是附加回报吧。

附录:

代码学习

(defreader matrix(.slurp-space &reader)(setv start (.getc &reader))(assert (= start "["))(.slurp-space &reader)(setv out [[]])(while (not (.peek-and-getc &reader "]"))(cond(any (gfor  c " \t"  (.peek-and-getc &reader c)))None(.peek-and-getc &reader "\n")(.append out [])True(.append (get out -1) (.parse-one-form &reader))))(lfor  line out  :if line  line))

这段代码是一个自定义的读取器(reader)函数,设计用于从一个输入源(如文件或字符串)中解析出嵌套列表(列表的列表)的结构。它采用了类似Scheme或Clojure中读取(reader macros)的语法和逻辑,但使用了Python风格的语法和假设的一些函数(如.slurp-space.getc.peek-and-getc.append.parse-one-form等),这些函数在标准的Python中并不直接存在,但可以理解为是某个自定义的读取器环境(可能是基于某种解析库或自定义实现的)提供的接口。下面是对这段代码的详细解析:

  1. 函数定义defreader matrix 定义了一个名为matrix的读取器函数。

  2. 跳过空白.slurp-space &reader 调用可能是为了跳过输入流(&reader)中的任何空白字符(如空格、制表符等),准备开始读取实际的列表内容。

  3. 读取起始符setv start (.getc &reader) 读取一个字符作为开始,并通过assert (= start "[")确认这个字符是左方括号[,表示一个列表的开始。

  4. 再次跳过空白:再次调用.slurp-space &reader以跳过可能的空白字符。

  5. 初始化输出列表setv out [[]] 初始化一个包含空列表的列表,用于存储解析出的嵌套列表。

  6. 循环读取内容:通过一个while循环,持续读取输入直到遇到结束符]。循环体内使用cond结构(类似于Lisp的cond或Python的if-elif-else)来处理不同的读取情况:

    • 如果读取到的字符是空白字符(通过gfor循环和any函数检查),则忽略该字符(None操作)。
    • 如果读取到换行符\n,则在out列表中添加一个新的空列表,以表示新的一行或新的子列表的开始。
    • 如果上述条件都不满足,则调用.parse-one-form &reader来解析当前位置的“一个表单”(可能是一个数字、字符串、列表等),并将其添加到当前子列表中。
  7. 返回结果:最后,通过一个lfor循环(可能类似于列表推导或for循环,但专门用于列表)遍历out列表,并只返回那些非空的子列表(通过:if line条件)。这一步可能是为了过滤掉由于换行符而额外添加的空列表。


http://www.ppmy.cn/news/1473409.html

相关文章

黑芝麻科技A1000简介

文章目录 1. A1000 简介2. 感知能力评估3. 竞品对比4. 系统软件1. A1000 简介

【ARM 常见汇编指令学习 7.1 -- LDRH 半字读取指令】

请阅读【嵌入式开发学习必备专栏】 文章目录 LDRH 使用介绍LDRH(Load Register Half-word)总结 LDRH 使用介绍 在ARMv9架构中,汇编指令LDRH用于从内存中载入数据到寄存器的指令,下面将分别对它进行详细介绍: LDRH&am…

spring-boot-starter-data-redis是否支持reactive响应式编程

开源项目SDK:https://github.com/mingyang66/spring-parent 个人文档:https://mingyang66.github.io/raccoon-docs/#/ spring-boot-starter-data-redis: 使用传统的基于阻塞的I/O编程模型,这意味着当你调用Redis操作时&#xff0…

手把手教你生成一幅好看的AI图片

很多人看到别人用SD生成出来的图片感到非常的羡慕,因为即使给了他们最好的SD软件,他们也是词穷,不知道该如何去描述要生成的图片。 别急,这篇文章会一步步的教会你怎么才能生成一个好看的AI图片。 跟着我,别走丢。 …

IOS17闪退问题Assertion failure in void _UIGraphicsBeginImageContextWithOptions

最近项目更新到最新版本IOS17,发现一个以前的页面突然闪退了。原来是IOS17下,这个方法 UIGraphicsBeginImageContext(CGSize size) 已经被移除,原参数如果size为0的话,会出现闪退现象。 根据说明,上述方法已经被替换…

在mfc程序中,如何用c++找到exe文件所在的路径

在 MFC&#xff08;Microsoft Foundation Class&#xff09;程序中&#xff0c;你可以使用 GetModuleFileName 函数来获取当前运行的可执行文件&#xff08;.exe&#xff09;的路径。 以下是一个示例代码&#xff1a; #include <afxwin.h> #include <iostream>in…

Python脚本:将Word文档转换为Excel文件

引言 在文档处理中&#xff0c;我们经常需要将Word文档中的内容转换成其他格式&#xff0c;如Excel&#xff0c;以便更好地进行数据分析和报告。针对这一需求&#xff0c;我编写了一个Python脚本&#xff0c;能够批量处理指定目录下的Word文档&#xff0c;将其内容结构化并转换…

SQL Server解决Float字段使用ISNULL时报错

SQL Server解决Float字段使用ISNULL时报错 一、前言1.报错内容2.解决案例 一、前言 1.报错内容 > [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]从数据类型 varchar 转换为 float 时出错。 (8114)。这个错误通常是由于SQL Server在执行ISNULL函数时遇到…