digitalmars.D.bugs - [Issue 4591] New: Concat of std.typecons.Tuples
- d-bugmail puremagic.com (159/159) Aug 06 2010 http://d.puremagic.com/issues/show_bug.cgi?id=4591
- d-bugmail puremagic.com (10/10) Oct 07 2010 http://d.puremagic.com/issues/show_bug.cgi?id=4591
- d-bugmail puremagic.com (13/13) Jul 25 2011 http://d.puremagic.com/issues/show_bug.cgi?id=4591
- d-bugmail puremagic.com (46/49) May 14 2013 http://d.puremagic.com/issues/show_bug.cgi?id=4591
http://d.puremagic.com/issues/show_bug.cgi?id=4591 Summary: Concat of std.typecons.Tuples Product: D Version: D2 Platform: All OS/Version: All Status: NEW Keywords: patch Severity: enhancement Priority: P2 Component: Phobos AssignedTo: nobody puremagic.com ReportedBy: bearophile_hugs eml.cc In Python it's often useful to join tuples. So I suggest to add this operator to the tuples of std.typecons. A problem arises when you try to concatenate two Tuple that share one or more field names. This problem can be faced in several different ways, here I show two different solutions. Both solutions can allow to perform Tuple~Tuple or Tuple~struct but not struct~Tuple. To perform struct~Tuple you have to use Tuple!()()~struct~Tuple (I don't know if this can be improved). This first version refuses to join two tuples if two field names clash: alias T TypesAndStrings; // better to move this at the top of the Tuple struct /// auto opBinary(string op, TOther)(TOther other) if (op == "~" && is(TOther == struct) && (!__traits(compiles, { void isTuple(U...)(Tuple!U){} isTuple(other); }) || distinctFieldNames!(T, TOther.TypesAndStrings)() )) { // is TOther a Tuple? static if (__traits(compiles, { void isTuple(U...)(Tuple!U){} isTuple(other); })) { enum string fieldsName = "field"; alias TOther.Types OtherTypes; Tuple!(T, TOther.TypesAndStrings) result; } else { enum string fieldsName = "tupleof"; alias typeof(TOther.tupleof) OtherTypes; Tuple!(T, OtherTypes) result; } // copy fields of this instance foreach (i, Unused; Types) // static foreach static if (__traits(isStaticArray, Types[i])) mixin( Format!("result.field[%s][] = this.%s[%s];", i, fieldsName, i) ); else mixin( Format!("result.field[%s] = this.%s[%s];", i, fieldsName, i) ); // copy fields of other instance foreach (i, Unused; OtherTypes) // static foreach static if (__traits(isStaticArray, OtherTypes[i])) mixin( Format!("result.field[%s][] = other.%s[%s];", i + Types.length, fieldsName, i) ); else mixin( Format!("result.field[%s] = other.%s[%s];", i + Types.length, fieldsName, i) ); return result; } +/ This second version removes the fields names from both tuples if and only if two or more field names clash: alias T TypesAndStrings; // better to move this at the top of the Tuple struct /// auto opBinary(string op, TOther)(TOther other) if (op == "~" && is(TOther == struct)) { // is TOther a Tuple? static if (__traits(compiles, { void isTuple(U...)(Tuple!U){} isTuple(other); })) { enum string fieldsName = "field"; alias TOther.Types OtherTypes; static if (distinctFieldNames!(T, TOther.TypesAndStrings)()) { Tuple!(T, TOther.TypesAndStrings) result; } else { Tuple!(Types, OtherTypes) result; } } else { enum string fieldsName = "tupleof"; alias typeof(TOther.tupleof) OtherTypes; Tuple!(T, OtherTypes) result; } // copy fields of this instance foreach (i, Unused; Types) // static foreach static if (__traits(isStaticArray, Types[i])) mixin( Format!("result.field[%s][] = this.%s[%s];", i, fieldsName, i) ); else mixin( Format!("result.field[%s] = this.%s[%s];", i, fieldsName, i) ); // copy fields of other instance foreach (i, Unused; OtherTypes) // static foreach static if (__traits(isStaticArray, OtherTypes[i])) mixin( Format!("result.field[%s][] = other.%s[%s];", i + Types.length, fieldsName, i) ); else mixin( Format!("result.field[%s] = other.%s[%s];", i + Types.length, fieldsName, i) ); return result; } } --------------------- To work that code needs Iota and distinctFieldNames that can be found in bug 4582 : private template Iota(int stop) { // this is useful in general static if (stop <= 0) alias TypeTuple!() Iota; else alias TypeTuple!(Iota!(stop-1), stop-1) Iota; } private bool distinctFieldNames(T...)() { enum int tlen = T.length; // can't move this below, probably DMD bug foreach (i1; Iota!(tlen)) static if (is(typeof(T[i1]) : string)) foreach (i2; Iota!(tlen)) static if (i1 != i2 && is(typeof(T[i2]) : string)) if (T[i1] == T[i2]) return false; return true; } --------------------- Both versions need an extra alias that keeps all the Tuple instantiation arguments: alias T TypesAndStrings; This code shown to me by Philippe Sigaud avoids to define this alias, but this solution looks too much complex for this job: private string[3] _between(char b, char e, string s)() { int foundb, ib; string notFound = ""; foreach (i, c; s) { if (c == b) { if (foundb == 0) { foundb = 1; ib = i+1; continue; } else { foundb++; } } if (c == e) { if (foundb == 1) return [s[0 .. ib-1], s[ib .. i], s[i+1 .. $]]; else foundb--; } } return [s, notFound, notFound]; } /// Given an instantiated template, returns a tuple of its arguments. template TemplateParameters(T) { mixin("alias TypeTuple!(" ~ _between!('(', ')' , T.stringof)[1] ~ ") TemplateParameters;"); } If the _between() + TemplateParameters() are added to Phobos then I can use them instead of the TypesAndStrings alias. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Aug 06 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4591 Andrei Alexandrescu <andrei metalanguage.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |ASSIGNED CC| |andrei metalanguage.com AssignedTo|nobody puremagic.com |andrei metalanguage.com -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Oct 07 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4591 Slicing too is sometimes useful: import std.typecons; void main() { auto t1 = tuple(10, 20, 30, 40, 50); auto t2 = tuple(100, 200, 300); auto t3 = t1[0 .. 2]; // tuple slicing auto t4 = t1 ~ t2; // tuple concat } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 25 2011
http://d.puremagic.com/issues/show_bug.cgi?id=4591 bearophile_hugs eml.cc changed: What |Removed |Added ---------------------------------------------------------------------------- AssignedTo|andrei erdani.com |nobody puremagic.com I suggest to add the support for Tuple concatenation and join: In Python 2.6:(1, 2, 1, 2)t1 = (1, 2) t1 + t1(1, 2, 3) Proposed D syntax: void main() { import std.typecons; auto t1 = tuple(1, 2); auto t2 = t1 ~ t1; auto t3a = t1 ~ 3; auto t3b = t1 ~ tuple(3); } An use case, this computes the frequency of the first digit (Benford's Law): import std.stdio, std.range, std.math, std.conv, std.bigint, std.algorithm; auto benford(R)(R data) { auto heads = data.filter!q{a != 0}.map!q{ a.text[0] - '1' }.array; immutable double k = heads.length; return iota(1, 10) .zip(heads.sort().group.map!(p => p[1] / k)) .map!q{ [a[]] ~ log10(1.0 + 1.0 / a[0]) }; } void main() { auto fibs = recurrence!q{a[n - 1] + a[n - 2]}(1.BigInt, 1.BigInt); writefln("%9s %9s %9s", "Actual", "Expected", "Deviation"); foreach (p; fibs.take(1000).benford) writefln("%1.0f: %5.2f%% | %5.2f%% | %5.4f%%", p[0], p[1] * 100, p[2] * 100, abs(p[2] - p[1]) * 100); } Currently the benford() function returns a range of double[]: .map!q{ [a[]] ~ log10(1.0 + 1.0 / a[0]) }; If I want to return a range of 3-tuples: .map!q{ tuple(a[], log10(1.0 + 1.0 / a[0])) }; With the proposed syntax the code becomes: .map!q{ a ~ log10(1.0 + 1.0 / a[0]) }; Or: .map!q{ a ~ tuple(log10(1.0 + 1.0 / a[0])) }; -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------t1 + (3,)
May 14 2013