在elisp中去除字符串列表中的重复元素

30

假设有一个列表,如下所示:

(list "foo" "bar" nil "moo" "bar" "moo" nil "affe")

我该如何构建一个新的列表,将重复的字符串移除并去除nil,即:

(list "foo" "bar" "moo" "affe")

需要保留元素的顺序 - 不能删除字符串的第一次出现。

我处理的列表比较短,因此不需要使用哈希表进行唯一性检查,尽管这样做肯定也没有坏处。但是,使用 cl 功能不是可行的选择。

5个回答

46

2
更加优雅和直观。有时我真的希望有像命名空间一样将相关函数分组的东西,或者一个更一致的命名方案,可以通过猜测名称来查找特定的内容 :-/ - rafl
1
分组(通过命名约定或命名空间)很难,因为总有许多方法可以进行分组。Emacs并没有尝试非常努力去做这件事,而是提供了像apropos这样的东西。但这也不是理想的解决方案。我们可能应该更加努力,虽然现在修复大多数问题有点晚了。 - Stefan

20

Common Lisp package包含许多列表操作函数,特别是remove-duplicates

(require 'cl)
(remove-duplicates (list "foo" "bar" nil "moo" "bar" "moo" nil "affe")
                   :test (lambda (x y) (or (null y) (equal x y)))
                   :from-end t)

是的,我知道你说你不想使用cl。但我仍然提到这个作为正确的方法,为其他可能阅读此线程的人提供参考。

(为什么cl对你来说不可行呢?它已经随Emacs一起发布了大约20年了,不包括过去功能较少的版本。)


1
我一直在对Gnus进行一些微小的补丁,而在它的提交日志中,我经常看到更改将某些来自cl的内容替换为非cl等效内容。我不太确定原因是什么,但很可能是为了支持奇怪的Emacs版本或避免加载cl包,除非真正必要。 - rafl
3
我认为对cl的限制始于RMS希望保持emacs-lisp的小巧。请查看最近讨论此观点的线程:http://lists.gnu.org/archive/html/emacs-devel/2010-09/msg01278.html - Trey Jackson
8
@Trey: 哦,我明白了。为了保持Emacs核心的小巧精悍,需要要求每个包重新实现他们自己的基本数据结构函数。嗯,如果你是决定Emacs核心仅包含你使用的功能的人,那么这种方式可以行得通。但其他人却需要重复劳动和臃肿不堪…… - Gilles 'SO- stop being evil'

6
如果您使用 dash.el 库,那就足够了:
(-distinct (-non-nil '(1 1 nil 2 2 nil 3)) ; => (1 2 3)

dash.el由Magnar Sveen编写,是一个非常棒的列表操作库,具有许多用于各种任务的函数。如果你经常编写Emacs Lisp代码,我建议你安装它。函数-distinct从列表中删除重复元素,-non-nil删除nil元素。虽然上面的代码已经足够,但下面我描述了一种替代方法,所以可以忽略本帖子的其余部分。

-non-nil在版本2.9中添加,因此如果出于某种原因您必须使用较早的版本,则实现相同效果的另一种方法是使用内置的-keepidentity函数,该函数只返回其所接收到的参数:(identity 1) ; => 1。思路是-keep仅保留断言返回true(Lisp术语中的“非nil”)的元素。显然,identity仅对不是nil的值返回非nil:

(-distinct (-keep 'identity '(1 1 nil 2 2 nil 3)) ; => (1 2 3)

2

这是一个简短的例子:

(delete-duplicates '("~/.emacs.d" "~/.emacs.d") :test #'string-equal) ;; '("~/emacs.d")

基本上,您可以使用:test关键字选择函数string-equal来测试元素是否重复。

否则,默认的函数测试不会检查字符串相等性。


0

这是你想要的:

(defun strip-duplicates (list)
  (let ((new-list nil))
    (while list
      (when (and (car list) (not (member (car list) new-list)))
        (setq new-list (cons (car list) new-list)))
      (setq list (cdr list)))
    (nreverse new-list)))

这很好地完成了任务。我现在学到了member。是否还有类似的函数可以指定比较函数? - rafl
不,据我所知没有。不过写起来相当简单。 - Sean
还有memqmemql - phils
9
在重新发明已经有实现的常见模式(在这种情况下是delete-dups)之前,阅读文档可以得到一个更好的答案。 - aculich
1
如果你感到好奇,你可以比较一下你的实现:subr.el中的delete-dups - aculich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接