Lisp - n列表中的拆分列表

时间:2017-12-18 19:31:01

标签: functional-programming lisp common-lisp

我无法想办法如何平等地拆分列表,例如这个列表:

(("6" "S") ("7" "S") ("8" "S") ("9" "S") ("10" "S") ("J" "S") ("K" "S")
 ("A" "S") ("6" "C") ("7" "C") ("8" "C") ("9" "C") ("10" "C") ("J" "C")
 ("Q" "C") ("K" "C") ("A" "C") ("6" "H") ("7" "H") ("8" "H") ("9" "H")
 ("10" "H") ("J" "H") ("Q" "H") ("K" "H") ("A" "H")("6" "D") ("7" "D")
 ("8" "D") ("9" "D") ("10" "D") ("J" "D") ("Q" "D") ("K" "D"))

n列表中,例如在3或4中,取决于拆分需要多少。 如果在3个列表中,则应返回的列表应如下所示:

(("6" "S") ("7" "S") ("8" "S") ("9" "S") ("10" "S") ("J" "S") ("K" "S")
 ("A" "S") ("6" "C") ("7" "C") ("8" "C") ("9" "C"))
(("10" "C") ("J" "C") ("Q" "C") ("K" "C") ("A" "C")("6" "H") ("7" "H")
 ("8" "H") ("9" "H") ("10" "H") ("J" "H") ("Q" "H"))
(("K" "H") ("A" "H")("6" "D") ("7" "D") ("8" "D") ("9" "D") ("10" "D")
 ("J" "D") ("Q" "D") ("K" "D"))

第一个列表将包含12个元素,第二个列表为12,第三个列表为11。

4 个答案:

答案 0 :(得分:3)

如果你研究一下方案的拍摄和拍摄功能,你就可以达到你想要的效果。例如,观察这个简单的程序:

(define (splitparts lst num)
  (letrec ((recurse
            (lambda (lst num acc)
              (if (null? lst)
                acc
                (recurse (drop lst num) num (append acc (list (take lst num))))))))
    (recurse lst num '())))

> (splitparts '(1 2 3 4) 2)
((1 2) (3 4))
> (splitparts '(1 2 3 4 5 6 7 8) 2)
((1 2) (3 4) (5 6) (7 8))

现在问题在于,如果取下并希望列表至少具有您请求的元素数量。 所以我们可以编写我们自己的版本,这些版本需要占用一些元素,如果它们变得越来越少,他们就不会在乎。这是一个受this thread启发的具有正确尾部递归实现的实现

(define (takeup to lst)
  (letrec ((recurse
           (lambda (to lst acc)
             (if (or (zero? to) (null? lst))
               acc
               (recurse (- to 1) (cdr lst) (append acc (list (car lst))))))))
    (recurse to lst '())))

> (takeup 5 '(1 2 3))
(1 2 3)
> (takeup 5 '(1 2 3 4 5 6 7))
(1 2 3 4 5)

现在,当您实现类似的dropupto函数时,您可以轻松编写splitparts函数。在常见的lisp中,您可以使用subseq函数来实现类似于take和drop的功能。

编辑:普通的lisp实现简单的删除(请原谅我非常非惯用的CL)

;; recursive implemention of take just for demo purposes.
(defun takeinner (lst num acc)
  (if (or (= num 0) (null lst))
    acc
    (takeinner (cdr lst) (- num 1) (append acc (list (car lst))))))

(defun take (lst num)
  (takeinner lst num '()))

;; of course take can be implemented using subseq as drop.
(define take-alternative (lst num)
  (subseq lst 0 num))

(defun drop (lst num)
  (subseq lst num))

(defun splitpartsinner (lst num acc)
   (if (null lst)
        acc
       (splitpartsinner (drop lst num) num (append acc (list (take lst num))))))

(defun splitparts (lst num)
  (splitpartsinner lst num '()))

> (splitparts '(1 2 3 4) 2)
((1 2) (3 4))

这将遇到上述问题,因此您仍然需要实现-up-to版本。

答案 1 :(得分:3)

这是一个使用loop的简单CL实现:我认为这应该很容易理解。这种收集的东西是loop特别擅长的东西。

(defun partition-list (list parts &key (last-part-longer nil))
  ;; Partition LIST into PARTS parts.  They will all be the same
  ;; length except the last one which will be shorter or, if
  ;; LAST-PART-LONGER is true, longer.  Doesn't deal with the case
  ;; where there are less than PARTS elements in LIST at all (it does
  ;; something, but it may not be sensible).
  (loop with size = (if last-part-longer
                        (floor (length list) parts)
                      (ceiling (length list) parts))
        and tail = list
        for part upfrom 1
        while tail
        collect (loop for pt on tail
                      for i upfrom 0
                      while (or (and last-part-longer (= part parts))
                                (< i size))
                      collect (first pt)
                      finally (setf tail pt))))

答案 2 :(得分:0)

试试这个

 (multiple-value-bind (sub-list-size inc)
       // returns the sublist size and the adjustment you may use on the last list
       // size is the number of lists you wish to create
       (round (length list) size)
     (let ((iteration 1)
           (final-list '())
           (sub-list '()))
         (dotimes (x (length list))
            (cond ((< iteration size)
              // if its not the last list, add to the sublist until you reach the limit
                    (cond ((< (length sub-list) sub-list-size)
                           (push (nth x list) sub-list))
                          (t 
                            // when you reach the limit, increment the iteration number and start a new sub list with the current number
                            (push (reverse sub-list) final-list)
                            (incf iteration)
                            (setf sub-list (list (nth x list))))))                        
                  (t
                    // in the last iteration, add to the sub list until you have no more elements
                    (push (nth x list) sub-list))))
          (push (reverse sub-list) final-list)
          (reverse final-list)))

答案 3 :(得分:0)

我可能会这样做(虽然它比一些解决方案更冗长,我觉得它更具可读性和可重用性)

首先是一些实用功能:

(defun split-at (n data)
  (labels ((split-rec (n acc rest)
             (if (or (null rest) (zerop n)) 
                 (cons (nreverse acc) rest)
                 (split-rec (1- n) (cons (car rest) acc) (cdr rest)))))
    (split-rec n () data)))


;; this one would count the needed chunk sizes to cover different input cases
(defun chunk-lengths (data-len parts)
  (let ((chunk-size (floor (/ data-len parts)))
        (longer-chunks (rem data-len parts)))
    (nconc (loop :repeat longer-chunks :collect (1+ chunk-size))
           (loop :repeat (- parts longer-chunks) :collect chunk-size))))

和分区功能:

(defun split-into (chunks-count data)
  (nreverse 
   (car
    (reduce (lambda (acc len)
              (destructuring-bind (res . remaining-items) acc
                (destructuring-bind (l . r) (split-at len remaining-items)
                  (cons (push l res) r))))
            (chunk-lengths (length data) chunks-count)
            :initial-value (cons nil data)))))

CL-USER> (split-into 6 '(1 2 3 4 5 6 7 8 9))
;;=> ((1 2) (3 4) (5 6) (7) (8) (9))

CL-USER> (split-into 10  '(1 2 3 4 5))
;;=> ((1) (2) (3) (4) (5) NIL NIL NIL NIL NIL)

请注意,此解决方案可确保您获得所需数量的块(即使集合短于块数)