Tuples citizenship

bearophile bearophileHUGS at lycos.com
Thu Mar 1 14:08:44 PST 2012


I think std.typecons.Tuples merit to be a little more citizens in D and Phobos.
I think reducing the usage of "out" argument, and replacing them with a tuple result, will avoid mistakes and make the code look better. In std.math there are functions that maybe are better to use std.typecons.Tuple:

pure nothrow @trusted real frexp(real value, out int exp);
==>
pure nothrow @trusted Tuple!(real, int) frexp(real value);


nothrow @trusted real remquo(real x, real y, out int n); 
==>
nothrow @trusted Tuple!(real, int) remquo(real x, real y); 

------------------------

It's good for tuples to become more common in D code. Some time ago I have asked the built-in associative arrays to grow a method to iterate on key-value pairs, named "byPair":
http://d.puremagic.com/issues/show_bug.cgi?id=5466

Andrei answered:
>byPair is tricky because std.tuple is not visible from object.

How do you solve this problem?


Now I think about a "pairs" method too:

int[string] aa;
Tuple!(string, int) ps = aa.pairs;
foreach (p; aa.byPair)
	assert(is( typeof(p) == Tuple!(string, int) ));



This example shows an use case of byItem. Given a string, the task here is to show the char frequencies, putting higher frequencies on top, and sorting alphabetically the chars that share the same frequency.

A Python 2.6 program that solves the problem:


from collections import defaultdict
text = "the d programming language is an object oriented " + \
       "imperative multi paradigm system programming " + \
       "language created by walter bright of digital mars"
frequences = defaultdict(int)
for c in text:
    frequences[c] += 1
pairs = sorted(frequences.iteritems(), key=lambda (c,f): (-f,c))
for (c, f) in pairs:
    print f, c



The output of the Python program:

20  
14 a
12 e
11 g
11 i
11 r
10 t
9 m
6 n
5 d
5 l
5 o
4 p
4 s
3 b
3 u
2 c
2 h
2 y
1 f
1 j
1 v
1 w



Three different solutions in D (probably there are other solutions, maybe even better ones) using Phobos:


import std.stdio, std.typecons, std.algorithm, std.array;

void main() {
    auto text = "the d programming language is an object oriented " ~
                "imperative multi paradigm system programming " ~
                "language created by walter bright of digital mars";

    int[char] frequences;
    foreach (char c; text)
        frequences[c]++;

    Tuple!(int,char)[] pairs1 = array(map!(c => tuple(frequences[c], c))(frequences.byKey));
    schwartzSort!(p => tuple(-p[0], p[1]))(pairs1);
    foreach (pair; pairs1)
        writeln(pair[0], " ", pair[1]);
    writeln();

    import std.conv;
    dchar[] keys = to!(dchar[])(frequences.keys);
    schwartzSort!(c => tuple(-frequences[cast(char)c], c))(keys);
    foreach (dchar c; keys)
        writeln(frequences[cast(char)c], " ", c);
    writeln();

    Tuple!(int,char)[] pairs1b = array(map!(c => tuple(-frequences[c], c))(frequences.byKey));
    sort(pairs1b);
    foreach (pair; pairs1b)
        writeln(-pair[0], " ", pair[1]);
    writeln();
}



A version using AA.pairs (or array(AA.byPair)), I have not used 'auto' for type clarity:

Tuple!(char,int)[] pairs2 = frequences.pairs;
schwartzSort!(c_f => tuple(-c_f[1], c_f[0]))(pairs2);
foreach (c_f; pairs2)
    writeln(c_f[1], " ", c_f[0]);



I am now writing a good amount of D2 code, some of it is functional style, or it's just translated from Python, and one annoying thing that comes out quite frequently is the lack of syntax to unpack a tuple into some variables.

If you want a short self-contained example, this is a small program (and it's not much functional. Tuples aren't just for functional-style code):
http://rosettacode.org/wiki/Sokoban#D

It contains code like:

immutable dirs = [tuple( 0, -1, 'u', 'U'),
                  tuple( 1,  0, 'r', 'R'),
                  tuple( 0,  1, 'd', 'D'),
                  tuple(-1,  0, 'l', 'L')];
// ...
foreach (di; dirs) {
    CTable temp = cur;
    immutable int dx = di[0];
    immutable int dy = di[1];


With tuple unpacking becomes:

foreach (di; dirs) {
    CTable temp = cur;
    immutable (dx, dy) = di.slice!(0, 2);


I don't know if this is going to work, because di[0..2] creates a 2-typetuple!

foreach (di; dirs) {
    CTable temp = cur;
    immutable (dx, dy) = di[0 .. 2];


Maybe this is not efficient:

foreach (di; dirs) {
    CTable temp = cur;
    immutable (dx, dy, _1, _2) = di;


Another example from that little program:


alias Tuple!(CTable, string, int, int) Four;
GrowableCircularQueue!Four open;
// ...
while (open.length) {
    immutable item = open.pop();
    immutable CTable cur = item[0];
    immutable string cSol = item[1];
    immutable int x = item[2];
    immutable int y = item[3];


With a tuple unpacking syntax becomes:

while (open.length) {
    immutable (cur, cSol, x, y) = open.pop();


While I use tuples I hit similar situations often.

For people interested in trying this idea, there is a patch by Kenji Hara (one or two parts are missing, like tuple unpacking in a foreach(...) and in function signatures, but I think most meat is already present):
https://github.com/D-Programming-Language/dmd/pull/341

Bye,
bearophile


More information about the Digitalmars-d mailing list