Related
Disjunctive Support :
let an itemset I formed by any non-empty subset from C
Supp(I) is the number of transactions containing at least one item of I for example i have :
vector < vector <int> > transactions = {{1, 2},
{2, 3, 7},
{4,6},
{1,5,8}};
vector<int> I ={1,2};
expected result :
Supp(I) = 3
but my code return Supp(I) = 1
#include <iostream>
#include <vector>
using namespace std;
int getSupport(vector < vector<int> > &transactions, vector<int> item) {
int ret = 0;
for(auto&row:transactions){
int i, j;
if(row.size() < item.size()) continue;
for(i=0, j=0; i < row.size();i++) {
if(j==item.size()) break;
if(row[i] == item[j]) j++;
}
if(j==item.size()){
ret++;
}
}
return ret;
}
int main() {
vector < vector <int> > transactions = {{1, 2},
{2, 3, 7},
{4,6},
{1,5,8}};
vector <int> I={1,2};
int D = getSupport(transactions, I);
printf("Disjunctive support = %d",D);
return 0;
}
You wrote that:
Supp(I) is the number of transactions containing at least one item of
I
But your implementation looks like you are trying to count transactions containing all the items of I.
Anyway if you still need implementation for the defintion you supplied, you can try this:
#include <iostream>
#include <vector>
int getSupport(std::vector<std::vector<int>> const & transactions, std::vector<int> const & item) {
int ret = 0;
for (auto const & tran : transactions) {
bool bFoundAtLeastOne{ false };
for (auto const & tran_elem : tran) {
for (auto const & item_elem : item)
{
if (tran_elem == item_elem)
{
ret++;
bFoundAtLeastOne = true;
break;
}
}
if (bFoundAtLeastOne) {
break;
}
}
}
return ret;
}
int main() {
std::vector<std::vector<int>> transactions =
{ { 1, 2 },
{ 2, 3, 7 },
{ 4,6 },
{ 1,5,8 } };
std::vector<int> I = { 1,2 };
int D = getSupport(transactions, I);
printf("Disjunctive support = %d\n", D);
return 0;
}
Some notes:
Better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?
I changed passing all the vectors by const& for efficiency and safety.
Given a vector, how to find all subsets of the vector accompanied by the original indices from the vector?
For example given the vector,
vector<float> numbers {1.3, 0.5, 2.4};
get subsets:
{}, {1.3}, {0.5}, {2.4}, {1.3, 2.4}, {0.5, 2.4}, {1.3, 0.5}, {1.3, 0.5, 2.4}
and corresponding indices for each subset:
{}, {0}, {1}, {2}, {0, 2}, {1, 2}, {0, 1}, {0, 1, 2}.
Is this a homework assignment? :-P
The following function will generate index subsets. The indices argument is just a temporary scratch variables.
void getIndexSubsets(
int l, int u, std::vector<int>* indices,
std::vector<std::vector<int>>* result) {
if (l == u) {
result->push_back(*indices);
} else {
int next = l + 1;
getIndexSubsets(next, u, indices, result);
indices->push_back(l);
getIndexSubsets(next, u, indices, result);
indices->pop_back();
}
}
It is used like this:
std::vector<float> numbers{1.3, 0.5, 2.4};
std::vector<int> indices;
std::vector<std::vector<int>> indexResult;
getIndexSubsets(0, numbers.size(), &indices, &indexResult);
The index subsets will be output in indexResult. Given the index subsets, we can compute the value subsets using this function:
std::vector<std::vector<float>> getValueSubsets(
const std::vector<float>& srcValues,
const std::vector<std::vector<int>>& src) {
std::vector<std::vector<float>> dst;
for (const auto& inds: src) {
std::vector<float> x;
for (auto i: inds) {
x.push_back(srcValues[i]);
}
dst.push_back(x);
}
return dst;
}
Invoking it like this:
std::vector<std::vector<float>> valueResult = getValueSubsets(numbers, indexResult);
The full solution reads like this.
#include <iostream>
#include <vector>
void getIndexSubsets(
int l, int u, std::vector<int>* indices,
std::vector<std::vector<int>>* result) {
if (l == u) {
result->push_back(*indices);
} else {
int next = l + 1;
getIndexSubsets(next, u, indices, result);
indices->push_back(l);
getIndexSubsets(next, u, indices, result);
indices->pop_back();
}
}
std::vector<std::vector<float>> getValueSubsets(
const std::vector<float>& srcValues,
const std::vector<std::vector<int>>& src) {
std::vector<std::vector<float>> dst;
for (const auto& inds: src) {
std::vector<float> x;
for (auto i: inds) {
x.push_back(srcValues[i]);
}
dst.push_back(x);
}
return dst;
}
template <typename T>
std::ostream& operator<<(std::ostream& s, const std::vector<T>& src) {
s << "{";
bool f = true;
for (auto x: src) {
s << (f? "" : " ") << x;
f = false;
}
s << "}";
return s;
}
int main() {
std::vector<float> numbers{1.3, 0.5, 2.4};
std::vector<int> indices;
std::vector<std::vector<int>> indexResult;
getIndexSubsets(0, numbers.size(), &indices, &indexResult);
std::vector<std::vector<float>> valueResult = getValueSubsets(numbers, indexResult);
for (int i = 0; i < indexResult.size(); i++) {
std::cout << "Subset inds=" << indexResult[i] << " values=" << valueResult[i] << std::endl;
}
return 0;
}
When executed, it will output this:
Subset inds={} values={}
Subset inds={2} values={2.4}
Subset inds={1} values={0.5}
Subset inds={1 2} values={0.5 2.4}
Subset inds={0} values={1.3}
Subset inds={0 2} values={1.3 2.4}
Subset inds={0 1} values={1.3 0.5}
Subset inds={0 1 2} values={1.3 0.5 2.4}
My goal is to create an immutable function (functional programming) using "accumulate" in C++. I created a dummy list that generates 1's based on the position I send, which will be 6. So the list at the start contains {1,1,1,1,1,1}. I tried using accumulate to somehow use the information on this list and make the fibonacci sequence into a new list. The result has to be {1,1,2,3,5,8}
Here is what I have.
list<int> immutableFibonacci(int position)
{
const size_t fixedListSize(position);
list<int> newList(position, int(1));
list<int> copyList;
list<int>::iterator it = newList.begin();
if (position <=2)
{
return newList; //returns {1,1} or {1}
}
while (position>0)
{
advance(it, 1);
sum = accumulate(newList.begin(),it, 0);
copyList.push_back(sum);
position--;
}
return copyList;
}
What I have so far will return copyList as {1,2,3,4,5,6}. Can someone please push me in the right direction on what to do? I tried researching quite a bit.
this method creates a 'container-like' object which exposes iterators via begin() and end()
#include <iterator>
#include <iostream>
struct fib_iterator : std::iterator<std::forward_iterator_tag, long long>
{
fib_iterator(std::size_t torun = 0) : to_run(torun) {}
value_type operator*() const {
return value();
}
fib_iterator& operator++()
{
--to_run;
switch(preamble)
{
case 2:
--preamble;
return *this;
case 1:
--preamble;
return *this;
}
auto next = value();
x = y;
y = next;
return *this;
}
value_type value() const
{
switch(preamble)
{
case 2:
return 0;
case 1:
return 1;
}
return x + y;
}
bool operator==(const fib_iterator& r) const {
return to_run == r.to_run;
}
bool operator!=(const fib_iterator& r) const {
return to_run != r.to_run;
}
long long x = 0;
long long y = 1;
std::size_t preamble = 2;
std::size_t to_run;
};
struct fibonacci_sequence
{
fibonacci_sequence(std::size_t length) : length_(length) {}
fib_iterator begin() const { return { length_ }; }
fib_iterator end() const { return { }; }
std::size_t length_;
};
int main()
{
for (auto i : fibonacci_sequence(50))
std::cout << i << ", ";
std::cout << '\n';
}
sample output:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987,
1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393,
196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887,
9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141,
267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073,
4807526976, 7778742049,
How about this:
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include <functional>
int main()
{
std::vector<int> v{1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
std::vector<int> s = std::accumulate(v.begin(), v.end(),std::vector<int>{},
[](const std::vector<int>& a, int b)
{
std::vector<int> d = a;
if(a.size()<2)
{
d.push_back(1);
}
else
{
auto start = d.rbegin();
auto first = *start;
start++;
auto second = *start;
d.push_back(first+second);
}
return d;
});
std::cout << "Fibo: " <<'\n';
for( auto c : s )
{
std::cout << c << "-";
}
std::cout << '\n';
}
But I also think it is a bit too much overhead, for something that simple.
EDIT: Remember to compile that with: g++ --std=c++14 fibo.cpp -o fibo.
EDIT: If you don't want to use the lambda function look here: How can I modify this Fibonacci code in C++ to use a function instead of lambda?
I'm running a test showing the benefits of sorting a 2d array, by columns, by pulling the data off into an individual array and sorting that array, then copying it back to the column. I'm wanting to run std::sort as a the sorting algorithm for every run. I'm trying to figure out how to run the loop in place first, before moving into the copying on and off the 2D array. An example of the input / output would be this.
#include <iostream>
#include <algorithm>
int main() {
int input[][5] = { { 13, 27, 4 , 1 , 11 },
{ 11, 19, 2 , 37, 1 },
{ 32, 64, 11, 22, 41 },
{ 71, 13, 27, -8, -2 },
{ 0 , -9, 11, 99, 13 } };
// std::sort something here.
int output[][5] = { { 0 , -9, 2 , -8, -2 },
{ 11, 13, 4 , 1 , 1 },
{ 13, 19, 11, 22, 11 },
{ 32, 27, 11, 37, 13 },
{ 71, 64, 27, 99, 41 } };
return 0;
}
Thanks for the help.
You may write your own iterator, something like:
#include <iterator>
template<typename Container>
class column_iterator : public std::iterator<std::random_access_iterator_tag,
typename std::decay<decltype(std::declval<Container>()[0][0])>::type>
{
typedef typename Container::iterator iterator;
typedef typename std::decay<decltype(std::declval<Container>()[0][0])>::type type;
public:
column_iterator(iterator it, int n) : it(it), n(n) {}
column_iterator& operator++() {++it; return *this;}
column_iterator& operator++(int) { auto res(*this); ++*this; return res;}
column_iterator& operator +=(std::ptrdiff_t offset) { it += offset; return *this;}
column_iterator operator +(std::ptrdiff_t offset) const { auto res(*this); res += offset; return res;}
column_iterator& operator--() {--it; return *this;}
column_iterator& operator--(int) { auto res(*this); --*this; return res;}
column_iterator& operator -=(std::ptrdiff_t offset) { it -= offset; return *this;}
column_iterator operator -(std::ptrdiff_t offset) const { auto res(*this); res -= offset; return res;}
type& operator*() { return (*it)[n];}
type* operator->() { return &(it)[n];}
bool operator == (const column_iterator& rhs) const { return it == rhs.it && n == rhs.n; }
bool operator != (const column_iterator& rhs) const { return !(*this == rhs); }
bool operator < (const column_iterator& rhs) const { return it < rhs.it; }
std::ptrdiff_t operator -(const column_iterator& rhs) const { return it - rhs.it; }
private:
iterator it;
int n;
};
template<typename Container>
column_iterator<Container> begin(Container& cont, int n)
{
return column_iterator<Container>(cont.begin(), n);
}
template<typename Container>
column_iterator<Container> end(Container& cont, int n)
{
return column_iterator<Container>(cont.end(), n);
}
Now, let's test it:
#include <algorithm>
#include <array>
#include <iostream>
#include <vector>
#include <cassert>
void display(const std::vector<std::array<int, 5>>& v)
{
for (auto rows : v) {
for (auto elem : rows) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
}
int main() {
std::vector<std::array<int, 5>> input {
{{ 13, 27, 4 , 1 , 11 }},
{{ 11, 19, 2 , 37, 1 }},
{{ 32, 64, 11, 22, 41 }},
{{ 71, 13, 27, -8, -2 }},
{{ 0 , -9, 11, 99, 13 }} };
for (int i = 0; i != 5; ++i) {
std::sort(begin(input, i), end(input, i));
}
display(input);
const std::vector<std::array<int, 5>> output {
{{ 0 , -9, 2 , -8, -2 }},
{{ 11, 13, 4 , 1 , 1 }},
{{ 13, 19, 11, 22, 11 }},
{{ 32, 27, 11, 37, 13 }},
{{ 71, 64, 27, 99, 41 }} };
assert(input == output);
return 0;
}
You can copy each column into a temp array,sort them and put them back into output array
for(j=0;j<5;++j)
{
for(i=0;i<5;++i)
{
temp[i]=input[i][j];
}
//sort temp[i]
//put it in output array
}
I finally gave up and decided to write my own version to compare with. I think I'm just going to keep all versions of the sorting algorithm similar to this.
#RichardPlunkett I tried creating my own compare function but was worried about it swapping entire rows.
#include <iostream>
#include <vector>
#include <random>
void sort (std::vector<std::vector<int> >& array, int start, int stop, int pos) {
if (stop - start < 2) return;
int mid = (start + stop) / 2;
int i = start, j = stop, pivot = array[mid][pos];
while (true) {
while (array[i][pos] < pivot) i++;
while (array[j][pos] > pivot) j--;
if (i > j) break;
std::swap(array[i++][pos], array[j--][pos]);
}
sort (array, start, j, pos);
sort (array, i, stop, pos);
}
int main() {
const int size = 10;
std::random_device rd;
std::default_random_engine generator(rd());
std::uniform_int_distribution<int> distribution(-10,10);
std::vector<std::vector<int> > test(size, std::vector<int>(size));
std::cout << "Unsorted array: \n";
for (int i=0;i<(int) test.size();++i) {
for (int j=0;j<(int) test[i].size();++j) {
test[i][j] = distribution(generator);
std::cout << test[i][j] << '\t';
}
std::cout << std::endl;
}
for (int i=0;i<size;++i)
sort(test, 0, size-1, i);
std::cout << "\nSorted array: \n";
for (int i=0;i<(int) test.size();++i) {
for (int j=0;j<(int) test[i].size();++j)
std::cout << test[i][j] << '\t';
std::cout << ' ' << std::endl;
}
return 0;
}
Imagine you have a simple matrix class
template <typename T = double>
class Matrix {
T* data;
size_t row, col;
public:
Matrix(size_t m, size_t n) : row(m), col(n), data(new T[m*n]) {}
//...
friend std::ostream& operator<<(std::ostream& os, const Matrix& m) {
for (int i=0; i<m.row; ++i) {
for (int j=0; j<m.col; ++j)
os<<" "<<m.data[i + j*m.row];
os<<endl;
}
return os;
}
};
Is there a way that I can initialize this matrix with an initializer list? I mean to obtain the sizes of the matrix and the elements from an initializer list. Something like the following code:
Matrix m = { {1., 3., 4.}, {2., 6, 2.}};
would print
1 3 4
2 6 2
Looking forward to your answers. Thank you all.
aa
EDIT
So I worked on your suggestions to craft a somewhat generic array that initializes elements using initializer lists. But this is the most generic I could obtain.
I would appreciate if any of you have any suggestions as to make it a more generic class.
Also, a couple of questions:
Is it fine that a derived class initializes the state of the base class? I'm not calling the base constructor because of this, but should I call it anyways?
I defined the destructor a the Generic_base class as protected, is this the right way to do it?
Is there any foreseeable way to carry out the code that belongs to the constructor of the initializer in a more generic way? I mean to have one general constructor that takes care of all cases?
I included just the necessary code to illustrate the use of initializer lists in construction. When going to higher dimensions it gets messy, but I did one just to check the code.
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
template <int d, typename T>
class Generic_base {
protected:
typedef T value_type;
Generic_base() : n_(), data_(nullptr){}
size_t n_[d] = {0};
value_type* data_;
};
template <int d, typename T>
class Generic_traits;
template <typename T>
class Generic_traits<1,T> : public Generic_base<1,T> {
protected:
typedef T value_type;
typedef Generic_base<1,T> base_type;
typedef std::initializer_list<T> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
data_ = new T[n_[0]];
int i = 0;
for (const auto& v : l)
data_[i++] = v;
}
};
template <typename T>
class Generic_traits<2,T> : public Generic_base<2,T> {
protected:
typedef T value_type;
typedef Generic_base<2,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
n_[1] = l.begin()->size();
data_ = new T[n_[0]*n_[1]];
int i = 0, j = 0;
for (const auto& r : l) {
assert(r.size() == n_[1]);
for (const auto& v : r) {
data_[i + j*n_[0]] = v;
++j;
}
j = 0;
++i;
}
}
};
template <typename T>
class Generic_traits<4,T> : public Generic_base<4,T> {
protected:
typedef T value_type;
typedef Generic_base<4,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> llist_type;
typedef std::initializer_list<llist_type> lllist_type;
typedef std::initializer_list<lllist_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
assert(l.begin()->size() > 0);
assert(l.begin()->begin()->size() > 0);
assert(l.begin()->begin()->begin()->size() > 0);
size_t m = n_[0] = l.size();
size_t n = n_[1] = l.begin()->size();
size_t o = n_[2] = l.begin()->begin()->size();
n_[3] = l.begin()->begin()->begin()->size();
data_ = new T[m*n*o*n_[3]];
int i=0, j=0, k=0, p=0;
for (const auto& u : l) {
assert(u.size() == n_[1]);
for (const auto& v : u) {
assert(v.size() == n_[2]);
for (const auto& x : v) {
assert(x.size() == n_[3]);
for (const auto& y : x) {
data_[i + m*j + m*n*k + m*n*o*p] = y;
++p;
}
p = 0;
++k;
}
k = 0;
++j;
}
j = 0;
++i;
}
}
};
template <int d, typename T>
class Generic : public Generic_traits<d,T> {
public:
typedef Generic_traits<d,T> traits_type;
typedef typename traits_type::base_type base_type;
using base_type::n_;
using base_type::data_;
typedef typename traits_type::initializer_type initializer_type;
// initializer list constructor
Generic(initializer_type l) : traits_type(l) {}
size_t size() const {
size_t n = 1;
for (size_t i=0; i<d; ++i)
n *= n_[i];
return n;
}
friend std::ostream& operator<<(std::ostream& os, const Generic& a) {
for (int i=0; i<a.size(); ++i)
os<<" "<<a.data_[i];
return os<<endl;
}
};
int main()
{
// constructors for initializer lists
Generic<1, double> y = { 1., 2., 3., 4.};
cout<<"y -> "<<y<<endl;
Generic<2, double> C = { {1., 2., 3.}, {4., 5., 6.} };
cout<<"C -> "<<C<<endl;
Generic<4, double> TT = { {{{1.}, {7.}, {13.}, {19}}, {{2}, {8}, {14}, {20}}, {{3}, {9}, {15}, {21}}}, {{{4.}, {10}, {16}, {22}}, {{5}, {11}, {17}, {23}}, {{6}, {12}, {18}, {24}}} };
cout<<"TT -> "<<TT<<endl;
return 0;
}
Which prints as expected:
y -> 1 2 3 4
C -> 1 4 2 5 3 6
TT -> 1 4 2 5 3 6 7 10 8 11 9 12 13 16 14 17 15 18 19 22 20 23 21 24
Why not?
Matrix(std::initializer_list<std::initializer_list<T>> lst) :
Matrix(lst.size(), lst.size() ? lst.begin()->size() : 0)
{
int i = 0, j = 0;
for (const auto& l : lst)
{
for (const auto& v : l)
{
data[i + j * row] = v;
++j;
}
j = 0;
++i;
}
}
And as stardust_ suggests - you should use vectors, not arrays here.
The main issue with using initializer lists to tackle this problem, is that their size is not easily accessible at compile time. It looks like this particular class is for dynamic matrices, but if you wanted to do this on the stack (usually for speed/locality reasons), here is a hint at what you need (C++17):
template<typename elem_t, std::size_t ... dim>
struct matrix
{
template<std::size_t ... n>
constexpr matrix(const elem_t (&...list)[n]) : data{}
{
auto pos = &data[0];
((pos = std::copy(list, list + n, pos)), ...);
}
elem_t data[(dim * ... * 1)];
};
template<typename ... elem_t, std::size_t ... n>
matrix(const elem_t (&...list)[n]) -> matrix<std::common_type_t<elem_t...>, sizeof...(n), (n * ... * 1) / sizeof...(n)>;
I had to tackle this same problem in my linear algebra library, so I understand how unintuitive this is at first. But if you instead pass a C-array into your constructor, you will have both type and size information of the values you've passed in. Also take note of the constuctor template argument deduction (CTAD) to abstract away the template arguments.
You can then create constexpr matrix objects like this (or, leave out constexpr to simply do this at runtime on the stack):
constexpr matrix mat{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
Which will initialize an object at compile time of type:
const matrix<int, 4, 3>
If C++20 is supported by your compiler, I would recommend adding a "requires" clause to the CTAD to ensure that all sub-arrays are the same size (mathematically-speaking, n1 == n2 == n3 == n4, etc).
Using std::vector::emplace_back() (longer)
Using std::vector, instead of plain old array, you can use std::vector::emplace_back() to fill the vector:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row(lst.size())
, col(lst.size() ? lst.begin()->size() : 0) // Minimal validation
{
// Eliminate reallocations as we already know the size of matrix
data.reserve(row * col);
for (auto const& r : lst) {
for (auto const &c : r) {
data.emplace_back(c);
}
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
Using std::vector::insert() (better and shorter)
As #Bob mentioned in a comment, you can use std::vector::insert() member function, instead of the inner emplace_back loop:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row{lst.size()}
, col{lst.size() ? lst.begin()->size() : 0} // Minimal validation
{
// Eliminate reallocations as we already know the size of the matrix
data.reserve(row * col);
for (auto const& r : lst) {
data.insert(data.end(), r.begin(), r.end());
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
So, we're saying: For each row (r) in the lst, insert the content of the row from the beginning (r.begin()) to the end (r.end()) into the end of the empty vector, data, (in an empty vector semantically we have: empty_vec.begin() == empty_vec.end()).
i might be a bit late but here is code for generally initializing tensors, regardless if they are matricies or vectors or whatever tensor.You could restrict it by throwing runtime errors when its not a matrix. Below is the source code to extract the data from the initilizer_list its a bit hacky. The whole trick is that the constructor are implicitly called with the correct type.
#include <initializer_list>
#include <iostream>
using namespace std;
class ShapeElem{
public:
ShapeElem* next;
int len;
ShapeElem(int _len,ShapeElem* _next): next(_next),len(_len){}
void print_shape(){
if (next != nullptr){
cout <<" "<< len;
next->print_shape();
}else{
cout << " " << len << "\n";
}
}
int array_len(){
if (next != nullptr){
return len*next->array_len();
}else{
return len;
}
}
};
template<class value_type>
class ArrayInit{
public:
void* data = nullptr;
size_t len;
bool is_final;
ArrayInit(std::initializer_list<value_type> init) : data((void*)init.begin()), len(init.size()),is_final(true){}
ArrayInit(std::initializer_list<ArrayInit<value_type>> init): data((void*)init.begin()), len(init.size()),is_final(false){}
ShapeElem* shape(){
if(is_final){
ShapeElem* out = new ShapeElem(len,nullptr);
}else{
ArrayInit<value_type>* first = (ArrayInit<value_type>*)data;
ShapeElem* out = new ShapeElem(len,first->shape());
}
}
void assign(value_type** pointer){
if(is_final){
for(size_t k = 0; k < len;k ++ ){
(*pointer)[k] = ( ((value_type*)data)[k]);
}
(*pointer) = (*pointer) + len;
}else{
ArrayInit<value_type>* data_array = (ArrayInit<value_type>*)data;
for(int k = 0;k < len;k++){
data_array[k].assign(pointer);
}
}
}
};
int main(){
auto x = ArrayInit<int>({{1,2,3},{92,1,3}});
auto shape = x.shape();
shape->print_shape();
int* data = new int[shape->array_len()];
int* running_pointer = data;
x.assign(&running_pointer);
for(int i = 0;i < shape->array_len();i++){
cout << " " << data[i];
}
cout << "\n";
}
outputs
2 3
1 2 3 92 1 3
The shape() function will return you the shape of the tensor at each dimension. The array is exactly saved as it is written down. It's really import to create something like shape since this will give you the ordering in which the elements are.
If you want a specific index out of the tensor lets say a[1][2][3]
the correct position is in 1*a.shape[1]a.shape[2] + 2a.shape[2] + 3
Some minor details and tricks can be found in: https://github.com/martinpflaum/multidimensional_array_cpp