代码之家  ›  专栏  ›  技术社区  ›  Paul Nathan

更通用的lisp代码来生成对的组合

  •  2
  • Paul Nathan  · 技术社区  · 14 年前

    [53]> (setq thingie '())
    
    NIL
    [54]> (loop for i in (generate-range 0 3) do 
    (loop for j in (generate-range 4 6) do 
    (push (list i j) thingie)))
    
    NIL
    [55]> thingie
    
    ((3 6) (3 5) (3 4) (2 6) (2 5) (2 4) (1 6) (1 5) (1 4) (0 6) (0 5) (0 4))
    [56]>  
    

    我该如何构建某种成对的代码来生成 任意的 范围数?(或生成n维离散布局)。

    显然一个解决办法就是 defmacro n

    4 回复  |  直到 11 年前
        1
  •  2
  •   huaiyuan    14 年前
    (defun map-cartesian (fn bags)
      (labels ((gn (x y)
                 (if y (mapc (lambda (i) (gn (cons i x) (cdr y))) (car y))
                     (funcall fn x))))
        (gn nil (reverse bags))))
    
    CL-USER> (map-cartesian #'print '((1 2) (a b c) (x y)))
    
    (1 A X) 
    (2 A X) 
    (1 B X) 
    (2 B X) 
    (1 C X) 
    (2 C X) 
    (1 A Y) 
    (2 A Y) 
    (1 B Y) 
    (2 B Y) 
    (1 C Y) 
    (2 C Y) 
    

    如果你喜欢糖,

    (defmacro do-cartesian ((item bags) &body body)
      `(map-cartesian (lambda (,item) ,@body) ,bags))
    
    CL-USER> (do-cartesian (x '((1 2) (a b c) (x y)))
               (print x))
    

    编辑:(简要说明)

    gn的第一个参数x是到目前为止构造的部分元组;y是剩余的元素包。函数gn通过迭代剩余行李(car y)的每个元素i来扩展部分元组,形成(cons i x)。当没有剩余的包时 if 语句),所以我们调用元组上提供的函数fn。

        2
  •  0
  •   Svante    14 年前

        3
  •  0
  •   Vatine    14 年前

        4
  •  0
  •   Rörd    11 年前

    您不需要显式递归(甚至宏),这也可以通过高阶函数实现:

    (defun tuples-from-ranges (range &rest ranges)
      (reduce (lambda (acc range)
                (mapcan (lambda (sublist)
                          (mapcar (lambda (elt)
                                    (append sublist (list elt)))
                                  (apply #'generate-range range)))
                        acc))
              ranges
              :initial-value (mapcar #'list (apply #'generate-range range))))
    

    mapcan mapcar )执行与示例中的两个嵌套循环相同的函数。外高阶函数 reduce 然后,将首先将前两个范围的值组合到pair中,然后在每次调用其参数函数时,将some进程再次应用于前一次调用和下一个范围的中间结果。