www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to unpack a tuple into multiple variables?

reply Gary Chike <chikega gmail.com> writes:
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!

```python

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


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:

```d
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"


syntax)
     //or
     auto(a, b ,c) = fruits.expand;
}
```
Feb 05
next sibling parent reply TTK Ciar <ttk ciar.org> writes:
I've been using AliasSeq for that (and aliasing it to "put" for 
easier use):


```d
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.
Feb 05
parent Gary Chike <chikega gmail.com> writes:
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! :-) ```d 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: ```d __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.
Feb 05
prev sibling next sibling parent reply Sergey <kornburn yandex.ru> writes:
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!
The direct implementation still not presented. But there are other ways to have similar functionality, as with AliasSeq example. * https://forum.dlang.org/post/rlrnhyaxcetaczjpdoem forum.dlang.org * https://forum.dlang.org/thread/xaejpgrhjzccnllcvxbl forum.dlang.org * https://forum.dlang.org/thread/kvvvidfkasezljfhkebm forum.dlang.org In those posts you can find other "solutions", Tuple DIP description and other useful ideas.
Feb 05
parent Gary Chike <chikega gmail.com> writes:
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.
Feb 05
prev sibling next sibling parent reply Profunctor <profunctor example.com> writes:
On Monday, 5 February 2024 at 21:12:58 UTC, Gary Chike wrote:
 [ ... ]
In addition to the methods hitherto provided: ```d 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")`.
Feb 05
parent reply Gary Chike <chikega gmail.com> writes:
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](https://www.anthropic.com/news/claude-2) 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 ```d 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: ```d 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!
Feb 06
parent reply Menjanahary R. R. <megnany afaky.com> writes:
On Wednesday, 7 February 2024 at 05:03:09 UTC, Gary Chike wrote:
 ... But overall it's an elegant way to indirectly add names to 
 tuples in D for ergonomic access. Nice find!

 ---
Refactored it a bit to have a ready to run and easy to grasp code. Enjoy! ```d import std.stdio : writefln; import std.typecons: tuple; // 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]); } // 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];"); } void main() { auto getUser() => tuple("John Doe", 32); // auto u = getUser().named!("name", "age"); writefln("%s (%d)", u.name, u.age); // John Doe (32) // You could also do this. with (getUser().named!("name", "age")) writefln("%s (%d)", name, age); // John Doe (32) // mixin Unpack!(u, "name", "age"); writefln("%s (%d)", name, age); // John Doe (32) } ```
Feb 07
parent Gary Chike <chikega gmail.com> writes:
On Thursday, 8 February 2024 at 06:09:29 UTC, Menjanahary R. R. 
wrote:
 Refactored it a bit to have a ready to run and easy to grasp 
 code.

 Enjoy!
Thank you Menjanahary R.! I've saved the code to review and learn from! :>
Feb 09
prev sibling parent reply zjh <fqbqrr 163.com> writes:
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 ```d //C++ auto[a,b,c]=tuple. ```
Feb 06
parent reply Gary Chike <chikega gmail.com> writes:
On Wednesday, 7 February 2024 at 01:17:33 UTC, zjh wrote:

 Officially, there should be an unpacking solution, like
 ```d
 //C++
 auto[a,b,c]=tuple.
 ```
Wouldn't that be nice? I hope a clean and terse direct-implementation comes in the near future. :)
Feb 06
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
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
 ```d
 //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
Feb 07
parent Gary Chike <chikega gmail.com> writes:
On Wednesday, 7 February 2024 at 13:18:00 UTC, ryuukk_ wrote:

 There was a DIP for native tuples in D, hopefully we'll get it 
 soon
I just wanted to define DIP for newbs since we're in a new users thread. If they look it up, they will see: Dependency Inversion Principle, Dip programming language, etc.. In the D language context, DIP means: [D Improvement Proposal (DIP)](https://github.com/dlang/DIPs) which is a formal document that details a potential feature or enhancement to the language,
Feb 07