3

For a university project i have to make a game based in a matrix in the pretty big language, the matrix is being defined as a multidimensional vector. I need to set a single element of the matrix, my code is:

(require racket/vector)

(define test (make-vector 4 (make-vector 4 0)))

(define (matrix-set matrix row column value)
   (vector-set! (vector-ref matrix row) column value)
)
(display test)(newline)
(matrix-set test 0 0 1)
(display test)

And outputs this:

#(#(0 0 0 0) #(0 0 0 0) #(0 0 0 0) #(0 0 0 0))
#(#(1 0 0 0) #(1 0 0 0) #(1 0 0 0) #(1 0 0 0))

I have searched the racket documentation and only found functions that set an element by making a new matrix, this and this questions too.

Why is the function setting the whole column instead of only the element?

What can be done to resolve it?

Community
  • 1
  • 1
Daniel P.
  • 92
  • 1
  • 7

2 Answers2

3

(make-vector 4 (make-vector 4 0)) is the same as:

(let ((x (make-vector 4 0)))
  (vector x x x x))

That is, (make-vector 4 0) is called only once, and its value is used for all 4 slots of the outer vector.

What you need is something like (for/vector ((i 4)) (make-vector 4 0)), which will call (make-vector 4 0) (and create a distinct vector) for each element of the outer vector.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • Instead of writing `#(#0=#(0 0 0 0) #0# #0# #0#)`, which is very confusing, would it be better to write something like `(define o1 (vector 0 0 0 0))` and then write that the `test` vector is `(vector o1 o1 o1 o1)`, to explain that each `o1` points to the same object? – Alex Knauth May 26 '16 at 02:50
  • @AlexKnauth Thanks, that makes sense. I was trying to explain the concept of object aliasing and clearly I wasn't doing a fantastic job of it. :-) – C. K. Young May 26 '16 at 02:51
  • 1
    That's also how it's explained in the [Objects and Imperative Update](http://docs.racket-lang.org/reference/eval-model.html#%28part._.Objects_and_.Imperative_.Update%29) section of the documentation. – Alex Knauth May 26 '16 at 02:52
  • Thanks for the tip, but that isn't what i'm asking. The question is why my function to modify the elements is modifying all the column. – Daniel P. May 26 '16 at 13:22
  • 1
    @Daniel My answer explains why: all your columns are actually aliased to the same object. I also provided a solution. – C. K. Young May 26 '16 at 13:26
  • 2
    So instead of making a vector of vectors, i made a vector of references to the same vector? – Daniel P. May 26 '16 at 13:36
  • @Daniel Pretty much. – C. K. Young May 26 '16 at 13:39
  • 1
    So the vector is the problem, the function that sets the element is ok? – Daniel P. May 26 '16 at 13:51
  • Might this be a case where the familiarity of iteration makes for greater code clarity than thunking? – ben rudgers May 26 '16 at 18:05
  • @benrudgers Thanks! Implemented. – C. K. Young May 26 '16 at 18:38
  • I added an answer after griping about yours. I think showing the thunk is useful. – ben rudgers May 26 '16 at 18:42
  • 1
    racket says for/vector isn't defined, but i managed to solve this thanks to you. – Daniel P. May 26 '16 at 19:17
  • @DanielP. Which version of Racket are you running? Are you using `#lang racket` or one of the teaching languages? – ben rudgers May 26 '16 at 19:43
  • @benrudgers The question already says he's using Pretty Big; I just didn't know that Pretty Big doesn't have `for*` and I didn't bother to check. :-P Of course, Pretty Big doesn't have `thunk*` either. – C. K. Young May 26 '16 at 19:59
  • Missed that. I'd never noticed "pretty big" in DrRacket before. – ben rudgers May 26 '16 at 20:08
0

An alternative approach is to use the vector generating iterator for/vector. I'm not sure the syntax is any cleaner than using thunks, but conceptually, I feel it is more familiar and perhaps simpler.

(define test (for/vector ([i (range 4)])(make-vector 4 0)))

(matrix-set test 0 0 1)  ; '#(#(1 0 0 0) #(0 0 0 0) #(0 0 0 0) #(0 0 0 0))

A third alternative, that isn't as Racket like, would be using do.

(define test
  (do ((vec (make-vector 4))
       (i 0 (+ i 1)))
      ((= i 4) vec)
    (vector-set! vec i (make-vector 4 0))))

On the other hand, sometimes it's worth knowing a bit about do because do don't require memorizing a different command for each type since do does what it does on vectors, lists, and hashes.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
ben rudgers
  • 3,647
  • 2
  • 20
  • 32