better way to shrink copy/paste codes - c++

Currently I have this trunk of code: a vector of structs v[myStruct]. The size of it is 3. I named these structs as myStruct_A, myStruct_B, and myStruct_C. Each piece of code is almost same, but with its name as suffix.
SomeMap someMap_A;
for (auto& pair : myStruct_A.hashMap) {
SomeStruct someStruct = foo(pair);
someMap_A[someStruct.key] = someStruct.value;
anotherStruct_A[someStruct.nameX] = someStruct.bundle;
}
someVector.push_back[someMap_A];
All of the three A,B,C need do the above things. The question here is that if using a function, I don't know how to separate and specify the names. I need get several vectors of data which are based on all of those three myStructs' outputs.

You can do this with a macro. For example, here's a simplified version:
#define process(i, suffix) some_vect_ ## suffix.push_back(i); \
some_set_ ## suffix.insert(i);
int main() {
std::vector<int> some_vect_A, some_vect_B, some_vect_C;
std::set<int> some_set_A, some_set_B, some_set_C;
for (int i = 0; i < 100; i++) {
int val = rand();
process(val, A); // adds `val` to `some_vect_A` and `some_set_A`
process(val, B); // adds `val` to `some_vect_B` and `some_set_B`
process(val, C); // adds `val` to `some_vect_C` and `some_set_C`
}
}
This particular demo code is pretty pointless (it produces identical results in all three sets and all three vectors), but does show how to do the manipulation you need.

Related

Manipulating array's values in a certain way

So I was asked to write a function that changes array's values in a way that:
All of the values that are the smallest aren't changed
if, let's assume, the smallest number is 2 and there is no 3's and 4's then all 5's are changed for 3's etc.
for example, for an array = [2, 5, 7, 5] we would get [2, 3, 4, 3], which generalizes to getting a minimal value of an array which remains unchanged, and every other minimum (not including the first one) is changed depending on which minimum it is. On our example - 5 is the first minimum (besides 2), so it is 2 (first minimum) + 1 = 3, 7 is 2nd smallest after 2, so it is 2+2(as it is 2nd smallest).
I've come up with something like this:
int fillGaps(int arr[], size_t sz){
int min = *min_element(arr, arr+sz);
int w = 1;
for (int i = 0; i<sz; i++){
if (arr[i] == min) {continue;}
else{
int mini = *min_element(arr+i, arr+sz);
for (int j = 0; j<sz; j++){
if (arr[j] == mini){arr[j] = min+w;}
}
w++;}
}
return arr[sz-1];
}
However it works fine only for the 0th and 1st value, it doesnt affect any further items. Could anyone please help me with that?
I don't quite follow the logic of your function, so can't quite comment on that.
Here's how I interpret what needs to be done. Note that my example implementation is written to be as understandable as possible. There might be ways to make it faster.
Note that I'm also using an std::vector, to make things more readable and C++-like. You really shouldn't be passing raw pointers and sizes, that's super error prone. At the very least bundle them in a struct.
#include <algorithm>
#include <set>
#include <unordered_map>
#include <vector>
int fillGaps (std::vector<int> & data) {
// Make sure we don't have to worry about edge cases in the code below.
if (data.empty()) { return 0; }
/* The minimum number of times we need to loop over the data is two.
* First to check which values are in there, which lets us decide
* what each original value should be replaced with. Second to do the
* actual replacing.
*
* So let's trade some memory for speed and start by creating a lookup table.
* Each entry will map an existing value to its new value. Let's use the
* "define lambda and immediately invoke it" to make the scope of variables
* used to calculate all this as small as possible.
*/
auto const valueMapping = [&data] {
// Use an std::set so we get all unique values in sorted order.
std::set<int> values;
for (int e : data) { values.insert(e); }
std::unordered_map<int, int> result;
result.reserve(values.size());
// Map minimum value to itself, and increase replacement value by one for
// each subsequent value present in the data vector.
int replacement = *values.begin();
for (auto e : values) { result.emplace(e, replacement++); }
return result;
}();
// Now the actual algorithm is trivial: loop over the data and replace each
// element with its replacement value.
for (auto & e : data) { e = valueMapping.at(e); }
return data.back();
}

Iterating over ndarray columns in C/C++

How would one get a view of a PyArrayObject* similar to the following python code?
# n-column array x
# d is the length of each column
print(x.shape) # => (d, n)
by_column = [x[::,i] for i in range(x.shape[1])]
assert len(by_column) == n
print(by_column[n-1].shape) # => (d,)
So far my code is this:
// my_array is a PyArrayObject*
std::vector<PyArrayObject*> columns = {};
npy_intp* dims = my_array->dimensions;
npy_intp* strides = my_array->strides;
std::vector<int> shape = {};
for (int i = 0; &dims[i] != strides; i++){
shape.push_back(dims[i]);
}
switch (shape.size()) {
case 1: {
// handle 1D array by simply iterating
}
case 2: {
int columns = shape[1];
// What now?
}
}
I'm having trouble finding any reference to do this in C/C++ in both the documentation and the source code, could you give an example of how one would do this?
The C/C++ API for numpy seems really convoluted when compared to something like std::vector, and the documentation isn't very beginner-friendly either, so any references to easier guides would be appreciated too.
You should access the internal structure of PyArrayObject via the PyArray_XXX functions like PyArray_NDIM. To get the contents of a sequence, you use PyObject_GetItem with a tuple key, where in your use case the tuple will have a PySliceObject as the first element.

How can I generate a vector of vectors in a nice way?

I would like to have a function that has this definition:
vector<vector<int>> create_lists(int no_lists, int no_per_list)
The first argument is the number of sublist and the second argument is the number of items per sublist. Each list shall contain a subrange of a sequence of ints, so a call
create_lists(10,10);
will create 10 sublists, the first ranging from 0 to 9, second from 10 to 19 and so on.
I have done several versions of it, but they all feels clumsy. Is there a nice and elegant way to do this?
I rarely see an opportunity to say "use std::iota", so I'm not going to miss this one!
std::vector<std::vector<int>> create_lists(int no_lists, int no_per_list)
{
std::vector<std::vector<int>> v(no_lists,
std::vector<int>(no_per_list));
for (int i = 0; i < no_lists; ++i) {
std::iota(v[i].begin(), v[i].end(), no_per_list * i);
}
return v;
}
I would create the 2d vector with its default values as
std::vector<std::vector<int>> temp(no_lists, std::vector<int>(no_per_list));
Then I would iterate through each vector and transform them into the sequence values using
for (auto & e : temp)
std::transform(e.begin(), e.end(), e.begin(), [](int foo){static int counter = 0; return counter++;});
Putting it all together the function would be:
std::vector<std::vector<int>> create_lists(int no_lists, int no_per_list)
{
int counter = 0;
std::vector<std::vector<int>> temp(no_lists, std::vector<int>(no_per_list));
for (auto & e : temp)
std::generate(e.begin(), e.end(), [&counter](){return counter++;});
return temp;
}
And you can see it running in this Live Example
This may be the least exciting answer of them all, but I would probably simply use two for loops. This way seems least obscure and quickest to understand to me, which means the code is easier to understand later
std::vector<std::vector<int>> create_lists(int no_lists, int no_per_list)
{
std::vector<std::vector<int>> lol(no_lists, std::vector<int>(no_per_list));
int count = 0;
for(auto &list : lol)
for(auto &v : list)
{
v = count;
++count;
}
return lol;
}
This does one allocation and deallocation more than strictly necessary (the temporary vector<int> passed to the vector<vector<int>> constructor), but trying to avoid that would make the code slightly more complex and would probably not make much of a difference (your doing no_lists+1 allocations anyway).

Iteration to create variables

I was just curious to know if there is way to create a new variable with a new name every time a loop is executed.
For example:
#include <iostream>
int main()
{
for (int x = 1 ; x<=5 ; x++)
int a_x;
return 0;
}
5 new variables should be created with names a_1, a_2, ..., a_5
The above code just shows what I am looking for and is not the answer.
Is this possible without using arrays?
No, there is no way to do what you've outlined (directly). Here are several possible alternatives:
First off, if you do not need the "variables" to be accessible outside the loop, just use a normal local variable:
for (int x = 1; x <= 5; ++x) {
int a = whatever; // This will be freshly redeclared & reinitialised in each iteration
}
If the bounds of the iteration are known at compile time, you can use an array:
std::array<int, 5> a;
for (int x = 0; x < a.size(); ++x) {
a[x];
}
If the bounds are only known at runtime, use a dynamic array:
std::vector<int> a(the_runtime_size);
for (int x = 0; x < a.size(); ++x) {
a[x];
}
If you really need individual variables for some reason (and you know the number at compile time), you could resort to preprocessor tricks with Boost.Preprocessor. But that is far above beginner level:
#include <boost/preprocessor>
#define DECLARE_MY_VARIABLE(z, idx, name) \
int BOOST_PP_CAT(name, BOOST_PP_CAT(_, idx));
BOOST_PP_REPEAT(5, DECLARE_MY_VARIABLE, a)
The code above will expand to:
int a_0; int a_1; int a_2; int a_3; int a_4;
You could of course take this several steps further, to have each of the variables of a different type, or name them by names instead of by indices. It will just require more macro magic.
Disclaimer: Do NOT use this approach unless you very clearly know you need it. Even then, reconsider twice before you actually do that. And if you still do, document it heavily. Stuff like this should generally be hidden deep inside a library under a nice & clean user-friendly interface.
No you can't do that in C++.
The best thing to do in this case would be to create an array of ints and use the for loop to populate them.
int a_x[5];
for (int x = 1 ; x<=5 ; x++)
a_x[x - 1] = /*ToDo - something*/
Note that
arrays are zero-based: can you see how I've used x - 1. The normal thing to do would be to rebase x in the for loop though: for (int x = 0 ; x < 5; ...
arrays are not initialised. You must populate the contents.
While many will assume that this is impossible, it can be achieved with the preprocessor. It is necessary that the loop count is known at compile time. Here I use the Boost Preprocessor Library. The example for PP_REPEAT does almost exactly what you want.
#include <boost/preprocessor/repetition/repeat.hpp>
#define DECL(z, n, text) text ## n = n;
int main()
{
BOOST_PP_REPEAT(5, DECL, int a_) // expands to int a_0 = 0; int a_1 = 1; ...
return 0;
}
Please remember: this is certainly not what you want. You probably want to use an array. Do only use this if you are absolutely certain that you need it.

C++ Generate random numbers for dominoes

My assignment involves writing several classes that will work together to randomly sort 28 dominoes for the user and display them. The main trouble I'm having so far is just creating the dominoes without any duplication. If you're familiar with dominoes, you know that each half of them are either blank or have 1-6 dots. Basically I'll have a dynamic array of 28 unique structs (dominoes) but I'm just stuck on generating these dominoes without having identical ones. I was thinking of using FOR loops to just go through and assign values within each struct but I figured there had to be some easier way.
This is what I have so far below; I know it's not much but I can't and don't want to go on with writing methods for sorting and display without getting this right first.
class CDominoes{
public:
struct Data
{
int top;
int bottom;
Data()
{
top = 0;
bottom = 0;
}
} domino[28];
//methods to assign spots to halves
};
The simplest solution is to generate, and then shuffle. To generate, you need to avoid wasting time generating duplicates. For example, (4,5) is the same as (5,4), so you don't want to generate both. That means that your inner loop should always begin at the current value of the outer loop. In so doing, you'll never repeat a combination. Here's an example:
int main () {
for( int t = 0; t <= 6; ++t ) {
for( int b = t; b <= 6; ++b ) {
std::cout << "(" << t << "," << b << ")\n";
}
}
return 0;
}
In this example, we're considering '0' to be the same as a blank domino.
Next, instead of printing these, put them into a random access container such as std::array or std::vector, and then use std::shuffle to shuffle your container.