HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppModerate

Implementation of Nd vectors using valarray

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationvectorsusingvalarray

Problem

This is my code which implements Nd vectors. I chose valarray as a base of the class. Is this a good idea or I simply need to make a typedef std::valarray gvector instead of writing a class?
Please review this code.

```
//gvector.h
#ifndef GVECTOR_H_
#define GVECTOR_H_

#include
#include
#include
#include

double sqr(const double &); //square

typedef std::valarray coordinates;

std::ifstream & operator >> (std::ifstream &, coordinates &);
std::ostream & operator > (std::ifstream &, gvector &);
std::ostream & operator > (std::ifstream &, gvector &);
friend std::ostream & operator << (std::ostream &, const gvector &);

coordinates crds;

public:

gvector(): crds() {};

gvector(const size_t & d): crds(d) {}; //set dimension
gvector(const coordinates & c): crds(c) {};
gvector(const gvector & v): crds(v.coords()) {};

//Initialize gvector by its start-point and end-point:
gvector(
const coordinates & start,
const coordinates & end
): crds(end - start) {};

size_t dim() const; //show dimension
coordinates coords() const; //show
double len() const; //show length

void set_dim(const size_t &);
void set_coords(const coordinates &);
void set_coords(const coordinates &, const coordinates &); //Change start-point and end-point of gvector

double & operator [] (const size_t &);
double const & operator [] (const size_t &) const;

//Opposite vector
gvector operator - () const;

gvector & operator = (const gvector &);
gvector & operator += (const gvector &);
gvector & operator -= (const gvector &);
gvector & operator *= (const double &);

};

inline size_t gvector::dim() const { return crds.size(); }
inline coordinates gvector::coords() const { return crds; }

inline double gvector::len() const {
return sqrt(crds.apply(sqr).sum());
}

inline void gvector::set_dim(const size_t & d) {

Solution

std::valarray by itself only acts like a 1D array. The ` header includes a few ancillary classes to do this though. More importantly, valarray itself includes direct support for those classes.

For your purposes, the most important of these are probably
std::slice and std::gslice. std::slice allows you to address a slice of the array and address it (about) like you'd index along a dimension of a multi-dimensional array.

For example, let's assume we have an array of 28 items that we want to treat as a 2D array (with dimensions 4x7). While it's probably not entirely obvious from the standard1
std::slice makes it fairly easy to do this. Let's say we want to walk across it, and look at one "column" of the 2D array at a time. So, we're going to create 7 columns (one at a time) each containing 4 items.

Now, the trick to this: the slices you create this way act as objects in themselves. You don't get access to the individual elements in the slice; you get access to the slice as a whole. To assign one value to all the elements in the slice, you do not walk through the slice with a
for loop and assign to each element individually. Rather, you assign to the entire slice at once (which will assign that value to all the elements in the slice).

If you want to assign separate/unrelated values to the items in the slice, you can do that--but you need to use some other slice or valarray as the source. For example, let's create a valarray of 28 items, which we'll treat as a 2D array of 4x7 (4 rows of 7 columns each). For the sake of argument, we'll start with a valarray of 4 items holding 1, 2, 3, 4. We'll then assign that to the columns in the matrix. To keep things from getting too boring, we'll multiply that by the column number before assigning it, so the first row should count across by 1's, the second row by 2's, the third row by 3's and the fourth row by 4's.

std::valarray data(rows * cols);
std::valarray filler = { 1, 2, 3, 4 };

for (int i = 0; i < cols; i++)
    data[std::slice(i, rows, cols)] = filler * i;

    // show the data:
    for (int i = 1; i <= t.size(); i++) {
        std::cout << t[i-1] << "\t";
        if (i % cols == 0)
            std::cout << "\n";
    }


The result should look like this:

0       1       2       3       4       5       6
0       2       4       6       8       10      12
0       3       6       9       12      15      18
0       4       8       12      16      20      24


As we can see, this fits the expected results, indicating that our
slice has allowed us to address columns of the array, exactly as intended.

So, the bottom line: no, you can't (quite) just use
std::valarray to do N-dimensional array access--but you can do it with valarray + the other classes included in it header. As such, I'm not sure your gvector` class really adds any capability that isn't already present.

  1. Okay, almost nothing in the C++ standard is really obvious, but this is probably even harder to figure out than most things.

Code Snippets

std::valarray<int> data(rows * cols);
std::valarray<int> filler = { 1, 2, 3, 4 };

for (int i = 0; i < cols; i++)
    data[std::slice(i, rows, cols)] = filler * i;

    // show the data:
    for (int i = 1; i <= t.size(); i++) {
        std::cout << t[i-1] << "\t";
        if (i % cols == 0)
            std::cout << "\n";
    }
0       1       2       3       4       5       6
0       2       4       6       8       10      12
0       3       6       9       12      15      18
0       4       8       12      16      20      24

Context

StackExchange Code Review Q#27527, answer score: 11

Revisions (0)

No revisions yet.