digitalmars.D.learn - Appending static arrays
- =?UTF-8?B?Tm9yZGzDtnc=?= (8/8) Jul 17 2017 I'm want to define a specialization of `append()` that takes only
- ag0aep6g (7/16) Jul 17 2017 Like so?
- =?UTF-8?B?Tm9yZGzDtnc=?= (12/18) Jul 17 2017 Thanks, but I'm talking about the variadic case where the number
- ag0aep6g (34/48) Jul 17 2017 I see. Here's what I could come up with:
- =?UTF-8?B?Tm9yZGzDtnc=?= (5/6) Jul 17 2017 Further, the expression `a ~ b` here will allocate and create a
- Stefan Koch (18/25) Jul 17 2017 we have special code in the compiler to optimize a ~ b ~ c.
- =?UTF-8?B?Tm9yZGzDtnc=?= (2/18) Jul 17 2017 Thanks, but I wan't a @nogc-variant, which was posted below.
- =?UTF-8?B?Tm9yZGzDtnc=?= (7/8) Jul 17 2017 Interesting, can you elaborate on what you mean with "optimize".
- =?UTF-8?B?Tm9yZGzDtnc=?= (7/15) Jul 17 2017 I just realized that I can use `std.meta.staticMap` to get the
- H. S. Teoh via Digitalmars-d-learn (24/33) Jul 17 2017 Hmm. What about:
- H. S. Teoh via Digitalmars-d-learn (8/19) Jul 17 2017 Argh, forgot this important line:
- H. S. Teoh via Digitalmars-d-learn (19/39) Jul 17 2017 [...]
- =?UTF-8?B?Tm9yZGzDtnc=?= (6/21) Jul 17 2017 Thanks, I'll try this!
- H. S. Teoh via Digitalmars-d-learn (8/13) Jul 17 2017 Never know till you try. :-D
- =?UTF-8?B?Tm9yZGzDtnc=?= (11/22) Jul 17 2017 Made some adjustments with working unittest and put it up on
- H. S. Teoh via Digitalmars-d-learn (8/19) Jul 17 2017 See the working implementation in my latest post on this thread. That
- =?UTF-8?B?Tm9yZGzDtnc=?= (2/6) Jul 17 2017 Thanks.
- =?UTF-8?B?Tm9yZGzDtnc=?= (3/5) Jul 17 2017 Moved here
- Jacob Carlborg (8/9) Jul 18 2017 append - add array or element to existing array
- =?UTF-8?B?Tm9yZGzDtnc=?= (2/7) Jul 18 2017 Thanks.
- H. S. Teoh via Digitalmars-d-learn (29/29) Jul 17 2017 OK, here's an actual, compilable, runnable version:
- =?UTF-8?B?Tm9yZGzDtnc=?= (5/21) Jul 17 2017 This slice assignment doesn't support conversion between
- H. S. Teoh via Digitalmars-d-learn (11/19) Jul 17 2017 True. Maybe we should just leave out the `CommonType` thing for now.
- Jack Applegame (22/30) Jul 18 2017 My recursive version:
- Stefan Koch (2/34) Jul 18 2017 whhhahhh template bloat!!!!!!!!!!!!!!!!!!!!
- Jack Applegame (2/3) Jul 18 2017 Yes, and also very slow. :)
I'm want to define a specialization of `append()` that takes only static arrays as inputs and returns a static array being the sum of the lengths of the inputs. Have anybody already implemented this? If not, I'm specifically interested in how to most conveniently infer the length (as an enum) of the returned static array from the `.length`s of inputs (which of course must be enum-values too).
Jul 17 2017
On 07/17/2017 07:38 PM, Nordlöw wrote:I'm want to define a specialization of `append()` that takes only static arrays as inputs and returns a static array being the sum of the lengths of the inputs. Have anybody already implemented this? If not, I'm specifically interested in how to most conveniently infer the length (as an enum) of the returned static array from the `.length`s of inputs (which of course must be enum-values too).Like so? int[n + m] append(size_t n, size_t m)(int[n] a, int[m] b) { int[n + m] result = a ~ b; return result; }
Jul 17 2017
On Monday, 17 July 2017 at 17:46:42 UTC, ag0aep6g wrote:Like so? int[n + m] append(size_t n, size_t m)(int[n] a, int[m] b) { int[n + m] result = a ~ b; return result; }Thanks, but I'm talking about the variadic case where the number of input arguments are unknown (>= 2) where the function header looks something like import std.traits : allSatisfy, isStaticArray; auto append(R, Args...)(auto ref Args args) if (args.length >= 2 && allSatisfy!(isStaticArray, Args)) // TODO all ElementTypes have CommonType { // ... }
Jul 17 2017
On 07/17/2017 08:35 PM, Nordlöw wrote:Thanks, but I'm talking about the variadic case where the number of input arguments are unknown (>= 2) where the function header looks something like import std.traits : allSatisfy, isStaticArray; auto append(R, Args...)(auto ref Args args) if (args.length >= 2 && allSatisfy!(isStaticArray, Args)) // TODO all ElementTypes have CommonType { // ... }I see. Here's what I could come up with: ---- import std.traits : allSatisfy, isStaticArray; auto append(Args...)(auto ref Args args) if (args.length >= 2 && allSatisfy!(isStaticArray, Args)) { import std.algorithm : sum; import std.meta : staticMap; import std.range: ElementType; import std.traits : CommonType; enum staticArrayLength(A : E[n], E, size_t n) = n; alias E = ElementType!(Args[0]); static assert(is(CommonType!(staticMap!(ElementType, Args)) : E)); enum lengths = staticMap!(staticArrayLength, Args); E[sum([lengths])] result; foreach (i, length; lengths) { enum offset = sum!(size_t[])([lengths[0 .. i]]); result[offset .. offset + length] = args[i]; } return result; } nogc unittest { int[3] a = [1, 2, 3]; const int[4] b = [4, 5, 6, 7]; immutable int[5] c = [8, 9, 10, 11, 12]; auto r = append(a, b, c); assert(r == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); static assert(is(typeof(r) == int[12])); } ----
Jul 17 2017
On Monday, 17 July 2017 at 17:46:42 UTC, ag0aep6g wrote:int[n + m] result = a ~ b;Further, the expression `a ~ b` here will allocate and create a copy on the GC-heap before writing `result`. Maybe LDC optimizes away this now or in the future but DMD cannot. Yeah I know, kind of dumb but that's how it is currently.
Jul 17 2017
On Monday, 17 July 2017 at 18:38:16 UTC, Nordlöw wrote:On Monday, 17 July 2017 at 17:46:42 UTC, ag0aep6g wrote:we have special code in the compiler to optimize a ~ b ~ c. So all you need to do make it so auto cat(T[]...)(T args) { T[] result; mixin(() { string mix = `result = `; foreach(i;args.length) { mix ~= `args[` ~ itos(i) ~ `] ~`; } mix[$-1] = ';'; return mix; }()); return result; } that should do it :)int[n + m] result = a ~ b;Further, the expression `a ~ b` here will allocate and create a copy on the GC-heap before writing `result`. Maybe LDC optimizes away this now or in the future but DMD cannot. Yeah I know, kind of dumb but that's how it is currently.
Jul 17 2017
On Monday, 17 July 2017 at 18:54:31 UTC, Stefan Koch wrote:So all you need to do make it so auto cat(T[]...)(T args) { T[] result; mixin(() { string mix = `result = `; foreach(i;args.length) { mix ~= `args[` ~ itos(i) ~ `] ~`; } mix[$-1] = ';'; return mix; }()); return result; } that should do it :)Thanks, but I wan't a nogc-variant, which was posted below.
Jul 17 2017
On Monday, 17 July 2017 at 18:54:31 UTC, Stefan Koch wrote:we have special code in the compiler to optimize a ~ b ~ c.Interesting, can you elaborate on what you mean with "optimize". In that case, is there a reason why int x[2]; int y[2]; int z[x.length + y.length] = x ~ y; isn't nogc?
Jul 17 2017
On Monday, 17 July 2017 at 17:38:23 UTC, Nordlöw wrote:I'm want to define a specialization of `append()` that takes only static arrays as inputs and returns a static array being the sum of the lengths of the inputs. Have anybody already implemented this? If not, I'm specifically interested in how to most conveniently infer the length (as an enum) of the returned static array from the `.length`s of inputs (which of course must be enum-values too).I just realized that I can use `std.meta.staticMap` to get the lengths but I still need to reduce them and there is no variant of `reduce` in `std.meta`. Why? Hasn't it been written yet? I know I can always write yet another recursive CT-function, say `SumOfLengths`, but I thought I'd try to reuse `std.meta` this time. :)
Jul 17 2017
On Mon, Jul 17, 2017 at 05:38:23PM +0000, Nordlöw via Digitalmars-d-learn wrote:I'm want to define a specialization of `append()` that takes only static arrays as inputs and returns a static array being the sum of the lengths of the inputs. Have anybody already implemented this? If not, I'm specifically interested in how to most conveniently infer the length (as an enum) of the returned static array from the `.length`s of inputs (which of course must be enum-values too).Hmm. What about: template sumOfLengths(A...) if (A.length > 0) { static if (A.length == 1) enum sumOfLengths = A[0].length; else enum sumOfLengths = A[0].length + sumOfLengths!(A[1 .. $]); } T[sumOfLengths!StaticArrays] append(StaticArrays...)(StaticArrays arrays) if (/* insert static array constraints here */) { typeof(return) result = void; size_t offset = 0; foreach (a; arrays) { result[offset .. offset + a.length] = a[]; } return result; } T -- IBM = I'll Buy Microsoft!
Jul 17 2017
On Mon, Jul 17, 2017 at 12:01:48PM -0700, H. S. Teoh via Digitalmars-d-learn wrote: [...]T[sumOfLengths!StaticArrays] append(StaticArrays...)(StaticArrays arrays) if (/* insert static array constraints here */) { typeof(return) result = void; size_t offset = 0; foreach (a; arrays) { result[offset .. offset + a.length] = a[];Argh, forgot this important line: offset += a.length;} return result; }[...] T -- Marketing: the art of convincing people to pay for what they didn't need before which you fail to deliver after.
Jul 17 2017
On Mon, Jul 17, 2017 at 12:01:48PM -0700, H. S. Teoh via Digitalmars-d-learn wrote: [...]template sumOfLengths(A...) if (A.length > 0) { static if (A.length == 1) enum sumOfLengths = A[0].length; else enum sumOfLengths = A[0].length + sumOfLengths!(A[1 .. $]); } T[sumOfLengths!StaticArrays] append(StaticArrays...)(StaticArrays arrays) if (/* insert static array constraints here */) { typeof(return) result = void; size_t offset = 0; foreach (a; arrays) { result[offset .. offset + a.length] = a[]; } return result; }[...] Hmm, since we already have sumOfLengths available at compile-time, what about: T[sumOfLengths!StaticArrays] append(StaticArrays...)(StaticArrays arrays) if (allSatisfy!(isStaticArray, StaticArrays)) { typeof(return) result = void; foreach (i, a; 0 .. arrays.length) { enum offset = sumOfLengths!(arrays[0 .. i]); result[offset .. offset + a.length] = a[]; } return result; } T -- People say I'm indecisive, but I'm not sure about that. -- YHL, CONLANG
Jul 17 2017
On Monday, 17 July 2017 at 19:11:26 UTC, H. S. Teoh wrote:Hmm, since we already have sumOfLengths available at compile-time, what about: T[sumOfLengths!StaticArrays] append(StaticArrays...)(StaticArrays arrays) if (allSatisfy!(isStaticArray, StaticArrays)) { typeof(return) result = void; foreach (i, a; 0 .. arrays.length) { enum offset = sumOfLengths!(arrays[0 .. i]); result[offset .. offset + a.length] = a[]; } return result; } TThanks, I'll try this! Does this have a place in Phobos? If so, - under what name: append, concat or cat? - where: std.algorithm or std.array?
Jul 17 2017
On Mon, Jul 17, 2017 at 08:11:03PM +0000, Nordlöw via Digitalmars-d-learn wrote: [...]Does this have a place in Phobos?Never know till you try. :-DIf so, - under what name: append, concat or cat?I'd vote for concat.- where: std.algorithm or std.array?std.array, IMO, since it's specific to static arrays. T -- Without outlines, life would be pointless.
Jul 17 2017
On Monday, 17 July 2017 at 20:10:31 UTC, H. S. Teoh wrote:On Mon, Jul 17, 2017 at 08:11:03PM +0000, Nordlöw via Digitalmars-d-learn wrote: [...]Made some adjustments with working unittest and put it up on https://github.com/nordlow/phobos-next/blob/master/src/algorithm_ex.d#L2467 I had to special-case foreach body for `i == 0` since `sumOfLengths` couldn't instantiate with empty tuple `()`. Further, should `concat` support `CommonType`? That is, should int[2] x = [1, 2]; const double[2] y = [3, 4]; auto z = concat(x, y); static assert(is(typeof(z) == double[4])); be allowed?Does this have a place in Phobos?Never know till you try. :-DIf so, - under what name: append, concat or cat?I'd vote for concat.- where: std.algorithm or std.array?std.array, IMO, since it's specific to static arrays. T
Jul 17 2017
On Mon, Jul 17, 2017 at 08:28:12PM +0000, Nordlöw via Digitalmars-d-learn wrote: [...]I had to special-case foreach body for `i == 0` since `sumOfLengths` couldn't instantiate with empty tuple `()`. Further, should `concat` support `CommonType`? That is, should int[2] x = [1, 2]; const double[2] y = [3, 4]; auto z = concat(x, y); static assert(is(typeof(z) == double[4])); be allowed?See the working implementation in my latest post on this thread. That version supports CommonType, and works correctly with empty tuples as well. T -- Trying to define yourself is like trying to bite your own teeth. -- Alan Watts
Jul 17 2017
On Monday, 17 July 2017 at 20:53:36 UTC, H. S. Teoh wrote:See the working implementation in my latest post on this thread. That version supports CommonType, and works correctly with empty tuples as well. TThanks.
Jul 17 2017
On Monday, 17 July 2017 at 20:28:12 UTC, Nordlöw wrote:Made some adjustments with working unittest and put it up on https://github.com/nordlow/phobos-next/blob/master/src/algorithm_ex.d#L2467Moved here https://github.com/nordlow/phobos-next/blob/master/src/array_ex.d#L2765
Jul 17 2017
On 2017-07-17 22:11, Nordlöw wrote:- under what name: append, concat or cat?append - add array or element to existing array concat (concatenate) - add to arrays (or element) together to create a new array Seems like this is a concatenation. But please avoid shortening the names. I vote you go with "concatenate". -- /Jacob Carlborg
Jul 18 2017
On Tuesday, 18 July 2017 at 08:07:45 UTC, Jacob Carlborg wrote:append - add array or element to existing array concat (concatenate) - add to arrays (or element) together to create a new array Seems like this is a concatenation. But please avoid shortening the names. I vote you go with "concatenate".Thanks.
Jul 18 2017
OK, here's an actual, compilable, runnable version: import std.algorithm : sum; import std.meta : allSatisfy, staticMap; import std.range : only; import std.traits : CommonType, isStaticArray; alias Elem(A : E[n], E, size_t n) = E; enum Length(A) = A.length; enum sumLengths(A...) = sum(only(0, staticMap!(Length, A))); CommonType!(staticMap!(Elem, A))[sumLengths!A] append(A...)(A arrays) if (allSatisfy!(isStaticArray, A)) { typeof(return) result = void; foreach (i, a; arrays) { enum offset = sumLengths!(A[0 .. i]); result[offset .. offset + a.length] = a[]; } return result; } nogc unittest { int[2] a = [ 1, 2 ]; int[3] b = [ 3, 4, 5 ]; int[4] c = [ 6, 7, 8, 9 ]; auto d = append(a, b, c); assert(is(typeof(d) == int[9])); assert(d == [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); } T -- Be in denial for long enough, and one day you'll deny yourself of things you wish you hadn't.
Jul 17 2017
On Monday, 17 July 2017 at 20:01:41 UTC, H. S. Teoh wrote:OK, here's an actual, compilable, runnable version: import std.algorithm : sum; import std.meta : allSatisfy, staticMap; import std.range : only; import std.traits : CommonType, isStaticArray; alias Elem(A : E[n], E, size_t n) = E; enum Length(A) = A.length; enum sumLengths(A...) = sum(only(0, staticMap!(Length, A))); CommonType!(staticMap!(Elem, A))[sumLengths!A] append(A...)(A arrays) if (allSatisfy!(isStaticArray, A)) { typeof(return) result = void; foreach (i, a; arrays) { enum offset = sumLengths!(A[0 .. i]); result[offset .. offset + a.length] = a[];This slice assignment doesn't support conversion between different element-types, for instance from `int[]` to `double[]`. But I'm not convinced that we should allow `CommonType` when operator ~ doesn't.
Jul 17 2017
On Mon, Jul 17, 2017 at 10:32:14PM +0000, Nordlöw via Digitalmars-d-learn wrote:On Monday, 17 July 2017 at 20:01:41 UTC, H. S. Teoh wrote:[...]True. Maybe we should just leave out the `CommonType` thing for now. Not sure how to express that all element types should be equal in the sig constraints, though. (It shouldn't be overly hard to support `CommonType`; just replace the slice assignment with a manual loop. But yeah, probably not worth the effort.) T -- Do not reason with the unreasonable; you lose by definition.result[offset .. offset + a.length] = a[];This slice assignment doesn't support conversion between different element-types, for instance from `int[]` to `double[]`. But I'm not convinced that we should allow `CommonType` when operator ~ doesn't.
Jul 17 2017
On Monday, 17 July 2017 at 17:38:23 UTC, Nordlöw wrote:I'm want to define a specialization of `append()` that takes only static arrays as inputs and returns a static array being the sum of the lengths of the inputs. Have anybody already implemented this? If not, I'm specifically interested in how to most conveniently infer the length (as an enum) of the returned static array from the `.length`s of inputs (which of course must be enum-values too).My recursive version: auto concat(T, size_t N, ARRS...)(auto ref T[N] a, auto ref ARRS arrs) { static if(arrs.length == 0) { return a; } else { T[N + arrs[0].length] r = void; r[0..N] = a[]; r[N..$] = arrs[0][]; return concat(r, arrs[1..$]); } } unittest { int[3] a1 = [1, 2, 3]; int[3] a2 = [4, 5, 6]; int[3] a3 = [7, 8, 9]; static assert(is(typeof(concat(a1)) == int[3])); assert(concat(a1) == [1, 2, 3]); static assert(is(typeof(concat(a1, a2, a3)) == int[9])); assert(concat(a1, a2, a3) == [1, 2, 3, 4, 5, 6, 7, 8, 9]); }
Jul 18 2017
On Tuesday, 18 July 2017 at 08:46:50 UTC, Jack Applegame wrote:On Monday, 17 July 2017 at 17:38:23 UTC, Nordlöw wrote:whhhahhh template bloat!!!!!!!!!!!!!!!!!!!!I'm want to define a specialization of `append()` that takes only static arrays as inputs and returns a static array being the sum of the lengths of the inputs. Have anybody already implemented this? If not, I'm specifically interested in how to most conveniently infer the length (as an enum) of the returned static array from the `.length`s of inputs (which of course must be enum-values too).My recursive version: auto concat(T, size_t N, ARRS...)(auto ref T[N] a, auto ref ARRS arrs) { static if(arrs.length == 0) { return a; } else { T[N + arrs[0].length] r = void; r[0..N] = a[]; r[N..$] = arrs[0][]; return concat(r, arrs[1..$]); } } unittest { int[3] a1 = [1, 2, 3]; int[3] a2 = [4, 5, 6]; int[3] a3 = [7, 8, 9]; static assert(is(typeof(concat(a1)) == int[3])); assert(concat(a1) == [1, 2, 3]); static assert(is(typeof(concat(a1, a2, a3)) == int[9])); assert(concat(a1, a2, a3) == [1, 2, 3, 4, 5, 6, 7, 8, 9]); }
Jul 18 2017
On Tuesday, 18 July 2017 at 12:39:01 UTC, Stefan Koch wrote:whhhahhh template bloat!!!!!!!!!!!!!!!!!!!!Yes, and also very slow. :)
Jul 18 2017