List 使用scheme将元素插入循环列表

List 使用scheme将元素插入循环列表,list,insert,scheme,circular-list,List,Insert,Scheme,Circular List,我有一个循环列表,例如:#0=(1234.#0) 我想做的是在这个列表中插入一个新元素(x),这样结果就是#0=(x12 3 4.#0#)。我一直在尝试使用此代码(x是循环列表): 然而,我认为设置cdr!没有像我希望的那样工作。我错过了什么?也许我有点不对劲了?因为您试图将一个元素预先添加到循环列表中,所以需要做两件事: 在包含附加元素的列表前面插入一个新的cons单元格。这很容易,因为您只需执行一个简单的(cons elm x) 您还需要修改循环列表的递归部分以指向新创建的cons单元格,否

我有一个循环列表,例如:#0=(1234.#0)

我想做的是在这个列表中插入一个新元素(x),这样结果就是#0=(x12 3 4.#0#)。我一直在尝试使用此代码(x是循环列表):


然而,我认为设置cdr!没有像我希望的那样工作。我错过了什么?也许我有点不对劲了?

因为您试图将一个元素预先添加到循环列表中,所以需要做两件事:

  • 在包含附加元素的列表前面插入一个新的cons单元格。这很容易,因为您只需执行一个简单的
    (cons elm x)

  • 您还需要修改循环列表的递归部分以指向新创建的cons单元格,否则循环部分将只包括列表的旧部分

  • 要执行后者,您需要一种方法来确定循环列表的“结束”在哪里。这实际上并不存在,因为列表当然是循环的,但它可以通过对列表的每个元素执行
    eq?
    检查来确定,直到它找到一个与列表头部相等的元素

    创建一个助手函数来实现这一点,这是
    insert将如下所示:

    (define (find-cdr v lst)
      (if (eq? v (cdr lst)) lst
          (find-cdr v (cdr lst))))
    
    (define (insert! elm)
      (set! x (cons elm x))
      (set-cdr! (find-cdr (cdr x) (cdr x)) x))
    

    将元素前置到列表的最简单方法是修改列表的car,并将列表的cdr设置为新的cons,其car是列表的原始第一个元素,cdr是列表的原始尾部:

    (define (prepend! x list)                      ; list = (a . (b ...)) 
      (set-cdr! list (cons (car list) (cdr list))) ; list = (a . (a . (b ...)))
      (set-car! list x))                           ; list = (x . (a . (b ...)))
    

    现在,这仍然适用于循环列表,因为列表开头的cons单元格(即pair)保持不变,所以“final”cdr仍将指向作为开始的对象。不过,为了测试这一点,我们需要一些函数来创建循环列表并从循环列表中采样,因为它们不包括在语言中(据我所知)

    现在,我们可以检查如果在循环列表前加上前缀会发生什么:

    (define (insert! elm)
      (let ((temp x))
        (set-car! x elm)
        (set-cdr! x temp)))
    
    (let ((l (make-circular (list 1 2 3))))
      (prepend! 'x l)
      (display (take 15 l)))
    ;=> (x 1 2 3 x 1 2 3 x 1 2 3 x 1 2)
    

    我认为这可能比它需要的更复杂。由于尾部已经缠绕,因此更容易修改该cons的车辆,并在该cons之后插入新cons。这使得整个操作的时间保持不变,因为您不需要找到列表的“结尾”。我补充说,这表明了这种方法。谢谢你的回答。然而,我认为我不同意你的情商检查。例如,如果u有一个循环列表,其中有两个相同符号出现,会发生什么情况?当eq检查发现同一符号的第二次出现时,它是否会被错误地告知并认为它已经结束了?例如:#0=(123412.#0#)@Br0dskive不,亚历克西斯的答案不是比较列表中的元素;它实际上是比较列表结构中的实际pair或cons单元格<代码>(cdr lst)
    是列表的其余部分,即另一个列表。所以检查
    (eq?v(cdrlst))
    是否为真意味着检查列表v是否与lst的其余列表相同。@Joshua啊,是的,这很有意义。谢谢你的澄清:)谢谢你的回答!我总是发现解决方案比我想象的要简单得多。你说得对,+1!我很少使用可变列表,所以这个解决方案对我来说并不明显,但这肯定比我的方法要好。:)
    (define (make-circular list)
      (let loop ((tail list))
        (cond
          ((null? (cdr tail))
           (set-cdr! tail list)
           list)
          (else
           (loop (cdr tail))))))
    
    (define (take n list)
      (if (= n 0)
          '()
          (cons (car list)
                (take (- n 1)
                      (cdr list)))))
    
    (display (take 10 (make-circular (list 1 2 3))))
    ;=> (1 2 3 1 2 3 1 2 3 1)
    
    (let ((l (make-circular (list 1 2 3))))
      (prepend! 'x l)
      (display (take 15 l)))
    ;=> (x 1 2 3 x 1 2 3 x 1 2 3 x 1 2)