乐趣区

关于lisp:Elisp-12兔子洞

前言:不知多久能学会 Elisp

上一章:动静模块

从本章开始,进入这份 Elisp 教程的第三局部。这部分内容侧重于利用,在假使不得不引入没学过的 Elisp 语法之时,则阐明所偏重的利用必然是好的。

一个游戏

当初思考来写一个很简略的小游戏:在一个一维的世界里寻找兔子洞。

该如何示意这个一维世界呢?用缓冲区便可示意。该如何在这个世界里行走呢?在缓冲区内挪动插入点。

在这个世界里,如何示意兔子洞呢?这须要三思。

缓冲区变量

我想用两个既不是全局变量也不是局部变量的变量来示意兔子洞的入口和进口。在 Elisp 语言里,这样的变量能够是缓冲区变量。上面是这两个变量的定义:

(setq hole-entrance "@#")
(setq hole-exit "#@")

(progn
  (find-file "world.txt")
  (make-local-variable 'hole-entrance)
  (make-local-variable 'hole-exit))

尽管 hole-entrancehole-exit 一开始是定义成了全局变量,然而在 Elisp 解释器执行了 find-file 之后,以后缓冲区便是寄存 world.txt 文件内容的缓冲区,这两个变量在 make-local-variable 的作用下,就变成了以后缓冲区内的变量。

任何一个缓冲区都能间接应用同一个全局变量,也将它变成本人的变量,然而各个缓冲区都会感觉本人能够独占它,批改它的值,而且这种批改对其余缓冲区没有任何影响。就像是一个人同时呈现在多个世界里,他不晓得其余世界里的本人是怎么的境况。

简略的世界

当初,曾经有了兔子洞的入口和进口的示意模式了,因而能够为上述的游戏轻易结构一个世界:

********@# 一个兔子洞 #@********@# 又
一个兔子洞 #@**********************
****
@#
                   这也是一个兔子洞
#@****************

这个世界里,有三个兔子洞。我将这个世界保留在文本文件 world.txt 里,待 Elisp 程序应用 (find-file) 将其载入缓冲区。接下来的工作便是如何在寄存这个世界的缓冲区内找出这些兔子洞。

search-forward

分明了要解决的问题是什么,还有分明本人有哪些资源能够利用以及如何利用,此二者的充沛联合,便诞生了算法。要找出兔子洞,能够利用 Elisp 函数 search-forward,同之前用过的 re-search-forward 类似,可在缓冲区内前向递进搜寻与指定文本相匹配的文本,只是后者反对正则表达式匹配。在寻找兔子洞的过程中,不须要用正则表达式,因而用 search-forward 更为适宜。

当初试试 search-forward

(setq hole-entrance "@#")
(setq hole-exit "#@")

(progn
  (find-file "world.txt")
  (make-local-variable 'hole-entrance)
  (search-forward hole-entrance))

Elisp 解释器对上述程序的求值后果是 11,这正是第一个兔子洞入口 @# 之后的地位,亦即 search-forward 返回的后果是缓冲区内与 hole-entrance 的值匹配的文本的开端地位。

如果 search-forward 在缓冲区内未能发现与指定文本相匹配的文本,默认状况下它会报错。我感觉动不动就报错,太大题小作了,找不到就找不到,让它返回 nil 就好了,这须要将 search-forward 的第三个参数设为 t,即

(search-forward hole-entrance nil t)

要设置第三个参数,不得不设置第二个参数。从当初开始要记住,Elisp 函数的参数容许可选,然而假使想设置其中的某个参数,然而又不知它后面的可选参数如何设置的时候,就将它们设为 nil 即可,因为 nil 是所有变量的默认值,函数的参数是变量。一旦 search-forward 可能返回 nil,便能够通过条件表达式,去接管这种查找失败的状况。

因为 search-forward 不仅能返回缓冲区内匹配文本的开端地位,同时也将插入点挪动到这一地位,因而 search-forward 便持续搜寻下一个兔子洞,亦即

(progn
  (find-file "world.txt")
  (make-local-variable 'hole-entrance)
  (make-local-variable 'hole-exit)
  (if (search-forward hole-entrance nil t)
      (search-forward hole-entrance nil t)))

能够发现第二个兔子洞的入口。

同理,上面的代码能发现第三个兔子洞的入口:

(progn
  (find-file "world.txt")
  (make-local-variable 'hole-entrance)
  (make-local-variable 'hole-exit)
  (if (search-forward hole-entrance nil t)
      (if (search-forward hole-entrance nil t)
          (search-forward hole-entrance))))

搜寻全副的兔子洞

上一节重复使用 search-forward 搜寻兔子洞入口的过程显然是一个典型的迭代过程,因而能够用 while 表达式简化,即

(load "newbie")

(setq hole-entrance "@#")
(setq hole-exit "#@")

(progn
  (find-file "world.txt")
  (make-local-variable 'hole-entrance)
  (make-local-variable 'hole-exit)
  (let (@-extrance)
    (while (setq @-entrance (search-forward hole-entrance nil t))
      (princ\n (format "兔子洞入口:%d" @-entrance)))))

还记得 newbie.el 库和 princ\n 宏吗?遗记了,就再回顾一下后面第 10 章的内容吧!

执行这个程序,在我的机器上的输入为

Loading /home/garfileo/.my-scripts/elisp/newbie.el (source)...
兔子洞入口:11
兔子洞入口:28
兔子洞入口:67

留神,setq 能够用作条件表达式,因为它可返回变量绑定的值,要么是 nil,要么不是 nil,后者与 t 等价。

对上述程序略作批改,

(load "newbie")

(setq hole-entrance "@#")
(setq hole-exit "#@")

(progn
  (find-file "world.txt")
  (make-local-variable 'hole-entrance)
  (make-local-variable 'hole-exit)
  (let (@-extrance @-exit)
    (while (progn
             (setq @-entrance (search-forward hole-entrance nil t))
             (setq @-exit (search-forward hole-exit nil t)))
      (princ\n (format "兔子洞 (%d %d)" @-entrance @-exit)))))

便可找出所有兔子洞的入口和进口了,因为在我的机器上的输入后果是

Loading /home/garfileo/.my-scripts/elisp/newbie.el (source)...
兔子洞 (11 18)
兔子洞 (28 37)
兔子洞 (67 98)

结语

游戏远未完结,兔子洞是个很简单的所在。

退出移动版