Closed robert-strandh closed 1 year ago
Here is a complete version with improved comments and with appropriate markup:
;;; This function exists solely for efficiency reasons. Given keyword
;;; arguments with sequences of values such as
;;;
;;; (:KEY₁ (VALUE₁₁ VALUE₁₂ …) :KEY₂ (VALUE₂₁ VALUE₂₂ …) …)
;;;
;;; , this function constructs a keyword argument list of the form
;;;
;;; (:KEY₁ VALUE₁₁ :KEY₂ VALUE₂₁ …)
;;;
;;; and a function that destructively replaces each value with the
;;; next value. This function returns two values. The first value is
;;; initially (:KEY₁ NIL :KEY₂ NIL …) and the second values is the
;;; destructive function. An initial call to the destructive function
;;; replaces the NILs in the first value by VALUE₁₁ and VALUE₂₁. Once
;;; all the VALUES of a list have been exhausted, a call to the
;;; destructive function inserts a NIL as the VALUE.
(defun make-keyword-arguments (multi-keyword-arguments)
(loop for (keyword values) on multi-keyword-arguments by #'cddr
;; The following two clauses construct the initial list
;;(:KEY₁ NIL :KEY₂ NIL …) to be retuned as the first value.
collect keyword into result
collect nil into result
;; This clause constructions a list
;; ((VALUE₁₁ VALUE₁₂ …) (VALUE₂₁ VALUE₂₂ …) …)
;; to be traversed by the destructive function.
collect values into values-list
finally (return (values result
(lambda ()
(loop ;; Loop over subsequent tails of
;; the returned list so that the
;; CAR of the first CONS cell of the
;; tail should be updated.
for rest on (cdr result) by #'cddr
;; And loop over subsequenct tails
;; of the values list, so that the
;; CAR of of the first CONS cell of
;; the tail should be popped.
for values on values-list
do (setf (car rest) (pop (car values)))))))))
Don't feel you need to take the iterative version over the existing one. It is just how I may have written it myself.
I might have written make-keyword-arguments like this:
(defun make-keyword-arguments (multi-keyword-arguments) (loop for (keyword values) on multi-keyword-arguments by #'cddr collect keyword into result collect nil into result collect values into values-list finally (return (values result (lambda () (loop for rest on (cdr result) by #'cddr for values on values-list do (setf (car rest) (pop (car values))))))))) It has fewer lexical variables and its control structure is simpler.