D言語の標準ライブラリ Phobos には Haskell などでは有名な zip という関数が存在します。が、その逆となる unzip が存在しません...。

というわけで作りました。Range でもありませんし、非効率な実装ですが、D言語のメタプログラミングのしやすさが実感できました。

import std.meta : AliasSeq;
import std.traits : Unqual;
import std.typecons : Tuple, tuple;

auto toListTypes(Ts...)()
{
    alias TypeList = AliasSeq!Ts;
    string s;
    foreach(t; TypeList)
    {
        s ~= Unqual!t.stringof ~ "[],";
    }
    return s[0..$-1];
}

unittest
{
    enum result = toListTypes!(int, double, string)();
    static assert(result == "int[],double[],string[]");
}

auto unzip(Ts...)(Tuple!(Ts)[] zipped)
{
    mixin("Tuple!(" ~ toListTypes!Ts ~ ") result;");
    const n = zipped.length;
    foreach (j, T; AliasSeq!Ts)
    {
        result[j].length = n;
        foreach (i; 0 .. n)
        {
            result[j][i] = zipped[i][j];
        }
    }
    return result;
}

unittest
{
    import std.range : array, zip;
    immutable a = [1, 2, 3];
    immutable b = ["a", "b", "c"];
    immutable c = [0.1, 0.2, 0.3];
    static assert(unzip(zip(a, b, c).array) == tuple(a, b, c));
}

foreach (j, T; AliasSeq!Ts) のところに、enumerate を使うとコンパイル通らないのがハマりどころです。 あとmixin(s);するときはpragma(msg, s)とするとコンパイル時にprint debugできて楽ですね。

参考

追記

何度目かわかりませんが、ローカルの jekyll をアップデートした際に壊れてしまいました...。 そろそろこのフォーマットで書き散らかすのもキツいので、年内には MDwiki に移行する計画です。 それに向けてブログ用として、小さなツールを D 言語で書いているこの頃です。