www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Appending static arrays

reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
next sibling parent reply ag0aep6g <anonymous example.com> writes:
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
next sibling parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
parent ag0aep6g <anonymous example.com> writes:
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
prev sibling parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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:
     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.
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 :)
Jul 17 2017
next sibling parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
prev sibling parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
prev sibling next sibling parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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;
  	}


 T
Thanks, 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
next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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. :-D
 If 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
parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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: [...]
 Does this have a place in Phobos?
Never know till you try. :-D
 If 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
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?
Jul 17 2017
next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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.


 T
Thanks.
Jul 17 2017
prev sibling parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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#L2467
Moved here https://github.com/nordlow/phobos-next/blob/master/src/array_ex.d#L2765
Jul 17 2017
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
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
parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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
parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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:
[...]
 			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.
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.
Jul 17 2017
prev sibling parent reply Jack Applegame <japplegame gmail.com> writes:
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
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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:
 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]); }
whhhahhh template bloat!!!!!!!!!!!!!!!!!!!!
Jul 18 2017
parent Jack Applegame <japplegame gmail.com> writes:
On Tuesday, 18 July 2017 at 12:39:01 UTC, Stefan Koch wrote:
 whhhahhh template bloat!!!!!!!!!!!!!!!!!!!!
Yes, and also very slow. :)
Jul 18 2017