首页 > 其他分享 >Emacs折腾日记(十一)——求值规则

Emacs折腾日记(十一)——求值规则

时间:2025-01-21 20:58:56浏览次数:1  
标签:ast lisp pRoot 节点 Emacs 求值 日记 表达式

截至到现在,我觉得我自己的elisp水平有了一定的提高,希望各位读者借助之前的文章也能有一些收获。现在已经可以尝试写一点elisp的程序了,但是如果想深入了解一下 lisp 是如何工作的,不妨先花些时间看看 lisp 的求值过程。

对于我这样一个日常使用C/C++的程序员来说,习惯了C/C++的语法和写法,初次见到lisp这样使用括号并且主要是S-表达式的语言,开始总会有点不习惯,但是在尝试自己写了这么些文章之后,对lisp有那么一点感觉。这篇我想就着 求值规则 这篇文章以及自己的一些理解来尝试梳理一下自己是如何理解elisp表达式的。

S表达式

要理解S表达式,我们先从如何解析四则运算开始。在之前我鸽了一个系列就是使用C来实现C语言解析器的系列。在那个系列中提到,一个普通的4则运算最终会生成一个抽象语法树,例如 a * b - (c + d) 最终可以生成如下的抽象语法树

        -
     /     \
   *         +
 /   \     /   \
a     b   c     d

二叉树的每个节点,或者是叶节点,或者有2个子节点,叶节点可以用来存储数据。而每颗子树的根节点存储操作符,或者说表示要对数据进行的操作,而如果操作符需要一个或者多个操作数,那么可以对抽象语法树进行调整。

可以用上面的图来表示树的话有些麻烦了,后来发明了点对表示法, 如果只关心叶子节点,每颗子树的根节点采用.来表示,那么这颗二叉树可以表示为 ((a . b) . (c . d)) 。看到这里各位读者想到了什么呢?cons cell。

S表达式是点对表示法的形式定义:

原子 -> 数字 | 符号
S表达式 -> 原子 | (S表达式 . S表达式)

所以,S表达式或者是原子,或者是递归的由其他S表达式构成的点对。

虽然抽象语法树可以使用这种点对来描述,但是语法树大了,点的数量大了,其实也挺麻烦的,所以lisp中有一些简单的写法。回顾一下之前学习列表和cons cell的知识,简化也就得到了列表,例如

'((a . b) . (c . d)) ⇒ ((a . b) c . d)
'((a . b) . (c . (d . nil))) ⇒ ((a . b) c d)

如果我们考虑这颗树的根节点,并且采用先序遍历的方式访问,结果仍然采用点对来表示,那么将得到这样的结果 (- (* a b) (+ c d))。 这样我们得到了计算这个四则运算的lisp代码,这个它可以作为列表,也可以让lisp解释器来执行。

到此为止,各位读者应该理解了S表达式。它其实就是对应了一颗语法树。现在看到S表达式也不那么恐惧了,解释器如何执行它似乎也慢慢的清晰起来了呢

S表达式的求值

理解了S表达式,再回过头来看看它的求值过程。

所有的表达式可以分为三种:符号、列表和其它类型。我们来分别说明

最简单的就是自求值表达式,前面说过数字、字符串、向量都是自求值表达式。还有两个特殊的符号 t 和 nil 也可以看成是自求值表达式。

第二种表达式是符号。符号的求值结果就是符号的值。如果它没有值,就会出现 void-variable 的错误。

第三种表达式是列表表达式。而列表表达式又可以根据第一个元素分为函数调用、宏调用和特殊表达式(special form)三种。根据上面对S表达式的理解,这里的第一个元素也就是放在语法树的每颗子树的根节点上,表示对它的子节点进行的操作,例如上面的加减乘除,或者使用car之类的函数。而它的子节点可以是一颗语法树,也可以是简单的值,对应在elisp中的话,就是这个操作可以针对上面两种自求值表达式或者符号值,也可以是另一个S表达式。

整个求值过程就是不断的求子树然后使用根节点来对子树进行操作,例如针对上面的二叉树可以写下这么一段伪代码来实现求值

float calc-ast(ast* pRoot)
{
	switch(pRoot->eOprType)
	{
	case function: //函数调用
		return function(calc-ast(pRoot->left), calc-ast(pRoot->right));
	case math: // 数学计算
		return calc-ast(pRoot->left) + calc-ast(pRoot->right); //这里以加法为例
		....
	default:
		calc-ast(pRoot->left);
		calc-ast(pRoot->right);
		break
	}
}

第一个元素如果是一个特殊表达式时,它的参数可能并不会全求值。这些特殊表达式通常是用于控制结构或者变量绑定。每个特殊表达式都有对应的求值规则。这个就根据具体的语法来定,例如 and 和 or 这些操作符具有短路的特性。

本文内容到此就结束了,本文比较简单,算是对之前的一个总结,对lisp有一个大概的了解。最后,本文可能是年前最后一篇文章了,在这里提前祝各位读者新年快乐!

标签:ast,lisp,pRoot,节点,Emacs,求值,日记,表达式
From: https://www.cnblogs.com/lanuage/p/18684408

相关文章

  • GoWVP 全栈开发日记[5]:使用 react-hook-form 完成表单
    GoWVP全栈开发日记[5]:使用react-hook-form完成表单服务端源代码https://github.com/gowvp/gb28181前端源代码https://github.com/gowvp/gb28181_web介绍GoWVP(GolangWebVideoPlatfrom)是一个Go语言实现的,基于GB28181-2022标准实现的网络视频平台,负责实......
  • 日记(练习)
    为了分别OI与日常,这里只会放些我认为比较好的题,其他题应当在学习笔记中。todolist多项式杂烩(doing)LCT?仙人掌(2024.9.18)一些较难的DP构造与ad-hoc博弈论(不会打表找规律):(推式子练习计算几何?PAM,广义SAMKummer定理2025.1.7在平面直角坐标系求......
  • 2025年安卓苹果手机有哪些好用的日记本app推荐?
    进入2025年,有很多人想要直接在手机上随手写每天的日记,那么安卓或苹果手机上有哪些好用的日记本app推荐呢?今天来介绍四款简单又好用的手机版写日记的app软件,总有一款是适合你的。一、手机系统自带便签/备忘录/笔记工具不管你用的是哪款手机,手机上都有系统自带的便签/备忘录/笔记......
  • 寒假学习日记8
    今天主要是有关服务器查看自己系统和版本 .使用uname命令uname命令可以提供关于系统的基本信息。查看操作系统名称:uname-o查看操作系统的版本和内核版本:uname-a要查看服务器的架构(即处理器架构),你可以使用以下几种方法:.使用uname-m命令uname-m会显......
  • 在电脑上记录工作内容和日记的软件哪款好用?
    想要在电脑上随手记录工作内容、日记琐事、待办事项、日程安排等,哪款软件简单好用呢?今天来介绍四款常用的电脑桌面记事软件,总有一款是你喜欢的?一、stickynotesStickyNotes是Windows系统自带的便签工具,也叫“便笺”。它以彩色便利贴的形式展现在电脑桌面上,能记录简单文字......
  • 【日记】于绝境中悟人生
    前言这篇随笔的标题或许有些夸张,但它的确来源于我这段时间生病时的感悟。生病总是痛苦的,而在痛苦中,思绪往往会飞向远方,去探寻生命与存在的意义。一、生命是脆弱的生命如歌,苦涩又充满变数。小时候,我们或许曾经无意间踩死过蚂蚁,伤害过其他小生命,常常忽略了它们的脆弱。然而,换个角......
  • 51单片机学习日记:独立按键控制LED灯
    我们可以再单片机上看到4个独立的按键,控制一个LED灯的亮灭先用第一个就可以,我们先找到独立按键的原理图来看看如何通过按键来控制LED灯的亮灭;独立按键的一段接地,另一端接P3,则我们按下按键时会使端口为低电平,我们就可以通过逻辑:当P31=0(按下去按键)时让灯亮,当P31=1时灯灭。(大......
  • 菜鸟成长日记-前后端环境配置(一)
    菜鸟成长日记-前后端环境配置(一)一、前端开发软件安装1.vscode软件安装2.vscode插件安装3.Google浏览器安装二、后端软件开发软件1.jdk安装2.Tomcat安装三、注意事项1.jdk安装之后无特殊情况不要删除2.Tomcat运行时乱码问题一、前端开发软件安装1.vscode软件安......
  • Emacs 折腾日记(九)——elisp 数组与序列
    elisp中序列是数组和列表的统称,序列的共性是内部数据有一个先后的顺序,它与C/C++中有序列表类似。elisp中的数组包括向量、字符串、char-table和布尔向量,它们的关系如下:在之前一章中已经介绍了序列中的一种类型——列表,本篇将介绍序列中的另外一种数据类型——数组数组简......
  • Linux学习日记(十四)——Linux系统中的信号
    Linux学习日记(十四)——Linux系统中的信号目录1基本概念2信号的分类  2.1非实时信号(传统信号)  2.2实时信号(非传统信号)3常见信号的默认行为(系统默认操作)4进程对信号的处理方式  4.1signal()函数  4.2sigaction()函数5如何向进程发送信号......