我正在尝试在Common Lisp中编写一个类似于内置位置函数的函数,该函数返回haystack中与指针匹配的所有元素的位置列表,而不是第一个。我想出了一些可能的解决方案(例如,使用位置上的cdr-from函数递归搜索下一个元素并将结果添加到前一个位置)但是到目前为止我没有提出任何方法看起来特别优雅。
任何人都可以建议什么是接近这个的最佳方式,因为我目前正在努力。
答案 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)))