列表中所有匹配元素的位置

时间:2010-12-14 12:49:14

标签: lisp common-lisp

我正在尝试在Common Lisp中编写一个类似于内置位置函数的函数,该函数返回haystack中与指针匹配的所有元素的位置列表,而不是第一个。我想出了一些可能的解决方案(例如,使用位置上的cdr-from函数递归搜索下一个元素并将结果添加到前一个位置)但是到目前为止我没有提出任何方法看起来特别优雅。

任何人都可以建议什么是接近这个的最佳方式,因为我目前正在努力。

4 个答案:

答案 0 :(得分:9)

解决问题的显而易见的方法是依次查看列表中的每个元素,并且每次比较等于针将其位置收集到输出列表中。在这种情况下获取位置非常容易,因为我们从 haystack开始; 我们可以使用变量来计算从0开始的当前位置。

因此,如果我们在一个句子中描述完整的算法,我们会说“找到大海捞针中的所有位置,大海捞针中的每个元素,以及从0开始的位置,当元素等于针,收集位置。“

当您想要进行迭代处理时,LOOP工具基本上是正确的。尽管在describe formally,之后它的语法很复杂experience,你几乎可以将算法的英语描述放在LOOP的主体中,它会起作用。

(defun all-positions (needle haystack)
  (loop
    for element in haystack 
    and position from 0
     when (eql element needle)
      collect position))

答案 1 :(得分:1)

带上一粒盐(并确保事先加载Alexandria):

(defun positions (item sequence &key (test #'eql))
  (mapcar #'car
          (remove item (map 'list #'cons (alexandria:iota (length sequence)) sequence)
                       :test-not test
                       :key #'cdr)))

也就是说,它确实具有处理任意序列的优势:

CL-USER> (positions 'x #(x x y y x x y y))
(0 1 4 5)
CL-USER> (positions 5 (list 5.0 -1 5 5.0 -1) :test #'=)
(0 2 3)
CL-USER> (positions #\l "Hello")
(2 3)

答案 2 :(得分:0)

如果你想要一个递归函数,而不是基于(loop ...)的函数,你可以使用类似的东西:

(defun all-positions (needle haystack)
    (labels ((f (n h c r)
                 (if (null h)
                     r
                     (if (eql (car h) n)
                         (f n (cdr h) (1+ c) (cons c r))
                         (f n (cdr h) (1+ c) r))))))
    (reverse (f needle haystack 0 nil)))

答案 3 :(得分:0)

这是另一种(不一定更好)的方式。

(defun get-positions (needle haystack)
  (let ((result nil))
    (dotimes (i (length haystack))
      (if (eq (nth i haystack) needle)
          (push i result)))
    (nreverse result)))