将文件读入elisp中的对列表

时间:2015-10-15 21:00:59

标签: list emacs lisp elisp

我正在尝试编写一个elisp函数来将文件中的每个单词读成一对。我希望该对中的第一项是按字典顺序排序的字符串,而第二项是不受影响的。

给出示例文件:

cat
cow
dog

我希望列表看起来像:

(act cat)
(cow cow)
(dgo dog)

我最好的解决方法是:

(defun get-file (filename)
  (with-open-file (stream filename)
    (loop for word = (read-line stream nil)
          while word
          collect ((sort word #'char-lessp) word))))

它在Emacs lisp交互模式下正确编译。但是,当我尝试 通过执行

来运行它
(get-file "~/test.txt")

我最终进入了Emacs调试器,并没有告诉我任何有用的东西。 。

Debugger entered--Lisp error: (void-function get-file)
  (get-file "~/test.txt")
  eval((get-file "~/test.txt") nil)
  eval-last-sexp-1(t)
  eval-last-sexp(t)
  eval-print-last-sexp(nil)
  call-interactively(eval-print-last-sexp nil nil)
  command-execute(eval-print-last-sexp)

我是一个初学者,并且不知道出了什么问题。

谢谢,

贾斯汀

2 个答案:

答案 0 :(得分:3)

Vanilla Emacs

首先,让我们只使用Emacs的内置功能。没有内置函数来对Emacs中的字符串进行排序,因此您首先应该将字符串转换为列表,排序,然后将排序后的列表转换回字符串。这就是你convert a string to a list

的方式
(append "cat" nil) ; => (99 97 116)

转换为列表的字符串变为字符列表,并在Elisp中变为characters are represented as numbers。然后,您sort列表和convert it to a string

(concat (sort (append "cat" nil) '<)) ; => "act"

没有内置函数可以将文件内容直接加载到变量中,但您可以load them加载到temporary buffer。然后你可以return the entire temporary buffer作为字符串:

(with-temp-buffer
  (insert-file-contents-literally "file.txt")
  (buffer-substring-no-properties (point-min) (point-max))

这将返回字符串"cat\ncow\ndog\n",因此您需要split它:

(split-string "cat\ncow\ndog\n") ; => ("cat" "cow" "dog")

现在您需要traverse此列表并将每个项目转换为一对已排序项目和原始项目:

(mapcar (lambda (animal)
          (list (concat (sort (append animal nil) '<)) animal))
        '("cat" "cow" "dog"))
;; returns
;; (("act" "cat")
;;  ("cow" "cow")
;;  ("dgo" "dog"))

完整代码:

(mapcar
 (lambda (animal)
   (list (concat (sort (append animal nil) '<)) animal))
 (split-string
  (with-temp-buffer
    (insert-file-contents-literally "file.txt")
    (buffer-substring-no-properties (point-min) (point-max)))))

Common Lisp Emulation

其中一个Emacs内置软件包是cl.el,并且没有理由不在您的代码中使用它。因此我撒了谎,当我说没有内置函数来排序字符串时,上面是使用内置函数完成任务的唯一方法。因此,让我们使用cl.el

cl-sort一个字符串(或任何sequence):

(cl-sort "cat" '<) ; => "act"

cl-mapcar比Emacs的内置mapcar功能更多,但您可以使用其中任何一种。

cl-sort存在问题,它是destructive,这意味着它会就地修改参数。我们在匿名函数中使用局部变量animal两次,并且我们不想将原始animal弄乱。因此,我们应该将copy序列传递给它:

(lambda (animal)
  (list (cl-sort (copy-sequence animal) '<) animal))

结果代码变为:

(cl-mapcar
 (lambda (animal)
   (list (cl-sort (copy-sequence animal) '<) animal))
 (split-string
  (with-temp-buffer
    (insert-file-contents-literally "file.txt")
    (buffer-substring-no-properties (point-min) (point-max)))))

seq.el

在Emacs 25中添加了一个新的序列操作库seq.elmapcar的替代方案是seq-map,替代CL&#39 {s} cl-sortseq-sort。完整的代码变为:

(seq-map
 (lambda (animal)
   (list (seq-sort animal '<) animal))
 (split-string
  (with-temp-buffer
    (insert-file-contents-literally "file.txt")
    (buffer-substring-no-properties (point-min) (point-max)))))

dash,s,f

通常,处理序列和文件的最佳解决方案是直接访问这3个第三方库:

  • dash用于列表操作
  • s用于字符串操作
  • f用于文件操作。

他们的Github页面解释了如何安装它们(安装非常简单)。然而,对于这个特殊问题,它们有点不理想。例如, dash 中的-sort仅对列表进行排序,因此我们必须返回我们的字符串 - &gt; list-&gt;字符串转换:

(concat (-sort '< (append "cat" nil))) ; => "act"
来自 s-lines

s会在文件中留下空字符串。在GNU / Linux上,文本文件通常最后以换行符结尾,因此拆分文件将如下所示:

(s-lines "cat\ncow\ndog\n") ; => ("cat" "cow" "dog" "")

s-split支持一个可选参数来省略空行,但它的分隔符参数是regex(请注意,您需要\n\r portability):

(s-split "[\n\r]" "cat\ncow\ndog\n" t) ; => ("cat" "cow" "dog")

然而,有两个功能可以简化我们的代码。 -mapmapcar类似:

(-map
  (lambda (animal)
    (list (cl-sort (copy-sequence animal) '<) animal))
  '("cat" "cow" "dog"))
;; return
;; (("act" "cat")
;;  ("cow" "cow")
;;  ("dgo" "dog"))

但是在 dash 中,有anaphoric个版本的函数接受函数作为参数,例如-map。回指版本允许通过将局部变量公开为it并以2个破折号开头来使用更短的语法。例如。以下是相同的:

(-map (lambda (x) (+ x 1)) (1 2 3)) ; => (2 3 4)
(--map (+ it 1) (1 2 3)) ; => (2 3 4)

f 的另一个改进是f-read-text,它只是将文件内容作为字符串返回:

(f-read-text "file.txt") ; => "cat\ncow\ndog\n"

结合最好的世界

(--map (list (cl-sort (copy-sequence it) '<) it)
       (split-string (f-read-text "file.txt")))

答案 1 :(得分:0)

在我的emacs上, C-j C-x C-e 按照你的说法评估表格。当我尝试对(get-file "test")执行相同操作时,调试器会抱怨with-open-file未定义。我在with-open-file(或cl-lib)个emacs包中找不到cl。 你需要一些其他包吗?另外,我认为在Emacs中打开文件的惯用方法是在缓冲区中临时访问它们。 无论如何,如果代码是Common Lisp,那么除了collect ((sort ...) word)之外你没有问题,你在建立一个列表但在函数位置使用(sort ...)。我改用(list (sort ...) word)