When I first encountered Lisp a couple of months ago, I decided that
it was the ideal language for numerical computations (at least for the
kind I am doing). The only thing I disliked were the arrays: they
were column-major (while I got used to thinking column-major), and I
found it messy to interface them to foreign functions.
Now I realize that I have been too quick to judge Lisp arrays: they
are neat and well designed. I figured out a few ways to work around
some things, and I would like advice on how to do things right.
1. reduce and arrays
Suppose I want to find maxima, minima etc of arrays. Is this the
right way?
(defun array-reduce (function array)
(reduce function (make-array (array-total-size array)
:element-type (array-element-type array)
:displaced-to array)))
(array-reduce #'max array) ; find maximum
2. multidimensional arrays and FFI
Most implementations are happy to provide a pointer to arrays with
certain element types (I know, you better stop the GC (CMUCL) or pin
down the array (SBCL), I will disregard these things in the code
below). Now consider this code:
(defparameter *a-m* (make-array '(3 4)
:element-type '(unsigned-byte 32)
:initial-element 0))
(defparameter *a-v* (make-array 12 :displaced-to *a-m*
:element-type '(unsigned-byte 32)))
(sb-sys:vector-sap *a-v*) ; won't work
(defparameter *b-v* (make-array 12
:element-type '(unsigned-byte 32)
:initial-element 0))
(defparameter *b-m* (make-array '(3 4) :displaced-to *b-v*
:element-type '(unsigned-byte 32)))
(sb-sys:vector-sap *b-v*) ; will work
Apparently, if an array was created flat, then I can easily get its
address. But when it was created flat using displacement, I can't.
Possible workaround: for making every array, create one with a flat
original array and then displace.
(defun make-array-with-flat-original (dimensions element-type)
(if (= (length dimensions) 1)
(make-array dimensions :element-type element-type)
(let ((original (make-array (reduce #'* dimensions)
:element-type element-type)))
(make-array dimensions :element-type element-type
:displaced-to original))))
(defun find-original-array (array)
"Chase down original array."
(do ((a array))
(nil)
(let ((displacement (array-displacement a)))
(if displacement
(setf a displacement)
(return-from find-original-array a)))))
(sb-sys:vector-sap (find-original-array
(make-array-with-flat-original
'(3 4)
'double-float))) ; works fine
On Aug 17, 11:20 am, Tamas Papp <······@gmail.com> wrote:
[...]
> 1. reduce and arrays
> Suppose I want to find maxima, minima etc of arrays. Is this the
> right way?
> (defun array-reduce (function array)
> (reduce function (make-array (array-total-size array)
> :element-type (array-element-type array)
> :displaced-to array)))
It works and it's elegant. Are you having a problem with it?
The only thing I might do is change it so it looks like this:
(defun array-reduce (function array &rest args &key &allow-other-keys)
(apply #'reduce function
(make-array (array-total-size array)
:element-type (array-element-type array)
:displaced-to array)
args))
which allows you to pass the various keyword arguments to REDUCE.
However, I don't think most of them are super-relevant to this
function in the first place, so it's no big deal.
> 2. multidimensional arrays and FFI
[...]
> (defun find-original-array (array)
> "Chase down original array."
> (do ((a array))
> (nil)
> (let ((displacement (array-displacement a)))
> (if displacement
> (setf a displacement)
> (return-from find-original-array a)))))
The way you're using DO here looks really obscure to me. It's perfect
job for recursion.
(defun find-original-array (array)
(let ((displacement (array-displacement array)))
(if displacement
(find-original-array displacement)
array)))
Cheers,
Pillsy
Pillsy <·········@gmail.com> writes:
> On Aug 17, 11:20 am, Tamas Papp <······@gmail.com> wrote:
> [...]
>> 1. reduce and arrays
>
>> Suppose I want to find maxima, minima etc of arrays. Is this the
>> right way?
>
>> (defun array-reduce (function array)
>> (reduce function (make-array (array-total-size array)
>> :element-type (array-element-type array)
>> :displaced-to array)))
>
> It works and it's elegant. Are you having a problem with it?
>
> The only thing I might do is change it so it looks like this:
>
> (defun array-reduce (function array &rest args &key &allow-other-keys)
> (apply #'reduce function
> (make-array (array-total-size array)
> :element-type (array-element-type array)
> :displaced-to array)
> args))
>
> which allows you to pass the various keyword arguments to REDUCE.
> However, I don't think most of them are super-relevant to this
> function in the first place, so it's no big deal.
Thanks, I will think about it. :from-end could be relevant, but not
the rest. Am I mistaken in thinking that using apply could be less
efficient compared to funcall?
>
>> 2. multidimensional arrays and FFI
> [...]
>> (defun find-original-array (array)
>> "Chase down original array."
>> (do ((a array))
>> (nil)
>> (let ((displacement (array-displacement a)))
>> (if displacement
>> (setf a displacement)
>> (return-from find-original-array a)))))
>
> The way you're using DO here looks really obscure to me. It's perfect
> job for recursion.
>
> (defun find-original-array (array)
> (let ((displacement (array-displacement array)))
> (if displacement
> (find-original-array displacement)
> array)))
Yes, but this is not tail-recursive. Not a big deal for small depths
I guess.
I have been working on this and realized that I might need the sum of
index offsets. I came up with a version using tagbody (because do is
indeed obscure here), is this bad style? If so, improvements would be
appreaciated, especially non-recursive ones.
(defun displace-array (array dimensions index-offset)
"Make a displaced array from array with the given dimensions and the
index-offset and the same element-type as array."
(make-array dimensions :element-type (array-element-type array)
:displaced-to array :displaced-index-offset index-offset))
(defun find-original-array (array)
"Find the original parent of a displaced array, return this and the
sum of displaced index offsets."
(let ((sum-of-offsets 0))
(tagbody
check-displacement
(multiple-value-bind (displaced-to displaced-index-offset)
(array-displacement array)
(format t "~a ~a~%" displaced-to displaced-index-offset)
(when displaced-to
(setf array displaced-to)
(incf sum-of-offsets displaced-index-offset)
(go check-displacement))))
(values array sum-of-offsets)))
;; example
(defparameter *a* (make-array 19))
(defparameter *b* (displace-array *a* '(4 4) 2))
(defparameter *c* (displace-array *b* 7 3))
(multiple-value-bind (original index-sum) (find-original-array *c*)
(values (eq *a* original) index-sum)) ; => T,5
Thanks,
Tamas
On Aug 17, 12:50 pm, Tamas Papp <······@gmail.com> wrote:
> Pillsy <·········@gmail.com> writes:
[...]
> > The only thing I might do is change it so it looks like this:
> > (defun array-reduce (function array &rest args &key &allow-other-keys)
> > (apply #'reduce function
> > (make-array (array-total-size array)
> > :element-type (array-element-type array)
> > :displaced-to array)
> > args))
> > which allows you to pass the various keyword arguments to REDUCE.
> > However, I don't think most of them are super-relevant to this
> > function in the first place, so it's no big deal.
> Thanks, I will think about it. :from-end could be relevant, but not
> the rest. Am I mistaken in thinking that using apply could be less
> efficient compared to funcall?
It could be, but I doubt it's likely to be particularly important in
most cases. It's only going to come up once per call to ARRAY-REDUCE,
after all.
> > The way you're using DO here looks really obscure to me. It's perfect
> > job for recursion.
> > (defun find-original-array (array)
> > (let ((displacement (array-displacement array)))
> > (if displacement
> > (find-original-array displacement)
> > array)))
> Yes, but this is not tail-recursive. Not a big deal for small depths
> I guess.
Sure it is! Look at it again, and if you still don't believe me, try
tracing it.
This all assumes you aren't using declarations that prevent tail-call
elimination (high levels of
DEBUG will do this in SBCL):
http://www.sbcl.org/manual/Debug-Tail-Recursion.html
> I have been working on this and realized that I might need the sum of
> index offsets. I came up with a version using tagbody (because do is
> indeed obscure here), is this bad style? If so, improvements would be
> appreaciated, especially non-recursive ones.
I don't find it particularly ugly in this case, but then I usually
worked in Fortran before discovering CL. ;)
I still think the (tail-)recursive version is nicer:
(defun find-original-array (array)
(labels ((recurse (array total-offset)
(multiple-value-bind (displaced-to offset)
(array-displacement array)
(if displaced-to
(recurse displaced-to (+ offset total-offset))
(values displaced-to total-offset)))))
(recurse array 0)))
Anyway, for those times you are using a TAGBODY and a LET one inside
the other, you can fold them both into PROG.
Cheers,
Pillsy
Tamas Papp <······@gmail.com> writes:
> I have been working on this and realized that I might need the sum of
> index offsets. I came up with a version using tagbody (because do is
> indeed obscure here), is this bad style? If so, improvements would be
> appreaciated, especially non-recursive ones.
[snip]
Here's Erik Naggum's version:
http://groups.google.com/group/comp.lang.lisp/msg/f527904bd5167a83
Zach