tags:

views:

130

answers:

1
+1  Q: 

Matrix Add Lisp...

    (defun (matrix-add m1 m2)

  (defun (matrix-add-row r1 r2 res-row)
    (if (and (not (null? r1)) (not (null? r2)))
    (matrix-add-row (cdr r1) (cdr r2)
            (cons (+ (car r1) (car r2)) res-row))
    (reverse res-row)))

  (defun (matrix-add-each m1 m2 res)
    (if (and (not (null? m1)) (not (null? m2)))
    (let ((res-row (matrix-add-row (car m1) (car m2) ())))
      (matrix-add-each (cdr m1) (cdr m2) (cons res-row res)))
    (reverse res)))
  (matrix-add-each m1 m2 ()))

Hi I matrix addition on a piece of paper but it's now working when I type in lisp.. What's wrong?

+3  A: 

First off, the syntax is neither common lisp nor scheme, the two main lisp dialects which are in common use.

In common-lisp syntax this works as expected:

(defun matrix-add (m1 m2)
  (labels 
    ((matrix-add-row (r1 r2 res-row)
      (if (and (not (null r1)) (not (null r2)))
        (matrix-add-row (cdr r1) (cdr r2)
          (cons (+ (car r1) (car r2)) res-row))
        (reverse res-row)))

    (matrix-add-each (m1 m2 res)
      (if (and (not (null m1)) (not (null m2)))
        (let ((res-row (matrix-add-row (car m1) (car m2) ())))
          (matrix-add-each (cdr m1) (cdr m2) (cons res-row res)))
        (reverse res))))

    (matrix-add-each m1 m2 ())))

> (matrix-add `((1 2) (3 4)) `((10 20) (30 40)))
((11 22) (33 44))

The are a few things which are a bit more verbose than required here.

Firstly, in common lisp, nil is false, and and returns the last argument which is not false.

> (and () `(1))
NIL
> (and `(1) `(1))
(1)
> (and `(1) `(3))
(3)
> (and `(1) ())
NIL

so in your case, in common lisp, you don't need all the not null tests.

Secondly, it's common (more so in scheme than in lisp) to use tail recursive functions rather than accumulator based ones, so rather to add two lists without using the library functions you would typically take two lists, and return a list whose head is the sum of the heads of the two lists, and whose tail is the result of adding the elements of each lists tails. So instead of taking three arguments (input, input, accumulator), then reversing the accumulator, you would write the function as taking two arguments and returning the part of the result based on those two arguments.

  (defun matrix-add-row (r1 r2)
    (if (and r1 r2)
      (cons (+ (car r1) (car r2)) (matrix-add-row (cdr r1) (cdr r2)))
      ()))

> (matrix-add-row `(1 2 3 4) `(10 20 30 40))
(11 22 33 44)

But this pattern of applying a function to the car of lists and applying the function to the rest of the list is very common, so there are a set of library functions for it - the map family.

So you would tend to use map functions to operate on simple lists rather than writing your own. The #' reader macro (short for function) extracts a function from a symbol, so to apply the + function to the elements of two lists you can use mapcar:

> (mapcar #'+ `(1 2 3 4) `(10 20 30 40))
(11 22 33 44)

This removes the complexity of having to write the boiler-plate for the recursive application, it may be a more efficient, and expresses the higher level intent.

Since you no longer need the accumulator, you don't need to define matrix-add-each, and instead can simply return the result of applying the add-row function to each row in the matrix:

(defun matrix-add (m1 m2) 
  (flet ((matrix-add-row (r1 r2) (mapcar #'+ r1 r2)))
    (mapcar #'matrix-add-row m1 m2)))

or you can even use a lambda rather than defining the function separately, though it might be a bit easier to read the split-up version to start with:

(defun matrix-add (m1 m2)
  (mapcar (lambda (r1 r2) (mapcar #'+ r1 r2)) m1 m2))

Though for a 'real' function you might want to check that the matrices are the same size and the rows are the same size, which would be more complicated than a single line function. But for real matrix code you might want to be using arrays rather than lists anyway.

Once you start thinking in terms of functions over lists, and functions applying functions over lists, you'll start to find you have to do much less work to get lisp to do what you want it to.

As comments pointed out, defun creates a global function binding, flet and labels at local scope.

Pete Kirkham
DEFUN is not for creating local functions. Either make the functions top-level functions, or use LABELS.
Rainer Joswig