将文件读入 elisp 中的对列表

jus*_*ixx 2 lisp emacs elisp list

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

鉴于示例文件:

cat
cow
dog
Run Code Online (Sandbox Code Playgroud)

我希望列表看起来像:

(act cat)
(cow cow)
(dgo dog)
Run Code Online (Sandbox Code Playgroud)

我最好的破解方法是:

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

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

(get-file "~/test.txt")
Run Code Online (Sandbox Code Playgroud)

我最终进入了 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)
Run Code Online (Sandbox Code Playgroud)

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

谢谢,

贾斯汀

Mir*_*lov 5

香草 Emacs

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

(append "cat" nil) ; => (99 97 116)
Run Code Online (Sandbox Code Playgroud)

转换为列表的字符串成为字符列表,字符在 Elisp中表示为数字。然后列表进行排序并将其转换为字符串

(concat (sort (append "cat" nil) '<)) ; => "act"
Run Code Online (Sandbox Code Playgroud)

没有内置函数可以将文件内容直接加载到变量中,但您可以将它们加载临时缓冲区中。然后您可以将整个临时缓冲区作为字符串返回:

(with-temp-buffer
  (insert-file-contents-literally "file.txt")
  (buffer-substring-no-properties (point-min) (point-max))
Run Code Online (Sandbox Code Playgroud)

这将返回 string "cat\ncow\ndog\n",因此您需要拆分它:

(split-string "cat\ncow\ndog\n") ; => ("cat" "cow" "dog")
Run Code Online (Sandbox Code Playgroud)

现在你需要遍历这个列表并将每个项目转换成一对已排序的项目和原始项目:

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

完整代码:

(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)))))
Run Code Online (Sandbox Code Playgroud)

通用 Lisp 仿真

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

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

(cl-sort "cat" '<) ; => "act"
Run Code Online (Sandbox Code Playgroud)

cl-mapcar比 Emacs 的内置 更通用mapcar,但在这里您可以使用它们中的任何一个。

有一个问题cl-sort,它是破坏性的,这意味着它会就地修改参数。我们animal在匿名函数内部使用了两次局部变量,我们不想把原来的animal. 因此,我们应该将一个序列的副本传递给它:

(lambda (animal)
  (list (cl-sort (copy-sequence animal) '<) animal))
Run Code Online (Sandbox Code Playgroud)

生成的代码变为:

(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)))))
Run Code Online (Sandbox Code Playgroud)

seq.el

在 Emacs 25 中添加了一个新的序列操作库,seq.el. 替代mapcaris seq-map,替代 CL 的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)))))
Run Code Online (Sandbox Code Playgroud)

破折号, s, f

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

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

他们的 Github 页面解释了如何安装它们(安装非常简单)。然而,对于这个特定问题,它们有点次优。例如,-sortdash仅排序名单,所以我们必须回到我们与字符串>列表- >字符串转换:

(concat (-sort '< (append "cat" nil))) ; => "act"
Run Code Online (Sandbox Code Playgroud)

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

(s-lines "cat\ncow\ndog\n") ; => ("cat" "cow" "dog" "")
Run Code Online (Sandbox Code Playgroud)

s-split支持可选参数省略空行,但它的分离参数是一个正则表达式(请注意,您需要同时\n\r可移植性):

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

然而,有两个函数可以简化我们的代码。-map类似于mapcar

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

然而,dash有接受函数作为参数的函数的照应版本,例如-map. 照应版本允许通过将局部变量暴露为it并以 2 个破折号开头来使用更短的语法。例如以下是等效的:

(-map (lambda (x) (+ x 1)) (1 2 3)) ; => (2 3 4)
(--map (+ it 1) (1 2 3)) ; => (2 3 4)
Run Code Online (Sandbox Code Playgroud)

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

(f-read-text "file.txt") ; => "cat\ncow\ndog\n"
Run Code Online (Sandbox Code Playgroud)

结合世界上最好的

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