原版 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)))))
通用 Lisp 仿真
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.el。 mapcar 的替代品是seq-map,CL 的cl-sort 的替代品是seq-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)))))
破折号、s、f
通常使用序列和文件的最佳解决方案是直接访问这 3 个第三方库:
-
dash 用于列表操作
-
s 用于字符串操作
-
f 用于文件操作。
他们的 Github 页面解释了如何安装它们(安装非常简单)。然而,对于这个特殊问题,它们有点不理想。例如,来自 dash 的-sort 仅对列表进行排序,因此我们必须回到我们的字符串->列表->字符串转换:
(concat (-sort '< (append "cat" nil))) ; => "act"
s-lines from s 在文件中留下空字符串。在 GNU/Linux 上,文本文件通常以换行符结尾,因此拆分文件如下所示:
(s-lines "cat\ncow\ndog\n") ; => ("cat" "cow" "dog" "")
s-split 支持省略空行的可选参数,但它的分隔符参数是 regex(请注意,portability 需要 \n 和 \r):
(s-split "[\n\r]" "cat\ncow\ndog\n" t) ; => ("cat" "cow" "dog")
但是有两个函数可以简化我们的代码。 -map 类似于mapcar:
(-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")))