Re-using complex data structures
Sometimes you want your functions to behave in a âfunctionalâ way, i.e. return fresh results without side effects, sometimes you want them to re-use and modify existing data in a destructive way - consider the difference between append
and nconc
for an example.
Well, you can have your cake and eat it too, by using optional (or keyword) parameters. Hereâs an example: Letâs assume youâre writing a function complex-matrix-stuff
which takes two matrices m1
and m2
as its arguments and computes and returns a resulting matrix the size of which depends on m1
and m2
, i.e. for a fresh result youâll need an empty matrix whichâll be created by, say, (make-appropriate-result-matrix-for m1 m2)
.
The classical textbook way to implement this function will more or less look like this:
(defun complex-matrix-stuff (m1 m2)
(let ((result (make-appropriate-result-matrix-for m1 m2)))
;; ... compute storing the results in RESULT
result))
And youâll use it like this:
(setq some-matrix (complex-matrix-stuff A B))
But why not write it like so:
(defun complex-matrix-stuff (m1 m2
&optional
(result
(make-appropriate-result-matrix-for m1 m2)))
;; ... compute storing the results in RESULT
result)
Now you have it both ways. You can still âmake up resultsâ on the fly as in:
(setq some-matrix (complex-matrix-stuff A B))
But you can also (destructively) re-use previously allocated matrices:
(complex-matrix-stuff A B some-appropriate-matrix-I-built-before)
Or use your function like this:
(setq some-other-matrix
(complex-matrix-stuff A B some-appropriate-matrix-I-built-before))
in which case youâll end up with:
* (eq some-other-matrix some-appropriate-matrix-I-built-before)
T
Using ADJUST-ARRAY
instead of consing up new sequences with SUBSEQ
Most CL functions operating on sequences will accept start
and end
keywords so you can make them operate on a sub-sequence without actually creating it, i.e. instead of
(count #\a (subseq long-string from to))
you should of course use
(count #\a long-string :start from :end to)
whichâll yield the same result but not create an unnecessary intermediate sub-sequence.
However, sometimes it looks like you canât avoid creating new data. Consider a hash table the keys of which are strings. If the key youâre looking for is a sub-string of another string youâll most likely end up with
(gethash (subseq original-string from to)
hash-table)
But you donât have to. You can create one displaced string and reuse it multiple times with adjust-array
:
(let ((substring (make-array 0
:element-type 'character
:displaced-to ""
:displaced-index-offset 0)))
;; more code
(gethash
(adjust-array substring (- to from)
:displaced-to original-string
:displaced-index-offset from)
hash-table)
;; even more code
)
Page source: misc.md