digitalmars.D.learn - Recursive Algebraic
I'm trying to create a vector of vectors(more general than vectors or matrices). The idea is that a Vector can contain another Vector or another type. Vector can be specified to be fixed in length or dynamic for efficiency. Vector!(T, N) creates a vector of leaf type T and length N. If N = size_t.max then the vector internally uses a dynamic array. Vector is essentially a tree. Norm, dot, and many operators on Vector are computed recursively. The norm of a vector of vectors is the norm of the vector of the norm of vectors. This definition is valid when Vector is a simple Vector but also works for vectors of vectors. Other operators can be similarly defined. I believe the problem is that Algebraic only accepts This of the fixed N. So as of now one can only have vectors of vectors with size 1 or fixed N. I also get an error overload for type 'VariantN!(4u, int, This*)*' hasn't been specified". It would be nice if the compiler would construct the missing handler and give the signature so I could know what I'm doing wrong, I'm using (This* i) => ; but it does not recognize that. import std.typecons, std.range, std.array, std.algorithm, std.variant, std.stdio, std.traits; struct Vector(T, size_t N = size_t.max) // N could be small value if it is stored inside the vector as it could be stored as part of the flags { Flags flags; // Contains direction flag(row or column), normalized, etc import std.range, std.typecons, std.meta, std.algorithm, std.conv, std.math; static if (N == size_t.max) // For size_t.max sets N to be infinite/dynamic; { mixin("Algebraic!(T, This*)[] data;"); property size_t Length() { return data.length; } } else { mixin("Algebraic!(T, This*)[N] data;"); static property size_t Length() { return N; } } alias data this; property double Norm(size_t n = 2) { return iota(0, n).map!((a) { return data[a].visit!( (T i) { return 1; }, (This* i) { return 2000; } ); }).sum(); } auto opDispatch(string s, Args...)(Args v) { enum index = to!int(s[1..$]); static if (N == size_t.max) while(index >= data.length) data ~= T.init; static if (Args.length == 0) return data[index]; else static if (Args.length == 1) { data[index] = &v[0]; } } } void main() { import std.math, std.variant; Vector!(int, 5) v; v.x1 = v; writeln(v.x1); v.x2 = 4; v.x3 = 5; writeln(v.x3); writeln(v.Norm); getchar(); }
Jun 30 2018
The problem is that it seems that when one has a parameterized type, you must completely specify the parameters when using Algebraic, Algebraic!(T, Vector!int, Vector!(double, 3), Vector!(double, 3), ...)[] data; to be able to encapsulate an Algebraic on Vector(as a collection of all fixed point evaluations). What I'd rather do is something akin to Algebraic!(T, Vector!(T, N))[] data; or, hell, even just Algebraic!(T, Vector)[] data; Since A!B bears no relationship to A!C except their name, this is not necessarily a good idea, but neither is having to explicitly express all kinds. I imagine there is some trick using inheritance, Maybe class X; class A(int N) : X; then Algebraic!(T, X)[] data; only works if Algebraic handles inheritance and checks with handlers for specificity. The problem with D's type system is that it does not allow one to express sets of types, which is a special type in and of itself, except in some specific cases such as using is(,...). Here is a snippet of code https://dpaste.dzfl.pl/8ff1cd3d7d46 That shows that an algebraic does not handle inheritance. if Y : X then we'd expect Algebraic!(T, Y) : Algebraic!(T, X) and for there to be no problem. For this not to be a problem two things need to be known: 1. To be able to get the type of X and Y 2. To be able to determine if Y : X. D provides the ability to determine if a type inherits from another at runtime using classinfo which every D class contains and it's inheritance relationships, solving problem 2. Problem 1 is to be able get the types of an object in a way that can be related to D's pre-existing methods for comparing type info. Variant stores the typeid so Problem 2 is solved! Therefor, we can safely say that Algebraic can easily deal with inheritance. I am not familiar enough with the D internals to provide a fix. Anyone familiar with Algebraic should be able to write a few lines of code to allow for inheritance checking(or even complex patterns using a lambda to determine if a type is in the Algebraic. This becomes a function on the typeid's and classinfo's which is determined by the user. Algebraic!((x) { if (is(x.type == typeid(y))) return x.get!X; return defaultX(x); }, int) Where the above algebraic allows any object that has the same typeid of some object y or some object that has the same type as defaultX(x)(which could be anything) or an int. One could even do strange things like Algebraic!((x) { if (rand1(0.5) return x; return 1; }, int) which, remembering that this lambda is run at runtime to determine if an object stored in the variant is "in the algebraic", will allow any object to pass 50% of the time(and potentially crash when some code gets an object it can't handle. For example, if the above algebraic was to work only integral types then it would be problematic when x was a non integral type(but maybe floating point t types would work). This is assuming some handler was called, which wouldn't be because there is no handler that exists handle the non integral types. Hence things like visit would have to allow, if the lambda was specified, a way to fall through. a.visit!((int i) => 4*i, (Object o) => return 2); Which, 50% of the time would return 2 and the other half of the time it would return 4. Allowing functions to specify Algebraic relationships gives far more power. D seems to already have everything available to do it. For example, the original problem of inheritance relationships can easily be expressed: Algebraic!((x) { if (doesDerive!Y(x)) return cast(Y)x; if (doesDerive!Z(x)) return new Q(x); return null; }, int) The algebraic handles an object derived from Y, Q, and int. a.visit!((int i) => 4*i, (Y) => 2, (Q q) => q.value);
Jun 30 2018