D Shuler
D Shuler

Reputation: 27

lisp: building a list of lists from a single list

I am trying to take a list of 16 numbers I have and make it into a list of 4, 4 element sublists to represent the game board of a magic square. I made a method that can take a list and return a single sublist, and now I am trying to recursively use this method to build the full board.

My problem however, is my initBoard returns nil no matter what and I know every other method is working as desired. Any clarification of my error would be greatly appreciated!

Also here is an example input list:

(4 5 15 10 14 11 1 8 9 16 6 3 7 2 12 13)

And what I want as the output would be:

((4 5 15 10) (14 11 1 8) (9 16 6 3) (7 2 12 13))

(defun smallList (lst cnt)
   (cond ((>= cnt 4) nil)
   (t (cons (car lst) (smallList (cdr lst) (+ 1 cnt))))))

(defun isEmpty (lst)
   (if lst 1 -1))

(defun initBoard (lst)
   (cond ((= (isEmpty lst) -1) nil) 
   (t (cons (smallList lst 0) (initBoard  (cddddr lst))))))

Upvotes: 0

Views: 2715

Answers (3)

Leo
Leo

Reputation: 1934

I would use the following recursive function:

(defun smalllist (l n) 
   (when l 
      (cons (subseq l 0 (min n (length l))) 
            (smalllist (nthcdr n l) n))))

Upvotes: 0

coredump
coredump

Reputation: 38789

Some remarks:

  • someList, lst, cnt is not idiomatic, use some-list, list, count
  • You don't need is-empty, just use endp or null, which returns a boolean (not -1 or 1). You could make an alias if you want (but why?):

    (setf (symbol-function 'is-empty) #'endp)
    

You could use a loop for small-list:

(defun small-list (list)
  (values (loop repeat 4 collect (pop list)) list))

The secondary value is the rest of the list, so that you don't need to cddddr.

But in fact, it might be better to initialize the whole board inside a single function:

(defun init-board (list)
  (loop repeat 4 collect
       (loop repeat 4 collect (pop list))))

The first LOOP collect lists of 4 elements, which are collected by the inner LOOP. The collected elements are popped from the input list. Now, if I wanted to be extremely careful, I would add some checks and report errors on bad inputs:

(defun init-board (list)
  (flet ((failure ()
           (error "Input list should contain exactly 16 integers: ~S"
                  list)))
    (loop
       with current = list
       repeat 4 collect
         (loop
            repeat 4
            collect (if current
                        (let ((element (pop current)))
                          (check-type element integer)
                          element)
                        (failure)))
       into board
       finally (if list (failure) (return board)))))

Also, I would use a multi-dimensional array for boards.

(make-array '(4 4) :initial-contents (init-board list)) 

Upvotes: 1

djeis
djeis

Reputation: 286

I just tested your three functions and it gave me the correct output, so perhaps your issue isn't where you think it is.

(initBoard '(4 5 15 10 14 11 1 8 9 16 6 3 7 2 12 13))

=> ((4 5 15 10) (14 11 1 8) (9 16 6 3) (7 2 12 13))

Upvotes: 0

Related Questions