代码之家  ›  专栏  ›  技术社区  ›  postfuturist

是否有一个通用的lisp宏用于从列表中弹出第n个元素?

  •  5
  • postfuturist  · 技术社区  · 14 年前

    我对常见的Lisp场景很熟悉,似乎找不到快速的方法从列表中获取第n个元素并同时将其从所述列表中移除。我已经做过了,但并不漂亮,我真正想要的是像“pop”这样的东西,但接受了第二个参数:

    (setf x '(a b c d))
    (setf y (popnth 2 x))
    ; x is '(a b d)
    ; y is 'c
    

    我很确定“popth”必须是一个宏,以防参数为0,并且它的行为必须像“pop”一样。

    编辑:这是我的垃圾第一个版本:

    (defmacro popnth (n lst)
      (let ((tempvar (gensym)))
        `(if (eql ,n 0)
          (pop ,lst)
          (let ((,tempvar (nth ,n ,lst)))
            (setf (cdr (nthcdr ,(- n 1) ,lst)) (nthcdr ,(+ n 1) ,lst))
            ,tempvar))))
    
    4 回复  |  直到 14 年前
        1
  •  11
  •   Rainer Joswig Michael Fox    14 年前

    像这样的:

    删除列表的第n个元素 :

    (defun remove-nth (list n)
      (remove-if (constantly t) list :start n :end (1+ n)))
    

    constantly 返回一个函数,该函数始终返回其参数。

    作为接受 place ,使用 define-modify-macro

    (define-modify-macro remove-nth-f (n) remove-nth "Remove the nth element")
    

    流行歌曲

    (defmacro pop-nth (list n)
      (let ((n-var (gensym)))
        `(let ((,n-var ,n))
           (prog1 (nth ,n-var ,list)
             (remove-nth-f ,list ,n-var)))))
    

    例子

    CL-USER 26 > (defparameter *list* (list 1 2 3 4))
    *LIST*
    
    CL-USER 27 > (pop-nth *list* 0)
    1
    
    CL-USER 28 > *list*
    (2 3 4)
    
    CL-USER 29 > (pop-nth *list* 2)
    4
    
    CL-USER 30 > *list*
    (2 3)
    
        2
  •  5
  •   Kaz    11 年前

    是的,Lisp有一个宏用于弹出列表的第N个元素:它被调用 pop .

    $ clisp -q
    [1]> (defvar list (list 0 1 2 3 4 5))
    LIST
    [2]> (pop (cdddr list))
    3
    [3]> list
    (0 1 2 4 5)
    [4]> 
    

    与任何表示地方的形式一起使用。

    问题是,不像 cddr , nthcdr (nthcdr 3 list) 不表示位置;它仅用作函数调用。

    写一种特殊形式的 流行音乐 第n个CDR 流行音乐 宏将起作用,并且 其他宏也一样 在像这样的地方工作 setf rotatef .

    ;; our clone of nthcdr called cdnth
    (defun cdnth (idx list)
      (nthcdr idx list))
    
    ;; support for (cdnth <idx> <list>) as an assignable place
    (define-setf-expander cdnth (idx list &environment env)
       (multiple-value-bind (dummies vals newval setter getter)
                            (get-setf-expansion list env)
         (let ((store (gensym))
               (idx-temp (gensym)))
           (values dummies
                   vals
                   `(,store)
                   `(let ((,idx-temp ,idx))
                      (progn
                        (if (zerop ,idx-temp)
                          (progn (setf ,getter ,store))
                          (progn (rplacd (nthcdr (1- ,idx-temp) ,getter) ,store)))
                        ,store))
                   `(nthcdr ,idx ,getter)))))
    

    测试:

    $ clisp -q -i cdnth.lisp 
    ;; Loading file cdnth.lisp ...
    ;; Loaded file cdnth.lisp
    [1]> (defvar list (list 0 1 2 3 4 5))
    LIST
    [2]> (pop (cdnth 2 list))
    2
    [3]> list
    (0 1 3 4 5)
    [4]> (pop (cdnth 0 list))
    0
    [5]> list
    (1 3 4 5)
    [6]> (pop (cdnth 3 list))
    5
    [7]> list
    (1 3 4)
    [8]> (pop (cdnth 1 list))
    3
    [9]> list
    (1 4)
    [10]> (pop (cdnth 1 list))
    4
    [11]> list
    (1)
    [12]> (pop (cdnth 0 list))
    1
    [13]> list
    NIL
    [14]> 
    

    对实现的一个可能的改进是分析 idx 国际直拨电话 . 也就是说,如果 是一个常量表达式,不需要发出测试 国际直拨电话 是零。适当的代码变量就可以发出。不仅如此,对于 国际直拨电话 ,代码可以根据“尸体”发出特殊变体: cddr公司 , cdddr 第n个CDR . 但是,其中一些优化可能是由Lisp编译器完成的,因此是多余的。

        3
  •  1
  •   postfuturist    14 年前

    (defmacro popnth (n lst)
      (let ((t1 (gensym))(t2 (gensym)))
        `(if (eql ,n 0)
          (pop ,lst)
          (let* ((,t1 (nthcdr (- ,n 1) ,lst))
                  (,t2 (car (cdr ,t1))))
            (setf (cdr ,t1) (cddr ,t1))
            ,t2))))
    

    这就是它的作用:

    [2]> (defparameter *list* '(a b c d e f g))
    *LIST*
    [3]> (popnth 3 *list*)
    D
    [4]> *list*
    (A B C E F G)
    [5]> (popnth 0 *list*)
    A
    [6]> *list*
    (B C E F G)
    
        4
  •  0
  •   Juanito Fatas    12 年前

    我和@6502有同样的怀疑…如果我没记错的话…也没有 push pop 可以定义为修改宏,前者是因为 place 不是它的第一个参数,后者是因为它的返回值不是modified对象。

    定义 define-modify-macro

    形式的表达 (define-modify-macro m (p1 ... pn) f) 定义新宏 m ,以便 (m place a1 ... an) 会导致 地方 (f val a1 ... an) ,其中 val 地方 . 参数还可以包括rest和可选参数。字符串(如果存在)将成为新宏的文档。

    我有这个 popnth 工作正常:

    (defun nthpop (index lst)
      (pop (nthcdr (1- index) lst)))
    
    > *list*
    (1 2 3 4 5)
    > (nthpop 2 *list*)
    2
    > *list*
    (1 3 4 5)