如何实现undo和redo的功能?

49 views
Skip to first unread message

seagate

unread,
Apr 24, 2012, 12:07:39 AM4/24/12
to Lisp-cn(Lisp中文用户组)
请教下各位大佬,如何用lisp实现如undo和redo的功能?有个办法是把命令保存成字符串,再用eval执行,感觉不是很合适。有没有更正规且灵
活的做法?

Liutos

unread,
Apr 24, 2012, 1:26:04 AM4/24/12
to lis...@googlegroups.com
是说写一个编辑器?

在 2012年4月24日 下午12:07,seagate <xiao.ao.j...@gmail.com>写道:
请教下各位大佬,如何用lisp实现如undo和redo的功能?有个办法是把命令保存成字符串,再用eval执行,感觉不是很合适。有没有更正规且灵
活的做法?

--
Lisp-cn(Lisp中文用户组)
CLUG http://lisp.org.cn



--
Liutos Love Linux LaTeX Lisp Ling

我的GitHub主页:https://github.com/Liutos

seagate

unread,
Apr 24, 2012, 2:58:05 AM4/24/12
to Lisp-cn(Lisp中文用户组)
嗯,编辑器算是比较典型的应用把。其实最想知道的是,怎么实现这种command的保存和调用。

On 4月24日, 下午1时26分, Liutos <mat.liu...@gmail.com> wrote:
> 是说写一个编辑器?
>

> 在 2012年4月24日 下午12:07,seagate <xiao.ao.jiang.hu...@gmail.com>写道:
>
> > 请教下各位大佬,如何用lisp实现如undo和redo的功能?有个办法是把命令保存成字符串,再用eval执行,感觉不是很合适。有没有更正规且灵
> > 活的做法?
>
> > --
> > Lisp-cn(Lisp中文用户组)

> > CLUGhttp://lisp.org.cn

Nala Ginrut

unread,
Apr 24, 2012, 4:20:30 AM4/24/12
to lis...@googlegroups.com
函数是一级对象,为什么不直接保存操作函数?操作保存成字符串是什么考虑?

2012/4/24 seagate <xiao.ao.j...@gmail.com>:

seagate

unread,
Apr 24, 2012, 4:26:47 AM4/24/12
to Lisp-cn(Lisp中文用户组)
嗯,但是函数需要用到的变量怎么保存?

On 4月24日, 下午4时20分, Nala Ginrut <nalagin...@gmail.com> wrote:
> 函数是一级对象,为什么不直接保存操作函数?操作保存成字符串是什么考虑?
>

> 2012/4/24 seagate <xiao.ao.jiang.hu...@gmail.com>:

Liutos

unread,
Apr 24, 2012, 4:29:05 AM4/24/12
to lis...@googlegroups.com
思路比较狭隘,说出来给大家听一下。我觉得不同类型的软件应该是用不同的操作来实现undo和redo的,以前做过一个绘图软件,我的室友实现了undo的功能,他的做法是在undo时直接把存储起来的图形元素给去掉,再重新绘制整个图像。如果是编辑器这类的软件,估计是对文本的增加或者修改进行记录,然后在undo的时候去掉上一次加入的文本这样吧……囧啊,总觉得这样很挫……

Jeova Sanctus Unus

unread,
Apr 24, 2012, 4:31:39 AM4/24/12
to lis...@googlegroups.com
不是有各种开源的编辑器么?

在 2012年4月24日 下午12:07,seagate <xiao.ao.j...@gmail.com> 写道:
> 请教下各位大佬,如何用lisp实现如undo和redo的功能?有个办法是把命令保存成字符串,再用eval执行,感觉不是很合适。有没有更正规且灵
> 活的做法?
>

seagate

unread,
Apr 24, 2012, 4:39:20 AM4/24/12
to Lisp-cn(Lisp中文用户组)
有点偏题了。我是想知道,怎么用lisp实现command buffer的效果。保存各种函数和函数使用的变量是重点,至于undo是拿来做个比
方。

On 4月24日, 下午4时31分, Jeova Sanctus Unus <jeova.sanctus.u...@gmail.com>
wrote:
> 不是有各种开源的编辑器么?

Jeova Sanctus Unus

unread,
Apr 24, 2012, 4:43:08 AM4/24/12
to lis...@googlegroups.com
看下emacs源代码?

Nala Ginrut

unread,
Apr 24, 2012, 4:44:37 AM4/24/12
to lis...@googlegroups.com
2012/4/24 seagate <xiao.ao.j...@gmail.com>:
> 嗯,但是函数需要用到的变量怎么保存?
>

状态信息可以存在闭包里,不需要通过参数传进去。

不过另一个方案是,undo-list可以实现为一个全局栈,使用circular-list可以实现循环栈。
这样就不需要传入状态变量来控制了。undo操作可以循环,也可以不必。

至于如何存储上一步的数据,就看你怎么设计了,编辑器的话,可以考虑结合页面元数据(colum/line等)来存储增量数据(比如diff那样的效果),如果不用增量方式那这个编辑器就开不了大文件了。
这个就不提供办法了,不然你做下去也没什么乐趣了。

Liutos

unread,
Apr 24, 2012, 6:44:18 AM4/24/12
to lis...@googlegroups.com
好奇问一下,``开不了大文件''具体是什么意思呢?

Nala Ginrut

unread,
Apr 24, 2012, 6:58:37 AM4/24/12
to lis...@googlegroups.com
2012/4/24 Liutos <mat.l...@gmail.com>:
> 好奇问一下,``开不了大文件''具体是什么意思呢?
>

如果不用增量,意味着每次操作都要有一份该文件的拷贝,所以如果有很大的文件就会很占内存。
而且文件很大的话undo一次时间很长。
这种看似弱智的方式也不是没人用,如果写个只针对小数据的undo的话,完全可以采用这种简单方式。

JIA Zhongye

unread,
Apr 24, 2012, 4:37:37 AM4/24/12
to lis...@googlegroups.com
潜水员冒个泡~
窃以为这里面应该区分“可逆操作”和“不可逆操作”。对于这两类操作,保存当前系
统的一个snapshot总是可以的。而对于“可逆操作”,当snapshot可能会很大时,保
存一个他的“逆过程”可能是比较经济的。举个例子,对于文本编辑器,增删一段话
就是可逆的,而对于图像编辑器打马赛克就是不可逆的。

--
Regards,
Zhongye

Nala Ginrut

unread,
Apr 24, 2012, 7:16:28 AM4/24/12
to lis...@googlegroups.com
恩...真的要讨论编辑器实现的话要聊的可就多了...

2012/4/24 JIA Zhongye <jia.z...@gmail.com>:

Xiaofeng Yang

unread,
Apr 24, 2012, 7:36:36 AM4/24/12
to lis...@googlegroups.com
虽然不知道LZ想要知道什么,不过通常undo/redo可以这样来实现:

* 首先,将所有用户操作封装成一个object。这个object可以是任意数据结构,只要能表达用户的操作,或者,用户操作所导致的改变,即可。
* 保存一个指针和一个用户操作列表(通常可以实现为一个链表),指针指向用户所进行的最后一个操作所对应的object。
* 用户进行了一个操作的时候,产生一个object来描述该用户的这个操作。用户操作完成后,将这个object添加到用户操作列表里面,就在当前指针所指向的操作之后。并且,改变当前指针,指向这个新的object。
* 如果要undo,那么将指针移动到当前指针所指向的object的前一个object,同时,根据这个object的信息,来将数据回退到当前操作之前的状态。
* 如果要redo,那么类似undo,指针向后移动一个对象。

P.S. 以上文字,可能有些含糊或者无法准确的表达意思。


     Best regards,
Xiaofeng Yang

seagate

unread,
Apr 24, 2012, 9:16:40 AM4/24/12
to Lisp-cn(Lisp中文用户组)
我想要学会lispy一点的写法和思维方式,用面向对象一类的语言实现这个对我来说不是问题,因此才来这里问问。本质不是想要问怎么实现redo,而是
用lisp怎么实现。
Nala的说法我也有想,不过不知道怎么做成循环命令,似乎undo和redo需要分成2个命令队列?

On 4月24日, 下午7时36分, Xiaofeng Yang <n.akr.aki...@gmail.com> wrote:
> 虽然不知道LZ想要知道什么,不过通常undo/redo可以这样来实现:
>
> * 首先,将所有用户操作封装成一个object。这个object可以是任意数据结构,只要能表达用户的操作,或者,用户操作所导致的改变,即可。
> * 保存一个指针和一个用户操作列表(通常可以实现为一个链表),指针指向用户所进行的最后一个操作所对应的object。
> *
> 用户进行了一个操作的时候,产生一个object来描述该用户的这个操作。用户操作完成后,将这个object添加到用户操作列表里面,就在当前指针所指向的操作之后。并且,改变当前指针,指向这个新的object。
> *
> 如果要undo,那么将指针移动到当前指针所指向的object的前一个object,同时,根据这个object的信息,来将数据回退到当前操作之前的状态。
> * 如果要redo,那么类似undo,指针向后移动一个对象。
>
> P.S. 以上文字,可能有些含糊或者无法准确的表达意思。
>
> Best regards,
> Xiaofeng Yang
>

> 在 2012年4月24日 下午7:16,Nala Ginrut <nalagin...@gmail.com>写道:
>
>
>
>
>
>
>
> > 恩...真的要讨论编辑器实现的话要聊的可就多了...
>

> > 2012/4/24 JIA Zhongye <jia.zhon...@gmail.com>:

Xiaofeng Yang

unread,
Apr 24, 2012, 9:48:27 AM4/24/12
to lis...@googlegroups.com
有这种想法就是你的不对了。
将这个程序用LISP写出来,遵守通常人们认为是lispy的规则,那么就是lispy的。


     Best regards,
Xiaofeng Yang


--
Lisp-cn(Lisp中文用户组)
CLUG http://lisp.org.cn

Nala Ginrut

unread,
Apr 24, 2012, 10:44:03 AM4/24/12
to lis...@googlegroups.com
2012/4/24 seagate <xiao.ao.j...@gmail.com>:

> 我想要学会lispy一点的写法和思维方式,用面向对象一类的语言实现这个对我来说不是问题,因此才来这里问问。本质不是想要问怎么实现redo,而是
> 用lisp怎么实现。
> Nala的说法我也有想,不过不知道怎么做成循环命令,似乎undo和redo需要分成2个命令队列?
>

redo可以指向undo-stack的头元素,对于lisp的话car就可以了。这样避免多余的数据结构。
对于FP来说,由于函数是一级对象,也就是说你可以把一次操作(带状态的函数)当成一个对象来存储,所以undo-list里面只需要存储一系列过程就可以了,不需要存储数据。或者你可以理解成建立一个函数指针类型的堆栈,只不过对于FP来说,由于函数是一级对象,所以不需要依靠函数指针来操作。
循环的话可以直接用circular-list,它相当于一般命令式语言中的循环链表,用它来作为stack,当然自己实现一个也行。
circular-list在Lisp中是否直接提供我不清楚,在Scheme中需要加载srfi-1。

只是想要为自己的代码加入Lispy风味的话,估计有一段时间的适应期才行,可能多写一些Lisp代码就能找到感觉。
怎么才能算够Lispy味,这是个主观的东西。拿排序来说,DP一般采用循环迭代,FP会选用尾递归/map/fold混用来实现,但很多人用Lisp也会用loop/while来写。用Alan
Perlis的话来说,这些都是语法糖,关注这些意义不大。当然,也不是一点用处都没有,至少可以显得很牛。

Nixie O

unread,
May 15, 2012, 8:32:17 PM5/15/12
to lis...@googlegroups.com
使用call/cc来实现redo吧!绝对lispy。

Liutos

unread,
May 15, 2012, 9:25:36 PM5/15/12
to lis...@googlegroups.com
续延啊续延……不过如果是续延实现的redo,那么每一次都是相同的文档内容做同样的事情?

使用call/cc来实现redo吧!绝对lispy。
--
Lisp-cn(Lisp中文用户组)
CLUG http://lisp.org.cn

Nixie O

unread,
May 16, 2012, 12:04:41 AM5/16/12
to lis...@googlegroups.com
如果pure functional的话,应该是的。
Reply all
Reply to author
Forward
0 new messages