Cartesian product of immutable ranges - d

Why can't we compute the cartesian product of two immutable ranges ?
The following code :
import std.stdio;
import std.algorithm;
void main() {
immutable int[] B = [ 1, 2, 3 ];
immutable int[] C = [ 4, 5, 6 ];
auto BC = cartesianProduct(B, C);
writeln(BC);
}
Throws :
/usr/include/dmd/phobos/std/range.d(4199): Error: cannot modify struct result._ranges_field_1 Repeat!(immutable(int)) with immutable members
/usr/include/dmd/phobos/std/range.d(4503): Error: template instance std.range.Zip!(immutable(int)[], Repeat!(immutable(int))) error instantiating
/usr/include/dmd/phobos/std/algorithm.d(11674): instantiated from here: zip!(immutable(int)[], Repeat!(immutable(int)))
laurent_test.d(8): instantiated from here: cartesianProduct!(immutable(int)[], immutable(int)[])
/usr/include/dmd/phobos/std/algorithm.d(11674): Error: template instance std.range.zip!(immutable(int)[], Repeat!(immutable(int))) error instantiating
laurent_test.d(8): instantiated from here: cartesianProduct!(immutable(int)[], immutable(int)[])
laurent_test.d(8): Error: template instance std.algorithm.cartesianProduct!(immutable(int)[], immutable(int)[]) error instantiating
Futhermore, if the second but the first immutable is removed it works.
According to the phobos implementation, one of the range as to be an inputRange and the other a forwardRange. Why such template constraints ?

I'm definitely not an expert in D, but I asked a similar question last year and this answer from Jonathan M Davis was excellent.
TL;DR: A range can't be immutable because it would't respect the 4 rules:
R r = void; // can define a range object
if (r.empty) {} // can test for empty
r.popFront(); // can invoke popFront()
auto h = r.front; // can get the front of the range
Can you guess the culprint? popFront

Related

How do I declare an array of red black trees?

When I want to initialize a red black tree I do as in the documentation.
auto rbt = redBlackTree(1,2,3,4)
but if I want to declare it globally or make an array of red black trees I don't know how to do it and the documentation is not helping. I've tried various things and I frequently get errors similar to: redBlackTree!int is used as a type Can you help me? I could do it if I knew what to put instead of auto, ie, if I knew the type of redBlackTree.
I want to declare a red black tree in global scope or declare an array for which I need to declare the type, I want to do something like this:
type rbt;
void main() {
rbt.insert(3);
}
or this:
void main{
type[2] rbt;
rbt[0].insert(1);
}
You don't need to know the type of redBlackTree. You can query for at compile-time with typeof:
alias RBTree = typeof(redBlackTree(1));
RBTree rbt = redBlackTree(1, 2, 3);
This is a common and an encouraged pattern as many functions in D return Voldemort types (types that cannot be named).
In your example the type is RedBlackTree!int. If you don't use an IDE, an easy way to discover the type is pragma(msg, typeof(<functionCall>(<args>)));.
Furthermore, I should note that declaring an array of RedBlackTree works with auto:
auto arr = [redBlackTree(1, 2), redBlackTree(3, 4)];
For more help, please feel free to post the exact code that failed.
The type (using long instead of int) is RedBlackTree!long, here are some examples. Remember you have to use new to initialize the class.
import std.stdio;
import std.container;
RedBlackTree!long rbtree;
RedBlackTree!long[2] rbarray;
RedBlackTree!long[] rbdynamicarr;
RedBlackTree!long[][] rbmat;
void main() {
rbtree.writeln;
rbtree = new RedBlackTree!long;
rbtree.insert(3);
rbtree.writeln;
rbarray.writeln;
rbarray = new RedBlackTree!long[2];
rbarray.writeln;
rbdynamicarr.writeln;
int n = 3;
rbdynamicarr = new RedBlackTree!long[n];
rbdynamicarr.writeln;
rbmat.writeln;
int m = 2;
rbmat = new RedBlackTree!long[][](n,m);
rbmat.writeln;
alias RBTree = typeof(redBlackTree!long(1L));
RBTree rbalias;
rbalias = new RBTree;
rbalias.writeln;
RBTree[3] crayola;
crayola.writeln;
typeid(redBlackTree(1)).writeln;
RedBlackTree!(long, "a < b", false) hola;
hola = new RedBlackTree!(long, "a < b", false);
hola.writeln;
}

std::make_shared not working, but creating the pointer using "new" works fine

I am currently making a GUI system for my game engine. I tried to create a shared pointer for one of the components "GUImovingbar" using std::make_shared() but got the following error when compiling
'std::shared_ptr<_Other> std::make_shared(_Types &&...)': could not deduce template argument for '_Types &&...' from 'initializer list'
However, when I used the exact same inputs to create a new pointer, it compiled fine with no errors. This struck me as a bit odd. What am I missing here?
Code using std::make_shared():
this->playerhullbar = std::make_shared<GUImovingbar>(
"right",
{ 50,hully }, //scoords
globalguitextures[findStringSrdPointerPairVectElement(globalguitextures, "barbackground")].second,
{ 0,static_cast<double>(maxplayerhullint),static_cast<double>(maxplayerhullint) },
{ 50,hully,250, hully,2,100 },//for int vector input ("bsbebdbc"): 1 barxstart, 2 y , 3 barendx, 4 y, 5 distance between bars in px, 6 bar count
{ 0,255,0 },
bartextvect
);
Above causes error:
'std::shared_ptr<_Other> std::make_shared(_Types &&...)': could not deduce template argument for '_Types &&...' from 'initializer list'
The following causes no errors at all:
std::shared_ptr<GUImovingbar> newptr(new GUImovingbar(
"right",
{ 50,hully}, //scoords
globalguitextures[findStringSrdPointerPairVectElement(globalguitextures, "barbackground")].second,
{ 0,static_cast<double>(maxplayerhullint),static_cast<double>(maxplayerhullint) },
{ 50,hully,250, hully,2,100 },//for int vector input ("bsbebdbc"): 1 barxstart, 2 y , 3 barendx, 4 y, 5 distance between bars in px, 6 bar count
{ 0,255,0 },
bartextvect)
);
this->playerhullbar = newptr;
As a template function, std::make_shared tries to find the appropriate constructor for your class given the parameters it has. Since you've given it initializer lists (the stuff in brackets), it is confused about what type those lists are supposed to initialize, and it thus can't find the appropriate constructor. However, when you use the constructor proper, ambiguity is removed, since thanks to the parameters' position the compiler knows what type the lists are supposed to initialize, and it converts them accordingly.
If you still want to use std::make_shared, you'll have to disambiguate the types of the initializer lists by putting them before the list :
this->playerhullbar = std::make_shared<GUImovingbar>(
"right",
Scoords { 50,hully },
globalguitextures[findStringSrdPointerPairVectElement(globalguitextures, "barbackground")].second,
Rect { 0,static_cast<double>(maxplayerhullint),static_cast<double>(maxplayerhullint) },
std:vector<int> { 50,hully,250, hully,2,100 },
Color { 0,255,0 },
bartextvect
);
(or, if you have an old compiler, use the former syntax with parentheses as well : std:vector<int>({ 50,hully,250, hully,2,100 }))
The problems are the aggregate initializations that you're doing in the make_shared call. If you create an object with new GUImovingbar(...) you are directly calling the constructor and thus, the compiler knows exactly which argument is of which type. This enables you to aggregate initialize said arguments.
However, if you call make_shared all arguments must be deduced from the value that you pass to the function (because it's a template). This would basically be like this:
auto x = {10, "hello", 4};
How should the compiler know what type this actually is?
If you still want to use make_shared you have to explicitly initialize the types with their names.

Compile-time fold results in error when called with a lambda

I'm trying to fold an array at compile time, and store the result in an enum. It works just fine when the enum (and call to fold) is at module-level, but compilation fails when its both contained within a struct and called using a lambda.
Here's a simple example of some failing code:
import std.algorithm.iteration;
import std.stdio;
struct Foo
{
// Version 1 (works)
//enum x = [ 1, 2, 3 ].fold!"a * b";
// Version 2 (works)
//enum x = [ 1, 2, 3 ].fold!mult;
// Version 3 (broken)
enum x = [ 1, 2, 3 ].fold!((a, b) => a * b);
pragma(msg, "x = ", x);
}
// Outside of the struct, it works
enum y = [ 1, 2, 3 ].fold!((a, b) => a * b);
pragma(msg, "y = ", y);
int mult(int a, int b)
{
return a * b;
}
void main(){}
(Versions 1 and 2, which are commented out, compile just fine. It's just Version 3 that has problems.)
Upon compiling, the following error is thrown:
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3690): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3690): while evaluating: `static assert(((int)).length == fun.length)`
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3697): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3718): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3636): Error: template instance `broken.Foo.reduce!((a, b) => a * b).reduceImpl!(false, int[], int)` error instantiating
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(4086): instantiated from here: `reduce!(int[])`
.\broken.d(13): instantiated from here: `fold!(int[])`
x = .\broken.d(13): Error: CTFE failed because of previous errors in `fold`
.\broken.d(16): while evaluating `pragma(msg, x)`
y = 6
Failed: ["C:\\D\\dmd2\\windows\\bin\\dmd.exe", "-v", "-o-", ".\\broken.d", "-I."]
I've tried looking at the source code mentioned in the error, but the concepts it uses are beyond my current level of knowledge of D.
I initially assumed that lambdas might not work properly at compile-time, but enum y evaluates correctly, so I'm guessing it's not that...
I'm using DMD v2.086.1, but had the same problem using LDC 1.16.0 and 1.14.0.
This is the compiler not being very good at figuring out if a lambda needs access to its context. If you'd written something like this:
struct S {
int n;
int fun() {
import std.algorithm.iteration : fold;
return [1,2,3].fold!((a,b) => a*b*n);
}
}
It should be clear that the lambda above needs access to n in the struct. In the same way, the compiler errs on the side of caution for enum x = [ 1, 2, 3 ].fold!((a, b) => a * b);, and assumes there's some state inside Foo that will affect the result of the calculation. Filed this as issue 20077.
You have already found some workarounds, and there's another worth mentioning - adding argument types to the lambda:
enum x = [ 1, 2, 3 ].fold!((int a, int b) => a * b);
This way, the compiler figures out what info the lambda needs at an earlier point, and is able to figure out that it doesn't need access to the surrounding scope.

perl xs - can't return a new custom c++ object from method call - returns scalar value instead

In my XS file I have:
As my new method:
matrix *
matrix::new( size_t ncols, size_t nrows )
which returns a matrix object like it should and I can invoke methods.
Then I have a method call which creates a new matrix object and is supposed to return it as a new matrix:
matrix *
matrix::getInnerMatrix( )
PREINIT:
char * CLASS = (char *)SvPV_nolen(ST(0));
CODE:
RETVAL = static_cast<matrix*>(THIS->matrix::getInnerMatrix());
OUTPUT:
RETVAL
However the returned type is matrix=SCALAR(0x122f81c) and therefore I am unable to invoke any method calls from this object as the perl interpreter seems to be viewing the returned type as a scalar value type instead of a 'matrix' object. Here is a test script:
$m1 = matrix::new(matrix,4,4);
#arr = ( 1 .. 16 );
$aref = [#arr];
$m1->assign_aref($aref);
my $m2 = $m1->getInnerMatrix();
print ref $m1; # returns "matrix" (like it should)
print "\n\n";
print ref $m2; # returns "matrix=SCALAR(0x122f81c)" (wrong)
Here is my typemap:
TYPEMAP
matrix * O_MATRIX
OUTPUT
O_MATRIX
sv_setref_pv( $arg, CLASS, (void*)$var );
INPUT
O_MATRIX
if ( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) {
$var = ($type)SvIV((SV*)SvRV( $arg ));
}
else {
warn( \"${Package}::$func_name() -- ${var} not a blessed SV reference\" );
XSRETURN_UNDEF;
}
What changes must I make in my XS file, or any other file to ensure that a pure matrix object is returned?
When using XS with C++, the XS preprocessor inserts THIS for instance methods and CLASS for static methods. A method called new is treated as a static method. This allows the resulting xsubs to be used as instance methods/class methods by default: matrix->new and $m->getInnerMatrix().
Your typemap uses the CLASS variable which is not provided for instance methods. In your case, I would hard-code the package name in the type map instead:
OUTPUT
O_MATRIX
sv_setref_pv( $arg, "matrix", (void*)$var );
The typemap is also used when an argument of that type is not used as the invocant. E.g. consider this xsub:
matrix*
some_other_xsub(x)
int x
Here there would not by a CLASS variable for the matrix* return value either.
Note that lowercase package names should only be used for pragma packages (like strict or warnings). Please use CamelCase for your classes.
Your attempt to provide your own value for CLASS failed because SvPV_nolen() stringifies the reference and does not get the reference type. I.e. it's equivalent to "$m", not to ref $m. A more correct alternative would have been to use sv_ref():
char* CLASS = SvPV_nolen(sv_ref(NULL, THIS, true));
The third parameter to sv_ref() makes this function work like the Perl function ref, i.e. return the class name if the scalar is blessed, not just the underlying reference type.

Sending a templated function as an argument to a templated function in D

I'm trying to send D's sort function as a template argument to the pipe function. When I use sort without template arguments it works:
import std.stdio,std.algorithm,std.functional;
void main()
{
auto arr=pipe!(sort)([1,3,2]);
writeln(arr);
}
However, when I try to use sort with a template argument:
import std.stdio,std.algorithm,std.functional;
void main()
{
auto arr=pipe!(sort!"b<a")([1,3,2]);
writeln(arr);
}
I get an error - main.d(5): Error: template instance sort!("b<a") sort!("b<a") does not match template declaration sort(alias less = "a < b",SwapStrategy ss = SwapStrategy.unstable,Range)
Why does it happen? sort!"b<a" works on it's own, and it has the same arguments and return types as sort, so why does pipe accept sort but not sort!"b<a"? And is there a correct syntax for what I try to do?
UPDATE
OK, I've tried to wrap the sort function. The following code works:
import std.stdio,std.algorithm,std.functional,std.array;
template mysort(string comparer)
{
auto mysort(T)(T source)
{
sort!comparer(source);
return source;
}
}
void main()
{
auto arr=pipe!(mysort!"b<a")([1,3,2]);
writeln(arr);
}
So why doesn't the original version work? is this because of the extra template parameters sort takes?
Yes it's because of the extra template parameters — specifically the Range parameter. The problem can be reduced to
size_t sort2(alias f, Range)(Range range)
{
return 0;
}
alias sort2!"b<a" u;
The instantiation sort!"b<a" will fail because the Range is not determined. The function call sort2!"b<a"([1,2,3]) works because the parameter [1,2,3] can tell the compiler the type Range is int[]. This is known as "implicit function template instantiation (IFTI)". But IFTI only works when it is used as a function. In your use case, sort!"b<a" is instantiated without providing all parameters, thus the error.
This can be fixed by making the input a function literal, which is just similar to your mysort solution:
auto arr = pipe!(x => sort!"b<a"(x))([1,3,2]);
Or you could provide all required template parameters. This makes the code very unreadable though.
auto arr = pipe!(sort!("b<a", SwapStrategy.unstable, int[]))([1,3,2]);