userx1234
userx1234

Reputation: 43

matrix data structure and function parameters

typedef struct {
    size_t rows;
    size_t cols;
    double* m;
}MATRIX;

for some functions that take a matrix as a parameter could be rewritten for a vector

int matrix_negate(MATRIX* const A) {
    size_t i = 0;

    for (; i < A->rows * A->cols; i++) {
        A->m[i] = -A->m[i];
    }
    return 0;
}

can be written to take a vector (double*) as

int vector_negate(double* const A, const size_t length) {
    size_t i = 0;

    for (; i < length; i++) {
        A[i] = -A[i];
    }
    return 0;
}

Now I have more uses of the same function, I can negate a row, I could negate a certain length of a row, and I could use it to negate a certain entry of the matrix:

MATRIX A = { ... };
 vector_negate(A.m,A.cols*A.rows);  //negate whole matrix
 vector_negate(A.m[A.cols*row],A.cols);         //negate a row
 vector_negate(A.m[A.cols*row],some_length);    //negate a row of some length
 vector_negate(A.m[(A.cols*row)+col],1);        //negate a entry

where as my negate matrix function only worked for a matrix, to do the same as above I would have to create another matrix and set the pointer to start at the row/entry (share pointers) and setup length, for example:

MATRIX A = matrix_init(&A,3,3);
MATRIX RowM = { 0 };
RowM.m = A.m[A.cols*row];
RowM.rows = 1;
RowM.cols = 3;

matrix_negate(&RowM);

The same could be done with the add function, since we are adding corresponding 1-d arrays , etc I have a library of functions, if I rewrite my functions to take double *M, size_t Mrows, size_t Mcols ill have more uses of the same function, and save memory. Passing the data as structure in the function parameters doesn't seem all the advantageous. Am I missing something here?

Upvotes: 0

Views: 107

Answers (3)

KamilCuk
KamilCuk

Reputation: 142025

Passing the data as structure in the function parameters doesn't seem all the advantageous.

Providing a simple to use, clear, readable and understandable API is a lot more advantageous than caring about performance and memory usage.

Your main concern seems to be code-reuse vs efficiency. There's nothing wrong with doing a lot of small wrappers:

void vector_negate(double*, size_t);

// small wrappers in matrix.h so that compiler may inline them
static void matrix_negate(MATRIX *m) {
      vector_negate(m->data, m->rows * m->cols);
}
static void matrix_negate_row(MATRIX *m, size_t row) {
      vector_negate(&m->data[m->cols * row], m->rows);
}
static void matrix_negate_row_part(MATRIX *m, size_t row, size_t start ,size_t stop) {
      // check input - `return EINVAL` or do assertions, depending on api
      assert(stop >= start);
      assert(stop < m->cols);
      assert(m->cols < stop);
      vector_negate(&m->data[m->cols * row + start], stop - start);
}
static void matrix_negate_entry(MATRIX *m, size_t rows, size_t col) {
      // etc...
}

When someone else will be introduced to your code and will see vector_negate(&A.m[A.cols*5],A.cols); he will think "och, that's some negation of something, now I need to think of what, and what does 5 represent?". When he will see matrix_negate_row(A, 5) he will immediately know it's negating 5th row.

Code readability will cause fewer errors in your code. Clear and self-understandable API will make your library easy to use and learn. Small wrappers will facilitate code-reuse. Error checking may be added to wrappers, providing safer code. And, if you happen to change the internal representation of MATRIX, the user of the library will not have to change much, as all is wrapped in matrix_* functions.

Remember about rules of optimization. You may want to research object oriented programming - your matrix is a composition of a vector, it's natural it will introduce its own operations on top of basic vector operations.

[w]ill have more uses of the same function, and save memory

Seems false. In a big program if a similar operation is called a lot of times, generally creating a function from it will save memory, as the compiler will be able to reuse the same code. Only profiling the code for both cases will bring you a definite answer. The compiler may also sacrifice code size for efficiency, inlining the function, doing the same as you. Let the compiler do such decisions, as different compiler options (-Os vs -Ofast) affect what the user wants - concentrate on writing readable code instead.

Upvotes: 2

userx1234
userx1234

Reputation: 43

I didn't want to rewrite the same code, instead reuse it, solution is to use macros in C:

int vector_negate(double* const A, const size_t length);
#define matrix_negate(A) vector_negate((A).m,(A).cols * (A).rows)

Upvotes: 1

Limina102
Limina102

Reputation: 1087

It is possible by treating all vectors as 1 x n matrices - writing a tiny helper function for that.

However, in my opinion it is difficult to do this kind of automatic transformation in C. The helper function needs to be called explicitly by human hand. Like:

MATRIX *vec2matrix(double *const A) {
        ...
}

#define DEF_FUNC(f) \
        int f##_v(double *const A) { \
                MATRIX *m = vec2matrix(A); \
                int result = f(m); \
                free(m); \
                return result; \
        } \
        \
        int f(MATRIX *const A)

DEF_FUNC(matrix_negate) {
        ...
}

// now matrix_negate & matrix_negate_v defined

Upvotes: 1

Related Questions