Jump to page: 1 2
Thread overview
How to unpack a tuple into multiple variables?
Feb 05
TTK Ciar
Feb 05
Sergey
Feb 07
zjh
Feb 07
ryuukk_
February 05

I hope all is well with everyone. I have come to an impasse. What is the best way to unpack a tuple into multiple variables in D similar to this Python code? Thank you!

# Example tuple
my_tuple = (2010, 10, 2, 11, 4, 0, 2, 41, 0)

# Unpack the tuple into separate variables
year, month, day, hour, minute, second, _, _, _ = my_tuple

print(f"Year: {year}, Month: {month}, Day: {day}")
print(f"Time: {hour}:{minute}:{second}")

My simple D code:

module tupleUnpack2;

import std.stdio;
import std.typecons : tuple;

void main() {
    auto fruits = tuple("apple", "banana", "cherry");

    writeln(fruits[0]); // "apple"
    writeln(fruits[1]); // "banana"
    writeln(fruits[2]); // "cherry"

    auto (a, b, c) = fruits; // wish it were this easy (C#-like syntax)
    //or
    auto(a, b ,c) = fruits.expand;
}
February 05

I've been using AliasSeq for that (and aliasing it to "put" for easier use):

import std.meta;
alias put = AliasSeq;

auto foo() { return tuple(1, 2, 3); }

int main(string[] args) {
  int x, y, z;
  put!(x, y, z) = foo();
  writeln(x, y, z);
  return 0;
}

My mnemonic: "put" is "tup" backwards, and undoes what tuple does.

February 05

On Monday, 5 February 2024 at 21:12:58 UTC, Gary Chike wrote:

>

I hope all is well with everyone. I have come to an impasse. What is the best way to unpack a tuple into multiple variables in D similar to this Python code? Thank you!

TL;DR

The direct implementation still not presented. But there are other ways to have similar functionality, as with AliasSeq example.

Some related posts

In those posts you can find other "solutions", Tuple DIP description and other useful ideas.

February 06

On Monday, 5 February 2024 at 21:30:15 UTC, Sergey wrote:

>

In those posts you can find other "solutions", Tuple DIP description and other useful ideas.

Thank you Sergey! Still reading through the different 'solutions' - very informative.

February 06

On Monday, 5 February 2024 at 21:21:33 UTC, TTK Ciar wrote:

>

My mnemonic: "put" is "tup" backwards, and undoes what tuple does.

Very clever mnemonic TTK Ciar! :) After reviewing the various solutions, the one I seem to gravitate towards is based on the one you presented, well, at least for now until a direct implementation is implemented (crossing my fingers!). I really appreciate everyone's help! :-)

import std.stdio;
import std.meta; // AliasSeq!
import std.typecons : tuple;

void main() {
    // Create a tuple
    auto myTuple = tuple(10, "hello", [1, 2]);
    auto myTuple1 = tuple(42, "bye", [3, 4]);

    // Declare some variables to unpack into
    int myInt, myInt1;
    string myString, myString1;
    int[] myArray, myArray1;

    // Use AliasSeq to unpack the tuple
    AliasSeq!(myInt1, myString1, myArray1) = myTuple1;

    // Now the variables contain the tuple contents
    writeln(myInt1); // 42
    writeln(myString1); // "bye"
    writeln(myArray1); // [3, 4]

    // using an alias to make more descriptive & possibly shorter
    alias unpack = AliasSeq;
    unpack!(myInt, myString, myArray) = myTuple;

    // Now the variables contain the tuple contents
    writeln(myInt); // 10
    writeln(myString); // "hello"
    writeln(myArray); // [1, 2]

    // Can also unpack directly:
    writeln(myTuple[0]); // 10

}

I really didn't know AliasSeq!, so I had Claude2 explain it to me. Perhaps the explanation below will help other newbies:


The AliasSeq!(myInt, myString, myArray) = myTuple; line is using AliasSeq to unpack the tuple into the individual variables. Here is what happens in more detail:

  1. AliasSeq takes a list of symbols as template arguments - in this case the variables we declared before (myInt, myString, myArray)

  2. It generates aliases to match each tuple element to the variables passed in.
    So the first alias will refer to myTuple[0], the second to myTuple[1], etc.

  3. The assignment then takes the value on the right (myTuple) and copies each tuple element over to the variable the alias refers to.

So for example, something like this is generated internally:

__alias1 = myTuple[0]; // alias for first element
__alias2 = myTuple[1]; // alias for second element

// Assign aliases to variables
myInt = __alias1;
myString = __alias2;
myArray = __alias3;

The key thing AliasSeq does behind the scenes is generate those positional aliases to each tuple element, and match them up to the variables we want to unpack into.

The assignment then copies the tuple elements over using the aliases.

February 06

On Monday, 5 February 2024 at 21:12:58 UTC, Gary Chike wrote:

>

[ ... ]

In addition to the methods hitherto provided:

auto getUser() => tuple("John Doe", 32);

// Name a Tuple's fields post hoc by copying the original's fields into a new Tuple.
template named(names...) {
    auto named(T)(ref auto T t) if (names.length <= T.Types.length) =>
        tuple!names(t.expand[0..names.length]);
}

{
    auto u = getUser().named!("name", "age");
    writeln(u.name, " (", u.age, ")"); // John Doe (32)

    // You could also do this. If only `with` were an expression!
    with (getUser().named!("name", "age"))
        writeln(name, " (", age, ")"); // John Doe (32)
}

// Define variables corresponding to a Tuple's fields in the current scope, whose
// identifiers are given by `names`.
mixin template Unpack(alias t, names...)
if (names.length <= t.Types.length) {
    static foreach (i, n; names)
        mixin("auto ", n, " = t[i];");
}

{
    auto u = getUser();
    mixin Unpack!(u, "name", "age");
    writeln(name, " (", age, ")"); // John Doe (32)
}

For either of these, one can "unpack" some or none of a Tuple's fields, and both can be modified to ignore any field to simulate val (name, _, prof) = ("John Doe", 32, "Chemist").

February 07

On Monday, 5 February 2024 at 21:12:58 UTC, Gary Chike wrote:

>

I hope all is well with everyone. I have come to an impasse. What is the best way to unpack a tuple into multiple variables in D similar to this Python code? Thank you!

Officially, there should be an unpacking solution, like

//C++
auto[a,b,c]=tuple.
February 07

On Tuesday, 6 February 2024 at 07:17:34 UTC, Profunctor wrote:

>

On Monday, 5 February 2024 at 21:12:58 UTC, Gary Chike wrote:

>

For either of these, one can "unpack" some or none of a Tuple's fields, and both can be modified to ignore any field to simulate val (name, _, prof) = ("John Doe", 32, "Chemist").

Thank you Profunctor! This gives me a lot to chew on. :)

I had to ask Anthropic Claude2 to help me understand what's going on. I placed the response below for other newbs to the D language:


This is a very clever alternative approach to tuple unpacking in D suggested on the forums! Here is how it works:

  1. Define a named template that takes a tuple and field names. It returns a new tuple with those field names applied to the original tuple's values.

  2. Access the new named tuple naturally with dot notation

auto user = getUser();

auto namedUser = user.named!("name", "age");

writeln(namedUser.name);

Similarly, the Unpack mixin template defines variables in the current scope matching the tuple by name:

mixin Unpack!(user, "name", "age");

writeln(name); // accessed naturally now

So both work by naming the anonymous tuple fields, allowing clean access without indexes.

The pros of this approach:

  • No AliasSeq, very straightforward
  • Custom field names
  • Clean syntax after setup

The cons:

  • More verbose initial setup
  • Doesn't modify original tuple

But overall it's an elegant way to indirectly add names to tuples in D for ergonomic access. Nice find!

February 07

On Wednesday, 7 February 2024 at 01:17:33 UTC, zjh wrote:

>

Officially, there should be an unpacking solution, like

//C++
auto[a,b,c]=tuple.

Wouldn't that be nice? I hope a clean and terse direct-implementation comes in the near future. :)

February 07

On Wednesday, 7 February 2024 at 05:29:45 UTC, Gary Chike wrote:

>

On Wednesday, 7 February 2024 at 01:17:33 UTC, zjh wrote:

>

Officially, there should be an unpacking solution, like

//C++
auto[a,b,c]=tuple.

Wouldn't that be nice? I hope a clean and terse direct-implementation comes in the near future. :)

There was a DIP for native tuples in D, hopefully we'll get it soon

« First   ‹ Prev
1 2