FriedRike
FriedRike

Reputation: 145

How to make a list of pairs in lisp?

I'm trying to do a list of pairs as a part of a homework assignment.

I tried doing (somewhere in the middle of a function)

(setq list1 (append list1 (cons n1 n2)))

And for some reason I don't understand, this works fine with the first pair, but as I try to append the second pair, this error pops up:

*** - APPEND: A proper list must not end with 2

How can I solve this?

So, continuing on this subject, thanks to the answer given, I was able to correct my problem. But a new one came up, and I think it is related with it. So, I have this function:

(defun action(state)
(let ((list_actions '())
      (limNumActions (1- (list-length state)))
      (limNumSubActions 0)
      (numActions 0)
      (numSubActions 0))
  (loop for numActions from 0 to limNumActions do
    (setq limNumSubActions (1- (list-length (nth numActions state))))
    (loop for numSubActions from 0 to limNumSubActions do
      (setq list_actions (append list_actions
        (list (cons numActions numSubActions))))
      (print 'list_actions)
      (print list_actions)))))

I used the printfunction as a simple "debugger". It returns this:

 LIST_ACTIONS 
 ((0 . 0)) 
 LIST_ACTIONS 
 ((0 . 0) (0 . 1)) 
 LIST_ACTIONS 
 ((0 . 0) (0 . 1) (1 . 0)) 
 LIST_ACTIONS 
 ((0 . 0) (0 . 1) (1 . 0) (1 . 1)) 
 NIL

And this is exactly the result I was expecting! Except for the NIL part... Can you understand why the list list_actions is NILat the end?

Upvotes: 0

Views: 2128

Answers (3)

huaiyuan
huaiyuan

Reputation: 26519

The code can be expressed more succintly as follows:

(defun action (state)
  (let ((list-actions '()))
    (loop for i from 0 for state-i in state do
      (loop for j from 0 below (length state-i) do
        (setf list-actions (append list-actions (list (cons i j))))
        (print 'list-actions)
        (print list-actions)))
    list-actions))

If only the result is needed, it can be shorter (and less costly, because it doesn't use the expensive append function),

(defun action (state)
  (loop for i from 0 for state-i in state append
    (loop for j below (length state-i) collect (cons i j))))

Upvotes: 1

user797257
user797257

Reputation:

I have tried to refine your example a bit + write a version which uses a different, but, IMO more idiomatic approach to the problem:

;; Your original version, but cleaned up a bit
(defun action (state)
  (loop with list-actions = nil
     with lim-num-actions = (1- (list-length state))
     with lim-num-sub-actions = 0
     for num-actions from 0 to lim-num-actions
     do (setq lim-num-sub-actions (1- (list-length (nth num-actions state))))
       (loop for num-sub-actions from 0 to lim-num-sub-actions
          do (push (cons num-actions num-sub-actions) list-actions)
            (format t "~&~s ~s" 'list-actions list-actions))
     finally (return list-actions)))

;; A more traditional approach (if you want to use iteration)
(defun action (state)
  (reverse 
   (loop for i in state
      for j from 0
      collect (cons j 0))))

;; Using a nice library to do iteration
(ql:quickload "iterate")
;; Note, you could do (in-package :iterate)
;; to lose `iterate:' prefix to make it even shorter
(defun action (state)
  (iterate:iter
    (iterate:for i #:on state)
    (iterate:for j #:from 0)
    (iterate:accumulate (cons j 0) #:by #'cons)))

;; Just another way to do this, using `reduce'
(reduce #'(lambda (a b)
            (declare (ignore b))
            (cons (cons (1+ (caar a)) 0) a))
        (cdr (mapcar #'list '(1 2 3 4 5)))
        :initial-value '((0 . 0)))

(action (mapcar #'list '(1 2 3 4 5)))

Upvotes: 0

Kevin Reid
Kevin Reid

Reputation: 43743

append takes two lists, not a list and a single element. You need to put a list around the pair before using it in append.

Currently the pair is being taken as part of the list, which makes the list improper and causes the second append to fail since improper lists don't exactly have an end to append to.

Upvotes: 1

Related Questions