《elisp 每日一练》——撤销树与原子化编辑

每个编辑器都有撤销与反撤销的操作。Emacs中也不例外。我们今天来介绍Emacs中撤销相关的操作

知识铺垫

buffer-undo-list 的理解

在Emacs中,撤销操作使用undo、而反撤销使用的是undo-redo。它们比较容易理解,也没什么参数。但是今天我们深入理解建立在这两个函数背后的数据维护以及基于此的现场保护与还原。

在Emacs的底层,为每个buffer都维护了一个 buffer-undo-list 的结构,用于记录该buffer的修改历史。Emacs将每一次的操作,包括文本内容的变化以及文本属性的变化作为一条记录插入这个结构中。

我们想象一下,如果我们设计一个编辑器,用户有操作,例如当前有一个 hello 的字符,用户删除了了最后一个o,此时buffer变成了 hell。后面有加了一个 word 单词那么我们会怎么记录呢?有的编辑器会记录每一次改动之后buffer的内容。例如:

  • 第一次用户输入完了,此时保存一条 hello 记录
  • 第二次用户编辑,删除了 o,此时添加一条 hell 记录
  • 第三次用户编辑,插入 word 这个单词,此时添加一条 hellword 记录。

后续每一次undoredo 操作都是在向前或者向后遍历记录列表,恢复buffer。

这是一个思路,但是Emacs采用的是另一个思路,它不记录当前buffer是什么样子,它只记录每次在哪里,做了哪些改动。我们查看 buffer-undo-list,它的每一条记录主要内容如下:

  • (START . END) 用于记录一次插入操作产生的文本范围,undo 时删除这个范围
  • string 类型的 undo record 主要用于记录被删除的文本。
  • nil 中间可能会插入一个有nil的节点,表示一个 撤销边界 ,一次 undo 通常会处理一个 change group,也就是从当前 undo 位置向后处理 undo records,直到遇到下一个 boundary

从上面的一些结构我们就可以看出一些执行undo 时的思路,还是以上面的例子为了,假设我们将 hello 改成了 hellworld,那么Emacs会记录一个类似于 ((5 . 9) "o") 的结构,下一次undo时,删除buffer中point从5到9这个位置的字符,然后在后面插入一个 “o” 字符串。

但是节点只是有可能包含这些,并不一定每个节点都是一样的结构。根据操作的不同,可能每个节点会出现不同的数据。它比较复杂因为Emacs支持针对 undo 的操作千奇百怪,除了普通文本插入、删除和文本属性修改之外,undo 系统还需要处理 point、marker、文本属性以及 region 中的 selective undo 等复杂情况,因此 undo record 的结构并不是固定的。

为什么我会将 undo 之后的 redo 作为它支持的一种 undo 操作呢?这与Emacs的另一种设计思路有关。常规的编辑器可能将 undoredo 视为不同的操作,可能维护两个列表或者像上面那样undo、redo将当前状态在列表中往不同的方向移动。但是Emacs不这么认为,它认为redo是另一种形式的文本修改,与我们手动修改文本没什么两样,所以传统 Emacs undo 机制并没有把“undo”和“redo”设计成两个相互独立的历史栈。一次 undo 本身会修改 Buffer,因此它也会影响 undo 历史。

例如,有三次修改记录 A->B->C。如果我们执行两次 undo 操作,回到了A状态,此时历史大致变成了

A->B->C->(undo)B->(undo)A

如果我们中间插入一些非undo 操作,哪怕只是移动光标,Emacs会将这些记录插入,变成固定的记录。如果我们再执行undo,那么就变成了再往回走,变成 undo A 的 undo,就能回到B,同理再次执行undo 就回到了初始状态的 C

从某种程度上来说,几次undo实际上能回到redo上。所以Emacs早期并没有提供专门的 redo 函数,现代的Emacs(28+)虽然提供了 undo-redo 来执行反撤销,但是这个函数只是为了与其他编辑器有相同的体验,它的内部仍然采用的是 buffer-undo-list 的实现方式

因为 buffer-redo-list 的结构比较复杂,每一项数据也都不固定,所以我们实际上无法通过像当初介绍 mark-ring 那样打印 mark-ring 来深入理解。如果你想要深入理解,可以参阅 undoprimitive-undoundo-more

undo-boundary

前面提到 nil 实际上组成了一个 boundary 边界,每一次执行撤销实际是以这个边界为准,回到了边界的位置。例如我们输入hello world 可能记录了第一次插入 h、第二次插入 e、第三次插入 l 等等操作。但是执行撤销之后它并没有每次删除一个字符,它可能直接把整个 hello world 字符串都给删除了。这个就是 boundary 在起作用

从某种程度上来说,这个机制简化了多次输入undo进行撤销的尴尬。但是Emacs默认给的 boundary 并不一定好用,例如hello word 我想改成 hello emacs,我希望我通过 undo 撤销后面的单词,我直接输入 emacs 。但是Emacs把整个字符串都删除了。只是这种简单的错误还好,如果是输入了一大段文本,我发现最后一两个词用的不准确想要重写,结果 undo 之后,整段都没了,用户心态可能直接原地爆炸了。

好在Emacs提供了 undo-boundary 让我们自己提供这个边界。

我们可以尝试一下下面两个函数

(defun my/insert-text()
  (interactive)
  (goto-char (point-max))
  (insert "hello")
  (insert " world"))

Emacs 默认会在一个命令执行前后插入一个 boundary,将执行前后的状态分开,所以上面一个函数执行之后,如果再执行 undo 会将整个插入的 hello world 都清空

(defun my/insert-text()
  (interactive)
  (goto-char (point-max))
  (insert "hello")
  (undo-boundary)
  (insert " world"))

下面一个函数,我们手动调用 undo-boundary ,在两次插入字符串之间插入了一个 boundary,这样调用 undo 只会删除后面插入的 world

原子化操作

实际上,用户的编辑操作千变万化,不光是Emacs,甚至连我们自己都无法事先预料到,我下一次undo想要把buffer恢复成什么状态。那么Emacs提供 undo-boundary 有什么用呢?实际上它常用于我们上面的标题说的 原子化操作 这样一种模式。

很多插件都会改变buffer的内容,比如各种mode的语法高亮、代码的格式化。它们本质上就是修改了buffer、产生了一条条的undo记录。但是如果插件中途报错,导致这个变化只变了一半,甚至有的变化来不及完全变更导致根本就是错的,就很影响用户体验。这个机制就像我们学习数据库的时候,提到的事务回滚。

提到这个,我们至今为止了解了各种级别的保存与恢复现场。save-excursionsave-restrictionwith-current-buffer。原子化操作的本质和要解决的问题与上面几个大致先当。同样的也有对应的宏进行处理 atomic-change-group。它主要做两件事:

  • 将宏包裹的buffer 操作打包成一个undo 单元,并且中间的操作不产生新的记录,用户事后反悔了可以一次undo回到之前的状态
  • 如果中途出错了,能回到原来的状态

练习

今天的练习,我们基于之前markdown标题转org mode标题的例子,在上面添加关于恢复现场的操作。

(defun my/markdown-title-to-orgmode-title()
  (interactive)
  (save-excursion
     (save-restriction
       (widen)
       (goto-char (point-min))
       (let ((header-regex (rx
			    line-start (group (repeat 1 6 "#")) (one-or-more space))))
	 (atomic-change-group
	   (while (re-search-forward header-regex nil t)
	     (let* ((hashes (match-string 1))
		    (has-count (length hashes))
		    (start (make-string has-count ?*))
		    (replace (concat start " ")))
	       (replace-match replace t t))))))))

总结

本节我们了解了Emacs的undo机制,同时介绍了如何进行原子化操作,本节介绍的api如下:

  • undo
  • undo-redo
  • undo-boundary
  • atomic-change-group
内容概要:本文聚焦于分布式传感器网络中的LEACH(Low-Energy Adaptive Clustering Hierarchy)聚类算法,系统研究其在能量消耗建模网络生命周期优化方面的性能表现,并结合Matlab代码实现完整的仿真分析流程。研究深入剖析LEACH协议的核心机制,即通过周期性选举簇头节点实现能量负载的均衡分布,从而有效延长网络整体生存时间。内容涵盖传感器节点部署优化、通信能耗模型构建、路由策略设计及能量耗尽过程的动态模拟,重点解决传统LEACH算法中存在的簇头分布不均、能耗集中于特定区域等缺陷。文档不仅提供了LEACH及其改进算法的仿真案例,还拓展至智能优化算法、机器学习、信号处理等多学科交叉应用方向,体现了该研究在物联网、边缘计算和无线传感网络领域的广泛适用性科研价值。; 适合人群:具备一定编程基础和科研能力,熟悉Matlab仿真环境,从事无线传感器网络、物联网、智能优化算法等相关领域的研究生或科研人员。; 使用场景及目标:①用于无线传感器网络中能量高效路由协议的设计优化;②通过Matlab仿真实现LEACH算法及其改进版本的性能对比分析;③支撑科研论文复现、算法验证教学演示;④为分布式系统中的能耗均衡问题提供解决方案参考。; 阅读建议:建议读者按照文档提供的目录结构系统学习,重点关注LEACH算法的核心机制能量模型构建,结合所提供的Matlab代码进行仿真实践,并参考网盘资源中的完整案例以加深理解。同时可拓展至其他优化算法通信协议的研究,提升综合科研能力。
内容概要:本文针对电力系统中考虑N-1故障集的安全约束经济调度(SCED)问题,提出了一种兼顾系统安全性经济性的优化建模方法,并提供了基于Matlab的代码实现。N-1故障集指系统中任一关键元件(如输电线路或发电机)发生故障退出运行的情形,确保在此类故障下系统仍能安全稳定运行是调度决策的核心要求。所构建的SCED模型在满足功率平衡、机组出力能力和线路传输容量等基本物理约束的基础上,进一步引入N-1故障后的安全校验约束,通过优化算法求解出一组既能维持系统安全稳定又可实现发电成本最小化的机组调度方案。该研究对于提升电网韧性、保障供电可靠性以及支撑现代电力系统的安全经济运行具有重要的理论价值实践意义。; 适合人群:具备电力系统分析、运筹优化理论基础及Matlab编程能力的高校研究生、科研人员和电力行业相关技术人员。; 使用场景及目标:①深入理解安全约束经济调度(SCED)的基本原理数学建模流程;②掌握N-1安全准则在优化模型中的具体建模方法实现逻辑;③获取可复现、可调试的Matlab代码实例,用于教学示范、科研复现或作为进一步开发复杂调度模型的基础。; 阅读建议:建议读者结合文档内容配套代码,重点关注模型中关于N-1故障场景的处理机制约束构建方式,通过逐步调试仿真分析,深化对目标函数、决策变量多重安全约束之间耦合关系的理解。
代码下载链接: https://pan.quark.cn/s/a4b39357ea24 在信息技术领域中,人工智能(AI)已经演变成一个至关重要的分支,特别是以深度学习和神经网络为代表的技术正持续促进科技的发展。本文将聚焦于“人工智能学习路线图”这一核心议题,详细剖析相关知识点,以协助学习者构建一个系统化的知识体系。 从标题入手,“人工神经网络_1、深度学习_1、数学基础_1、深度学习之外的人工智能_1”,这四个关键部分构成了人工智能学习的核心框架。 1. **人工神经网络**:作为人工智能领域的基础,该技术通过模拟人脑神经元的工作机制来构建模型。神经网络包含输入层、隐藏层和输出层,借助权重的调节来处理信息,从而达成分类、识别或预测等任务。掌握神经网络的构造、反向传播方法、激活函数(例如sigmoid、ReLU)以及损失函数(比如均方误差、交叉熵)是学习该领域的基础。 2. **深度学习**:深度学习属于机器学习的一个子集,它借助多层神经网络来识别复杂的模式。深度学习的优势在于能够处理高维数据,例如图像、声音和文本。卷积神经网络(CNN)在图像识别领域效果显著,而循环神经网络(RNN)和长短期记忆网络(LSTM)则适合处理序列数据。除此之外,生成对抗网络(GAN)等生成模型技术也值得关注。 3. **数学基础**:深度学习和神经网络的理论支撑依赖于数学。线性代数通过矩阵运算和特征分解,为理解神经网络的优化过程提供了支持;微积分和梯度下降构成了优化算法的基础,特别是在反向传播中的参数调整;概率论统计学是理解和构建模型的关键,如贝叶斯定理和最大似然估计;此外,还涉及到优化理论(比如牛顿法、拟牛顿法)和凸分析。 4. **深度学习之外的人工智能**:人工智能的应用范...
源码直接下载地址: https://pan.quark.cn/s/eda4370d97f3 溪谷H5游戏平台联运系统V3.0是一款为H5游戏运营人员量身定制的高效、稳固且具备多种功能的管理解决方案。联运平台是网络游戏领域中常见的商业模式,它允许多个合作方共同推广同一款游戏,并通过利润分配的方式共同享有收益。借助这一系统,开发团队运营商能够方便地处理游戏、用户、渠道、财务等多个核心领域,达成迅速发布和高效执行的目标。 1. **系统架构** - **前端框架**:该系统或许运用了如React或Vue.js等现代前端技术,确保用户能够获得顺畅的操作体验。 - **后端框架**:可能依托于Node.js、PHP或Java等后端技术,负责执行业务逻辑和数据交流。 - **数据库**:一般会采用MySQL或MongoDB等数据库管理系统来保存用户资料、游戏数据及运营数据统计等信息。 2. **核心功能** - **游戏管理**:系统应能够支持H5游戏的上传、发布、更新,并对游戏状态进行监控,包括游戏的发布、下架、版本迭代等操作。 - **渠道管理**:联运平台需要整合多种推广渠道,例如微信、QQ、浏览器等,并对每个渠道的推广成效进行监测和分析。 - **用户管理**:涵盖用户注册、登录、个人信息维护,以及用户行为数据的收集和剖析。 - **财务管理**:提供详尽的收入报告,包括渠道分成、充值记录、提现请求等,有助于运营商进行账目审核。 - **推广活动**:支持创建和管理各类营销活动,如限时优惠、新手礼包、积分兑换等,以提升用户活跃度和付费转化率。 - **统计分析**:提供即时的运营数据统计,例如DAU(日活跃用户)、ARPU(每用户平均收入)、留存率等,助力优化运营策略...
内容概要:本文研究了一种兼顾功率均分电能质量恢复的微电网抗拒绝服务(DoS)攻击的混合动态事件触发二次控制策略,并通过Simulink仿真实现。针对微电网在通信链路遭受DoS攻击时可能出现的信息中断通信资源受限问题,提出一种混合动态事件触发机制,在有效降低通信频率、节约带宽资源的同时,保障控制系统的稳定运行信息一致性。该策略能够在实现电压和频率精确恢复的同时,确保各分布式电源之间实现高精度的有功无功功率分配,显著提升孤岛微电网在异常通信环境下的鲁棒性、可靠性和控制经济性。仿真结果充分验证了所提方法在应对周期性或随机性DoS攻击时仍能保持优异的动态响应性能控制精度,有效解决了传统控制策略在攻击下易出现功率失衡电能质量恶化的问题。; 适合人群:具备电力系统、自动化或相关领域基础知识,从事微电网控制、分布式能源管理、电力电子智能电网研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于孤岛微电网在面临网络攻击时的二次控制设计;②解决因通信资源受限或受扰导致的控制性能下降问题;③实现功率精确分配电能质量协同恢复的综合控制目标; 阅读建议:建议结合Simulink仿真模型深入理解控制逻辑事件触发机制的设计细节,重点关注抗DoS攻击的能力验证部分,并可拓展至其他类型的网络攻击场景进行对比研究。
内容概要:本文档是AUTOSAR标准中关于端到端(E2E)通信保护协议的技术规范,详细定义了用于保障汽车电子系统间安全相关数据传输完整性的多种E2E配置文件(Profiles)。文档涵盖了E2E协议的功能架构、各类保护机制(如CRC校验、计数器、数据ID、源ID、消息类型长度检查等),并针对不同通信模式(信号型、面向服务的事件/客户端-服务器架构)提供了相应的实现方案。文中还描述了多个E2E Profile的具体结构行为流程,包括P01至P22以及新增的P76等,明确了各Profile的数据头布局、错误检测能力及状态机管理机制,并规定了API接口和配置参数,支持在不同通信中间件(如SOME/IP)中集成应用。此外,文档附带了变更历史、使用指南安全要求说明。; 适合人群:从事汽车电子系统开发、功能安全(ISO 26262/ASIL)相关的软件工程师、嵌入式系统架构师、车载通信协议开发者及AUTOSAR平台技术人员;具备C/C++编程基础和对车载网络(CAN/Ethernet/SOME/IP)有一定理解的研发人员尤为适用。; 使用场景及目标:① 在车载分布式系统中实现高可靠的安全相关数据通信保护,防止数据篡改、丢失、重放或路由错误;② 根据具体应用场景选择合适的E2E Profile进行配置集成,满足ASIL D等级的功能安全需求;③ 开发支持E2E保护的通信中间件或适配层,确保跨ECU数据交换的完整性一致性。; 阅读建议:本资源技术性强,涉及大量底层协议细节状态机逻辑,建议结合AUTOSAR其他基础模块文档(如R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值