Thread overview
Filtering a tuple of containers with indices
Nov 17, 2015
maik klein
Nov 17, 2015
anonymous
Nov 17, 2015
maik klein
Nov 17, 2015
anonymous
November 17, 2015
The question is also posted on

https://stackoverflow.com/questions/33757981/filtering-a-tuple-of-containers-with-indicies


template tupIndexToRange(alias Tup, Indices...){
  import std.meta;
  static if(Indicies.length == 0){
    alias tupIndexToRange = AliasSeq!();
  }
  else{
    alias tupIndexToRange = AliasSeq!(Tup[ Indices[0] ][], tupIndexToRange!(Tup,Indices[1..$]));
  }
}

void main{
  alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
  Integrals integrals;

  alias IntegralRange = tupIndexToRange!(integrals,0,1);
}

void main{
  alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
  Integrals integrals;

  alias IntegralRange = tupIndexToRange!(integrals,0,1);
}

I want to achieve something like this

auto range = zip(tupIndexToRange!(integrals,0,1));

I think the main problem is that Tup[ Indicies[0] ] doesn't work, to me it should have expanded to this AliasSeq!(itegrals[0][],integrals[1][]);


This is roughly what I want to achieve

  alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
  Integrals integrals;
  integrals[0].insertBack(1);
  integrals[1].insertBack(2);
  integrals[2].insertBack(3);

  auto range = zip(tuple(integrals[0][],integrals[1][]).expand);
  writeln(range);
  foreach(e;range){
    writeln("element: ",e);
  }
But instead of "auto range = zip(tuple(integrals[0][],integrals[1][]).expand);" I want it to be generic "auto range = zip(tupIndexToRange!(integrals, AliasSeq!(0, 1)).expand);"

Maybe I need use mixins?
November 17, 2015
On 17.11.2015 15:32, maik klein wrote:
> template tupIndexToRange(alias Tup, Indices...){
[snip]

I don't quite understand how that code is supposed to work. Maybe there's just some detail missing, but it could also be that your approach can't work.

> This is roughly what I want to achieve
>
>    alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
>    Integrals integrals;
>    integrals[0].insertBack(1);
>    integrals[1].insertBack(2);
>    integrals[2].insertBack(3);
>
>    auto range = zip(tuple(integrals[0][],integrals[1][]).expand);
>    writeln(range);
>    foreach(e;range){
>      writeln("element: ",e);
>    }
> But instead of "auto range =
> zip(tuple(integrals[0][],integrals[1][]).expand);" I want it to be
> generic "auto range = zip(tupIndexToRange!(integrals, AliasSeq!(0,
> 1)).expand);"

I think the problem can be split up into two independent tasks:

1) Select fields of a tuple by indices (to drop `integrals[3]`).
2) A "map" function for tuples (to apply `[]` to the selected arrays).

Here are two quick implementations of those applied to your problem:

----
template selectFromTuple(indices ...)
{
    auto selectFromTuple(Types...)(Types values)
    {
        import std.typecons: tuple, Tuple;
        static if (indices.length == 0) return Tuple!()();
        else
        {
            enum headIndex = indices[0];
            auto tail = .selectFromTuple!(indices[1 .. $])(values);
            return tuple(values[headIndex], tail.expand);
        }
    }
}

auto mapTuple(alias op, Types ...)(Types values)
{
    import std.meta: staticMap;
    import std.typecons: tuple;

    alias ResultType(T) = typeof(op(T.init));
    alias ResultTypes = staticMap!(ResultType, Types);

    ResultTypes results;
    foreach (i, v; values) results[i] = op(v);
    return tuple(results);
}

void main()
{
  import std.container.array;
  import std.meta: AliasSeq;
  import std.range: zip;
  import std.stdio: writeln;

  alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
  Integrals integrals;
  integrals[0].insertBack(1);
  integrals[1].insertBack(2);
  integrals[2].insertBack(3);

  auto range = integrals
    .selectFromTuple!(0, 1).expand
    .mapTuple!(a => a[]).expand
    .zip;

  writeln(range);
  foreach(e;range){
    writeln("element: ",e);
  }
}
----

That looks a lot like range based programming, which makes me think that there could be a way to use actual range algorithms from std.algorithm for this. But I don't see how.
November 17, 2015
On Tuesday, 17 November 2015 at 15:48:10 UTC, anonymous wrote:
> On 17.11.2015 15:32, maik klein wrote:
>> [...]
> [snip]
>
> I don't quite understand how that code is supposed to work. Maybe there's just some detail missing, but it could also be that your approach can't work.
>
> [...]

Thanks but I have one question.

.selectFromTuple!(0, 1).expand

Does this result in a copy? I avoided doing it like this because I was worried that I would copy every array. But I also don't fully understand when D will copy.


Also doing

  foreach(e;range){
    e[0] = 10;
    e[1] = 10.0f;
    writeln("element: ",e);
  }
  foreach(e;range){
    writeln("element: ",e);
  }

doesn't mutate the range at all.

November 17, 2015
On 17.11.2015 20:46, maik klein wrote:
> .selectFromTuple!(0, 1).expand
>
> Does this result in a copy? I avoided doing it like this because I was
> worried that I would copy every array. But I also don't fully understand
> when D will copy.

Yes and no. It copies the Array structs, but it does not copy the elements of the arrays. If I remember correctly, std.container.Array uses reference counting, and copying them should be cheap.

By the way, do you have a good reason to go with Array!int rather than int[]? They're similar, but the builtin int[] may be easier to handle.

> Also doing
>
>    foreach(e;range){
>      e[0] = 10;
>      e[1] = 10.0f;
>      writeln("element: ",e);
>    }
>    foreach(e;range){
>      writeln("element: ",e);
>    }
>
> doesn't mutate the range at all.

You need to mark the `e` as `ref`: `foreach(ref e; range)`. Otherwise, it's a copy of the element, and any changes to it are forgotten at the end of the iteration.

But even with `ref` it doesn't work. Seems to be a bug in or a limitation of `zip`. Works with `lockstep`:
----
  auto ranges = integrals
    .selectFromTuple!(0, 1).expand
    .mapTuple!(a => a[]).expand;
  auto range = ranges.zip;

  import std.range: lockstep;
  foreach(ref e0, ref e1; lockstep(ranges)){
    e0 = 10;
    e1 = 10.0f;
  }
  foreach(e;range){
    writeln("element: ",e);
  }
----