Common Lisp - 编写一个检测循环列表的函数

Mat*_*uet 4 lisp recursion function list common-lisp

我有一个CS功能语言类的作业,我们必须编写一个能够检测给定列表在其开头是否为循环的函数.该函数必须是递归的.我们来命名它circular:

* (setf list1 '(a b c d))
* (rplacd (cddr list1) list1)
* list1
#1=(A B C . #1#)
* (circular list1)
t
* (circular '(a b c a b c))
nil
Run Code Online (Sandbox Code Playgroud)

现在,我知道在这种情况下如何检查圆度:在递归的某个点上,列表中的原子 a cons将与列表本身共享相同的地址,因此是圆形度.到目前为止,这是我的功能:

(defun circular (list &aux list2)
  (setf list2 list)
  (cond
    ((null list) nil)
    ((eq (car list) list2) t)
    (t (circular (cdr list))) ) )
Run Code Online (Sandbox Code Playgroud)

我认为通过比较递归的每个点carlist反对list2,函数最终会返回一个结果,但是当函数对非循环列表起作用时,当我尝试在循环列表中使用它时,它会变得无限递归.我确信我错过了一些非常明显的东西,但是,任何帮助都会非常感激!

Syl*_*ter 6

这个问题可以通过乌龟和野兔算法来解决,在这种算法中你有两个游标一次扫描tortoise一次,一次扫描hare速度从第二个元素开始.如果乌龟和野兔是相同的元素,你就有一个循环.

(defun cyclicp (list)
  "check if a list is cyclic using tortoise and hare algorithm"
  (loop :for tortoise on list
        :for hare on (cdr list) :by #'cddr
        :thereis (eq tortoise hare)))
Run Code Online (Sandbox Code Playgroud)

所以想象一下你有列表,#1=(a b c . #1#)或者更确切地说'#1=(a . #2=(b . #3=(c . #1#))).它包含3 cons与地址1,2,3和每个利弊具有符号值中的一个a,b,c.

如果我写下car乌龟和野兔你会看到野兔缠绕并最终与cons乌龟一样

Iteration 1 2 3
Tortoise  a b c
Hare      b a c
              ^ match
Run Code Online (Sandbox Code Playgroud)

您需要做的只是使用递归重新实现它.它看起来像这样:

(defun cyclicp (list)
  "check if a list is cyclic using tortoise and hare algorithm"
  (labels ((aux (tortoise hare)
        (cond ((null hare) <??>)
              ((eq tortoise hare) <??>)
              (t (aux <??> <??>)))))
    (aux list (cdr list))))
Run Code Online (Sandbox Code Playgroud)

编辑

一个简单而简单的版本只能将引用检索回第一个,cons可以这样做:

(defun cyclic-1-p (list)
  "check if a list is cycles back to the first cons"
  (loop :for cursor on (cdr list)
        :thereis (eq cursor list)))
Run Code Online (Sandbox Code Playgroud)

您需要做的只是使用递归重新实现它.它看起来像这样:

(defun cyclic-1-p (list)
  "check if a list is cycles back to the first cons"
  (labels ((aux (cursor)
        (cond ((null cursor) <??>)
              ((eq cursor list) <??>)
              (t (aux <??>)))))
    (aux (cdr list))))
Run Code Online (Sandbox Code Playgroud)

如果循环发生但不在第一个循环中,cons则不会终止.

(cyclic-1-p '(1 . #1=(2 3 . #1#))) ; never halts
Run Code Online (Sandbox Code Playgroud)