Need advice on how to print a matrix in lisp
Asked Answered
B

4

1

I have a matrix defined so if I do this

(format t "~a" (get-real-2d 0 0))

it prints out the element in the first row first column

and if I do this

(format t "~a" (get-real-2d a 0 1))

it prints out the element in first row second column

and if I do this

(format t "~a" (get-real-2d a 1 0))

it prints out the element in second row first column.

The matrix a looks like this

a =
((0 1 2)
(3 4 5)
(6 7 8))

and I was hoping you can show me exactly how to write a dotimes loop or other loop that would in as few lines as possible would print out the matrix using the get-real-2d function so the output looks like this:

0 1 2 
3 4 5
6 7 8

I'm just hoping you can show me a slick loop that would be real small that I can use to print matrices that I can use in my lisp library something real professional looking, like one that would use only variables. Something like:

(format t "~a" (get-real-2d i j))

instead of a bunch of:

(format t "~a" (get-real-2d 0 0))
(format t "~a" (get-real-2d 0 1))
(format t "~a" (get-real-2d 0 2))

;;;;LATEST EDIT;;; to make this simple I call

(defparameter a (create-mat 3 3 +32fc1+))

to create a 3x3 matrix - create-mat is a wrapper for opencv's cvCreateMat

the output from that command at repl is

(defparameter a (create-mat 3 3 +32fc1+))
A
CL-OPENCV> a
#.(SB-SYS:INT-SAP #X7FFFD8000E00)

i/e the variable a is a pointer to the 3x3 matrix

then I run

(defparameter data (cffi:foreign-alloc :float :initial-contents 
          '(0.0f0 1.0f0 2.0f0 3.0f0 4.0f0 5.0f0 6.0f0 7.0f0 8.0f0)))

to create the data for the matrix - which I next will allocate to the matrix

the output from that command at repl is

CL-OPENCV> (defparameter data (cffi:foreign-alloc :float :initial-contents 
          '(0.0f0 1.0f0 2.0f0 3.0f0 4.0f0 5.0f0 6.0f0 7.0f0 8.0f0)))
DATA
CL-OPENCV> data
#.(SB-SYS:INT-SAP #X7FFFD8000E40)

i/e the variable a is data pointer to the data ill add to the matrix

then I call..

(set-data a data 12) to add the data to the matrix - set-data is a wrapper for opencv's cvSetData

so now when I run - (get-real-2d is a wrapper for opencv's cvGetReal2d)

(get-real-2d a 0 0)  it gets the element of matrix a at row 0 col 0 which is 0.0d0

the output from that command at repl is

CL-OPENCV> (get-real-2d a 0 0)
0.0d0

and now when I run

(get-real-2d a 0 1)  it gets the element of matrix a at row 0 col 1 which is is 0.0d0

the output from that command at repl is

CL-OPENCV> (get-real-2d a 0 1)
1.0d0

and when I run this loop

 (dotimes (i 3)
  (dotimes (j 3)
(format t "~a~%" (get-real-2d a i j))))

the output from that command at repl is

CL-OPENCV> (dotimes (i 3)
  (dotimes (j 3)
(format t "~a~%" (get-real-2d a i j))))
0.0d0
1.0d0
2.0d0
3.0d0
4.0d0
5.0d0
6.0d0
7.0d0
8.0d0
NIL

but when I try your method @Svante

(dotimes (i 3)
  (dotimes (j 3)
(format t "~{~{~a~^ ~}~%~}" (get-real-2d a i j))))

I get error:

The value 0.0d0 is not of type LIST.
    [Condition of type TYPE-ERROR]

because the output of 1 run of get-real-2d is just a 1 number float i/e

CL-OPENCV> (get-real-2d a 0 0)
0.0d0

with that info can you help me print the matrix so it looks like this

0.0d0 1.0d0 2.0d0 
3.0d0 4.0d0 5.0d0
6.0d0 7.0d0 8.0d0
Bellis answered 21/9, 2013 at 4:23 Comment(3)
Why define matrix as a list? Array is a lot better for this purpose. There are a handful of libraries that provide some implementation of matrix and functions you may need to apply to it. Why not just use those? If your matrix is a list, then something like this: (format t "~{~{~s~^ ~}~^~%~}" matrix) should do the job, but really, consider first two earlier questions.Cordellcorder
@wvxvw could you please look at my "Latest Edit" to my questionBellis
Well, you should've told what the format of the matrix is. You printed it as if it was a tree (a list containing other lists) when it is in fact a foreign object, most likely an array, but maybe a struct (I don't know how matrix is defined in OpenCV). Now, if you are after a long-term solution, I'd look into cffi:defcstruct and use its machinery to define a print-object method to print matrices. Else - I'd just have a function that prints the contents of the matrix to a stream, and then use it like here: lispworks.com/documentation/HyperSpec/Body/22_ced.htmCordellcorder
F
1

Your question can be understood in two ways, and that is why it has two solutions:

  • Define method for printing object of type matrix (in this case it may use the knowledge about the internal structure of matrix):

    (defmethod print-object ((matrix matrix) stream)
        (format stream "~{~{~a~^ ~}~%~}" matrix))
    

    Using format as is shown in the answers.

  • Define client function that can use the only method of your object - get-real-2d:

    (defun print-matrix (matrix dimension-x dimension-y)
        (dotimes (x dimension-x)
            (dotimes (y dimension-y)
                (princ (get-real-2d matrix x y))
                (princ #\Space))
            (princ #\Newline)))
    

    Just using dotimes.

Festatus answered 21/9, 2013 at 20:33 Comment(2)
YOU NAILED IT!!! PERFECT PERFECT PEFECT...THAT WAS A GREAT ANSWER Thank you very much Menschenkindlein you helped me out alot! = )Bellis
@user2735131, if this is the perfect answer, how about voting it up and marking it as the official answer so that Menschenkindlein can get credit.Obannon
T
5

You can do that directly in the format directive. The format instructions ~{ and ~} descend into a list structure.

(format t "~{~{~a~^ ~}~%~}" matrix)

The outer pair of ~{ ~} loops over the first level of the matrix, so that the directives inside get to see one row at a time. The inner pair of ~{ ~} loops over each such row, so that the directives inside get to see one element at a time. ~A prints that element. The part between ~^ and ~} gets printed only between executions of the loop body, not at the end. ~% emits a #\Newline.

EDIT as requested

Note that the ~{ ~} replace the looping, and that I named the variable matrix, not element. You need to put the entire matrix there, and it is supposed to be in the form of a nested list. I deduced this from your statement that a is ((0 1 2) (3 4 5) (6 7 8)). So, (format t "~{~{~a~^ ~}~%~}" a).

If the matrix happens not to be in the form of a nested list but rather some kind of array, you really need to loop over the indices. Nested dotimes forms should be sufficient at first:

(fresh-line)
(dotimes (i (array-dimension array 0))
  (dotimes (j (array-dimension array 1))
    (format t "~a " (aref array i j)))
  (terpri))

I don't know how your matrices map to arrays, so you will have to replace array-dimension and aref with your versions.

Tootsie answered 21/9, 2013 at 11:35 Comment(1)
could you please look at my "Latest Edit" to my questionBellis
O
1

This isn't hard, so I'd rather leave it to you to figure out, but here are some tips to make a "slick loop" Lisp-style. I would suggest one or more instances of mapc (or mapcar), rather than dotimes. This may feel odd if you're not used to functional programming, but once you're used to it, it's easier to read than dotimes, and you don't have to keep track of the indexes, so it can avoid errors. You really should learn to use mapcar/mapc if you aren't already familiar with them. They are cool. Or if you want to be really cool :-) you could use recursion to iterate over the matrix, but I think that for this purpose iterating using mapc will be easier to read. (But you should learn the recursive way for other jobs. If you find recursion confusing--I have no reason to think you do, but some people have trouble with it--my favorite tutorial is The Little Schemer.)

You may also want to use other format directives that allow you pad numbers with spaces if they don't have enough digits. The ~% directive may be useful as well. Peter Seibel has a very nice introduction to format.

Obannon answered 21/9, 2013 at 5:46 Comment(6)
still could use some sample code... been reading mapcar literature but no ideas so farBellis
I would actually give the opposite advice: if you don't already have a function defined that does exactly what you need, you would need to use lambda with mapc or mapcar. In those cases, using one of dotimes, dolist, or loop is usually more concise and readable (dolist or loop in ... do ... are exactly like mapc, loop in ... collect ... is just like mapcar.)Erechtheum
And explicit recursion should only be done if it can't be avoided, i.e. if the task at hand is not an iterative process. It's a low-level tool like goto.Erechtheum
@Obannon could you please look at my "Latest Edit" to my questionBellis
@Socialism could you please look at my "Latest Edit" to my questionBellis
Oops--when I mentioned the ugliness of dotimes (now edited out), I had in mind do. Sorry about that. It was late at night.Obannon
F
1

Your question can be understood in two ways, and that is why it has two solutions:

  • Define method for printing object of type matrix (in this case it may use the knowledge about the internal structure of matrix):

    (defmethod print-object ((matrix matrix) stream)
        (format stream "~{~{~a~^ ~}~%~}" matrix))
    

    Using format as is shown in the answers.

  • Define client function that can use the only method of your object - get-real-2d:

    (defun print-matrix (matrix dimension-x dimension-y)
        (dotimes (x dimension-x)
            (dotimes (y dimension-y)
                (princ (get-real-2d matrix x y))
                (princ #\Space))
            (princ #\Newline)))
    

    Just using dotimes.

Festatus answered 21/9, 2013 at 20:33 Comment(2)
YOU NAILED IT!!! PERFECT PERFECT PEFECT...THAT WAS A GREAT ANSWER Thank you very much Menschenkindlein you helped me out alot! = )Bellis
@user2735131, if this is the perfect answer, how about voting it up and marking it as the official answer so that Menschenkindlein can get credit.Obannon
S
1

Here are just the two dotimes loops that you were asking for. The only thing that you need to pay attention for is when to print spaces and when to print newlines.

(dotimes (i 3)
  (dotimes (j 3)
    (princ (get-real-2d a i j))
    (if (< j 2)
      (princ #\Space)
      (terpri))))

Alternatively, you might want to use the format directives for floating point printing to have the numbers always aligned in nice columns. You can choose between ~F that will never print an exponent, ~E that will always print one, and ~G that behaves according to the magnitude. Look for details here in the HyperSpec: http://www.lispworks.com/documentation/HyperSpec/Body/22_cc.htm.

Here's an example that uses ~F with a maximum field width of 5 and 1 fractional digit:

(dotimes (i 3)
  (dotimes (j 3)
    (format t "~5,1F" (get-real-2d a i j)))
  (terpri))
Socialism answered 21/9, 2013 at 21:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.