将所有项目加倍,同时限制Lisp中的cons调用

时间:2018-01-22 05:15:12

标签: lisp common-lisp

我有一个功能,可以将列表中的每个项目加倍:

(defun double (L)
   (cond
      ((atom L) nil)
      (t (cons (car L) (cons (car L ) (double (cdr L))))) ) )

(double '(a b c )) => (a a b b c c)

如何在将函数调用cons的次数除以2时完成相同的结果? (即在前面的例子中,它调用cons 6次。我怎么能只用3次呢?)

谢谢!

编辑2,在jkiiski的评论之后,似乎现在有效:

(defun double2 (L)
   (cond
      ((atom L) nil)
      (t (setf
             (cdr L)
             (cons (car L ) (double2 (cdr L))))
         L) ) )


(double2 (list 'a 'b 'c)) => (a a b b c c) 

2 个答案:

答案 0 :(得分:2)

这是另一种方法,没有递归。请注意,这个答案假设您正在做一个功课并试图找到一种方法来避免创建一个新的列表,这是最简单的解决方案。实际上,您只需在collect中使用loop两次,就像Sylwester演示的那样。这里破坏性地修改了原始输入列表。

复制清单

假设您的原始list(1 2 3)。而不是消耗 元素你自己,你可以调用(copy-list list)来执行 需要的金额。这就是你需要考虑的所有内存 分配。然后,你“只”需要将所有缺点单元格交错 获得所需的重复。

保持给定的list变量,并定义两个变量 迭代两个列表:

current = (1 2 3) ;; original
  fresh = (1 2 3) ;; copy

重新排序细胞

从图形上看,您希望将CDR更改为“将”现有的cons-cells“线程化”在一起。首先,两个列表都是这样的:

  current   ( 1 . x-)-->( 2 . x-)-->...

  fresh     ( 1 . x-)-->( 2 . x-)-->...

但你想拥有:

  current   ( 1 . x )   ( 2 . x )
                  |     ^     |
                  V     |     V
  fresh           ( 1 . x )   ( 2 . ...)

更正式地说,在每个步骤的开头,当您的列表不为空时,上述变量可以按如下方式分解:

current = (chead . ctail)
  fresh = (fhead . ftail)

您希望current的尾部指向fresh cons-cell,并使fresh的尾部指向ctail。 完成交错单元格后,变量应绑定如下:

current = (chead . (fhead . ctail))
  fresh = ftail

然后,你可以在current下降两次,最后:

current = ctail
  fresh = ftail

从这里开始,您可以继续使用其他两个列表。注意 list仍然包含您给出的原始cons单元格 输入。

代码

(defun double-loop (list)
  (loop
    with fresh = (copy-list list)                 ;; cursor to new cons cells
    with current = list                           ;; cursor to current cell
    while fresh                                   ;; stop when fresh is nil
    do (print (list list current fresh))          ;; for debugging
       (rotatef (cdr fresh) (cdr current) fresh)  ;; change CDRs, rebind fresh
       (setf current (cddr current))              ;; update CURRENT
    finally (return list)))                       ;; LIST points to head of result

我正在使用ROTATEF来连接cons细胞:

(rotatef (cdr fresh) (cdr current) fresh)

fresh的值放在(cdr current)中,其前一个值本身位于(cdr fresh),原始值为fresh 其值最终成为绑定到CL-USER> (double-loop (list 0 1 2 3)) ((0 1 2 3) (0 1 2 3) (0 1 2 3)) ((0 0 1 2 3) (1 2 3) (1 2 3)) ((0 0 1 1 2 3) (2 3) (2 3)) ((0 0 1 1 2 2 3) (3) (3)) => (0 0 1 1 2 2 3 3) 的新值。

实施例

以下是示例跟踪输出:

hive-import

答案 1 :(得分:1)

在jkiiski的评论之后,似乎现在有效:

    Object d = new Dog();
    ((Dog) d).print();
    d.getClass().getMethod("print").invoke(d);