4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

dranges.rangeについて-その1-

Last updated at Posted at 2012-12-26

前回のdranges.algorithmの続きとして、dranges.rangeについて書きます。
ただ、めちゃくちゃ長いので2回くらいに分けます。
今回はその1番目です。
drangesについてはdranges.algorithmを見てください。
ちなみにdrangesについてはすべてのunittestがコンパイルできるようになり、
かつ、unittestもすべて通るようになりました。

dranges.range.minLength

size_t minLength(R...)(R ranges) if (allSatisfy!(isForwardRange, R) && allHaveLength!R && !allAreInfinite!R)

受け取ったレンジの最小の長さを返します。受け取るレンジはすべて前進レンジで、長さを持っているか無限レンジである必要があります。

auto s = ["a", "b", "c", "d", "e", "f"];
int[] r = [0, 1, 2, 3];
auto c = repeat(5); //無限

auto ml = minLength(s, r, c);
assert(ml == 4);    //rの長さ

dranges.range.slice

Take!R slice(R)(R range, int begin, int end)

hasSlicing!Rがtrueでないものに対してもスライスするための関数です。

dranges.range.dropLast

R dropLast(R)(R range, size_t n) if (isBidirectionalRange!R)

双方向レンジを受け取り、レンジの後からn要素をけしたレンジを返します。

auto r1 = [0, 1, 2, 3, 4, 5];
auto d = dropLast(r1, 3);

assert(equal(d, [0,1,2][]));
assert(equal(dropLast(r1, 0), r1));
assert(dropLast(r1, 6).empty);
assert(dropLast(r1, 100).empty);

dranges.range.templateFunctionAnalysis

template templateFunctionAnalysis(alias templateFun, alias ParameterGenerator, size_t startN = 0, size_t limittedN = 10){
    enum endN;
    alias ... ArgumentTypes;
    enum arity;
    alias ... ReturnType;
}

このテンプレートは私が作ったものですが、そのうちdranges.traitsにいれようかと思います。
各パラメータを説明すると、
・templateFun : 解析したいテンプレート関数
・ParameterGenerator : [startN ~ limittedN]までの非負の整数を取り、templateFunの引数の型を生成するテンプレート
・startN : ParameterGeneratorを最初にインスタンス化する数
・limittedN : ParameterGeneratorをインスタンス化する場合の最終の数
となります。このとき、次のコードがコンパイル可能になるまでNをstartNからlimittedNまで増加させていきます。

templateFun(ParameterGenerator!(N).init)

使い方の例を挙げてくと、

template Generator(size_t N){alias TypeNuple!(int, N) Generator;}

alias templateFunctionAnalysis!(( (a, b, c) => a) , Generator) Result0;

static assert(Result0.arity == 3);
static assert(Result0.endN == 3);
static assert(is(Result0.ArgumentTypes == Generator!3));
static assert(is(Result0.ReturnType == int));

というようになります。
このテンプレートは、特殊な場面で使われ、naryFun!"a+b+c"などの引数の数を解析したりするのに使います。

dranges.range.dropWhile

template dropWhile(alias pred, size_t step = 1){
    template dropWhile(R, T...)if(isForwardRange!R){
        R dropWhile(R r, auto ref T subParam)
    }
}

predがtrueの間だけ要素を削除したレンジを返します。stepは1回に進める量です。
predは0以上の引数の個数に対応します。

string s = "  , abcd  efg";
auto d1 = dropWhile!(isOneOf!" ,")(s);  //' ' か ','をスキップ
assert(d1 == "abcd  efg");

auto r1 = [0, 1, 2, 3, 3, 4, 5];
auto d2 = dropWhile!"a<4"(r1);          //4未満の要素をスキップ
assert(equal(d2, [4, 5]));

auto d3 = dropWhile!"a<b"(r1);          //増加している部分を削除
assert(equal(d3, [3, 3, 4, 5]));        //(3,3)は増加していない

auto d4 = dropWhile!("a<b",2)(r1);      //[(0, 1),(2, 3),(3, 4)]
assert(equal(d4, [5]));                 //"a<b"を満たし消される

auto d5 = dropWhile!("a<b && b<c")(r1); //[(0, 1, 2), (1, 2, 3), (2, 3, 3), ...]
assert(equal(d5, [2, 3, 3, 4, 5]));     //(2, 3, 3)はpredを満たさない

auto d7 = dropWhile!"true"(r1);         //常にtrueなpred
assert(d7.empty);                       //もちろんempty

auto d7_d = dropWhile!("true", 2)(r1);  //stepが2でも
assert(d7_d.empty);                     //[5]が余るわけではない。
//[5]が余らない理由は、step=2でpredは0個の引数であるから、[0, 1, 2, 3, 3, 4, 5]の場合には
//[0, 2, 3, 5]が検査に使われ、すべての要素でtrueとなるから[5]まですべて消される。

auto r2 = [1];
auto d8 = dropWhile!"a<b"(r2);
//最初から1要素しかないのにbinary関数である場合には、そもそもpredを呼べないので検査できない
assert(equal(d8, [1][]));

auto d9 = dropWhile!"a < b"(r1, 4);     //bには4が入る。よってレンジからは1つずつ検査されていく
assert(equal(d9, [4, 5]));              //a < 4を満たす間drop

auto d10 = dropWhile!"a == b-c"(r1, 1); //cには1が入るため a == b-1が満たされるまでdrop
assert(equal(d10, [3, 3, 4, 5]));

auto d12 = dropWhile!((a, b, c) => a < b, 2)(r1, 1);
//実際にはcに1が入るが、検査にはa,bしか関わっておらず、これはd4と同じ結果[5]となる
assert(equal(d12, [5][]));

size_t cnt;
bool counter(){return cnt++ < 3;}       //0引数で3回目まではtrue

auto d16 = dropWhile!(counter)(r1);
assert(equal(d16, [3, 3, 4, 5]));       //3回目までtrueなので最初の3要素がなくなる

dranges.range.popFrontWhile

template popFrontWhile(alias pred, size_t step = 1){
    template popFrontWhile(R, T...){
        size_t popFrontWhile(ref R r, T subArgs)
    }
}

dropWhileのpopFrontバージョンです。

dranges.range.takeWhile

template takeWhile(alias pred){
    template takeWhile(R, T...)if(isForwardRange!R){
        TakeWhile takeWhile(R range, T args)
    }
}

dropWhileやpopFrontWhileのtakeバージョンです。

dranges.range.takeUntil

dranges.range.rotateWhile

実装されてない…?

dranges.range.tail

R tail(R)(R range) if (isInputRange!R)

Haskellのtailと同じです。が、emptyな場合にはそのまま返します。
Haskellの場合は空リストを渡すと例外が出るはずです。

dranges.range.tails

Tails!R tails(R)(R range) if (isForwardRange!R)

Haskellのtailsとすこし違いますが、ほとんど同じです。

auto r = [0,1,2,3];
auto t = tails(r);
assert(equal(t, [[1,2,3], [2,3], [3], [] ]));

dranges.range.heads

Heads!R heads(R)(R range) if (isForwardRange!R)

tailsのheadバージョンです。

auto r = [0,1,2,3];
auto h = heads(r);
auto witness = [ [], [0], [0,1], [0,1,2], [0,1,2,3] ][];
foreach(elem; h) {
    assert(equal(elem, witness.front));
    witness.popFront;
}

dranges.range.insertAt

auto insertAt(R1, R2)(size_t n, R1 range1, R2 range2) if (allSatisfy!(isForwardRange, R1, R2))
//または
auto insertAt(R, E)(size_t n, R range, E element) if (isForwardRange!R && is(ElementType!R == E))

次を返します。

return chain(take(range1, n), range2, drop(range1, n));
//または
return chain(take(range, n), [element][], drop(range, n));

例として、

auto r1 = [0,1,2,3];
auto m = map!"a*a"(r1);
auto ia0 = insertAt(0, r1, m);
auto ia2 = insertAt(2, r1, m);
auto ia10 = insertAt(10, r1, m);
assert(equal(ia0, [0,1,4,9,  0,1,2,3][]));
assert(equal(ia2, [0,1,  0,1,4,9,  2,3][]));
assert(equal(ia10,[0,1,2,3,  0,1,4,9][]));

auto ia1 = insertAt(1, r1, 99);
assert(equal(ia1, [0,99,1,2,3][]));

dranges.range.cutAt

Tuple!(R, R) cutAt(R)(size_t index, R range) if (isForwardRange!R && hasSlicing!R && hasLength!R)
Tuple!(ElementType!R[], R) cutAt(R)(size_t index, R range) if (isForwardRange!R && (!hasSlicing!(R) || !hasLength!(R)))

[0, index)までの要素(index個)を返り値の最初の要素に、残りを2番目に格納したものを返します。

auto r1 = [0,1,2,3,4,5];
auto c1 = cutAt(3, r1);
assert(first(c1) == [0,1,2][]);
assert(second(c1) == [3,4,5][]);

dranges.range.cutWhen

Tuple!(ElementType!(R)[], R) cutWhen(alias pred, R)(R range) if (isForwardRange!R)

predはunary関数です。
predがtrueな間だけのものを集めたのを先頭要素に、のこりを次の要素に入れ込みます。

auto r1 = [0, 1, 2, 3, 4, 1, 2];
auto cut = cutWhen!"a<3"(r1);
assert(equal(cut.field[0], [0, 1, 2]));
assert(equal(cut.field[1], [3, 4]));

dranges.range.knit

Knit!(R) knit(R...)(R ranges) if (allSatisfy!(isInputRange, R))

std.range.zipの弱いバージョンです。std.range.zipのほうがいいので、deprecatedになってもいいと思う。

dranges.range.stitch

StitchType!R stitch(R...)(R ranges) if (allSatisfy!(isTupleRange, R))

要素がtupleなレンジを複数個受け取り、tuple(a.field, b.field, ...)としたようなレンジを返します。

auto r1 = [0,     1,     2,   3,   4,   5];
auto r2 = [0.1,   0.2,   0.3, 0.4];
auto r3 = ["abc", "def", "",  "g"];
auto r4 = ["a",   "b",   "c", "d", "e", "f"];

auto k1 = knit(r1, r2); // returns Tuple!(int, double)
auto k2 = knit(r3,r4);  // returns Tuple!(string, char)
auto tf = tfilter!"b<2"(r3,r1); // returns Tuple!(string, int)

auto s = stitch(k1,k2,tf); // returns Tuple!(int,double,string,char,string,int)

assert(s.front == tuple(0, 0.1, "abc", "a", "abc", 0));
s.popFront;
assert(s.front == tuple(1, 0.2, "def", "b", "def", 1));
s.popFront;
assert(s.empty); //tfがemptyとなる。

dranges.range.twist

auto twist(int n, R)(R range) if (isTupleRange!R)

要素がtupleなレンジを受け取り、左方向にn要素回したようなレンジを返します。

auto r1 = [0,    1,    2,    3, 4];
auto r2 = [3.14, 2.78, 1.414];
auto s =  ["a",  "b",  "c",  "d","e","f"];

auto k = knit(r1,r2,s);
assert(is(ElementType!(typeof(k)) == Tuple!(int,double,string)));
assert(equal(k, [tuple(0,3.14,"a"), tuple(1,2.78,"b"), tuple(2,1.414,"c")][]));

auto rot1 = twist!1(k);
assert(is(ElementType!(typeof(rot1)) == Tuple!(double,string,int)));
assert(equal(rot1, [tuple(3.14,"a",0), tuple(2.78,"b",1), tuple(1.414,"c",2)][]));

auto rot_1 = twist!(-1)(k);
assert(is(ElementType!(typeof(rot_1)) == Tuple!(string,int,double)));
assert(equal(rot_1, [tuple("a",0,3.14), tuple("b",1,2.78), tuple("c",2,1.414)][]));

dranges.range.reverse

auto reverse(R)(R range) if (isTupleRange!R)

要素がtupleなレンジを受け取り、その順序を逆転させます。
つまり、tuple(a[n-1], a[n-2], ..., a[1], a[0])を作ります。

auto r1 = [0,    1,    2,3,  4];
auto r2 = [3.14, 2.78, 1.414];
auto s =  ["a",  "b",  "c",  "d", "e", "f"];

auto k = knit(r1, r2, s);
assert(is(ElementType!(typeof(k)) == Tuple!(int,double,string)));
assert(equal(k, [tuple(0, 3.14, "a"), tuple(1, 2.78, "b"), tuple(2, 1.414, "c")]));

auto rev = reverse(k);
assert(is(ElementType!(typeof(rev)) == Tuple!(string,double,int)));
assert(equal(rev, [tuple("a", 3.14, 0), tuple("b", 2.78, 1), tuple("c", 1.414, 2)]));

dranges.range.splice

auto splice(size_t n, R1, R2)(R1 range1, R2 range2)
if (allSatisfy!(isForwardRange, R1, R2) && is(ElementType!R1.Types) && (n <= ElementType!R1.Types.length))

range1は要素がtupleなレンジでなくてはいけず、
range1のn要素にrange2を割り込ませます。

auto r1 = [0,  1,  2,  3,  4];
auto s =  ["a","b","c","d","e","f"];
auto k = knit(r1, s);

auto r2 = [-2.1, 0.0, 3.14];

auto spl = splice!1(k, r2);     //tuple(k.front[0], r2.front, k.front[1])
assert(is(ElementType!(typeof(spl)) == Tuple!(int, double, string)));
assert(equal(spl, [tuple(0, -2.1, "a"), tuple(1, 0.0, "b"), tuple(2, 3.14, "c")]));

auto spl2 = splice!0(spl,k);    //tuple(k.front, spl.front[0], spl.front[1])
assert(is(ElementType!(typeof(spl2)) == Tuple!(Tuple!(int,string), int,double,string))); 
assert(equal(spl2, [tuple(tuple(0, "a"), 0, -2.1, "a"), tuple(tuple(1, "b"), 1, 0.0, "b"), tuple(tuple(2,"c"),2, 3.14, "c")]));

dranges.range.shred

template shred(idx...){
    auto shred(R)(R range)if(isTupleRange!R)
}

要素がtupleなレンジを受け取って、idxの順番に並べ替えます。

auto r1 = [0,    1,    2,    3,    4];
auto r2 = [3.14, 2.78, 1.414];
auto s =  ["a",  "b",  "c",  "d", "e", "f"];

auto k = knit(r1, r2, s);
auto shr1 = shred!(1,0)(k); //反転
assert(equal(shr1, [tuple(3.14, 0), tuple(2.78, 1), tuple(1.414, 2)]));

auto shr2 = shred!(1)(k); //e[1]のみを取得
assert(equal(shr2, [3.14, 2.78, 1.414]));

auto shr3 = shred!(2,0,1,1)(k); //重複もOK
assert(equal(shr3, [tuple("a", 0, 3.14, 3.14),
                    tuple("b", 1, 2.78, 2.78),
                    tuple("c", 2, 1.414, 1.414)]));

dranges.range.untuplify

TMapType!("a", R) untuplify(R)(R range) if (isTupleRange!R && ElementType!R.Types.length == 1)

要素が1要素からなるtupleなレンジを受け取って、tupleでなくします。

auto r1 = [0,1,2,3,4];

auto k = knit(r1);      //Tuple!(int)
assert(equal(k, [tuple(0), tuple(1), tuple(2), tuple(3), tuple(4)]));
auto u = untuplify(k);  //int[]
assert(equal(u, r1));

dranges.range.transverse

Transverse!R transverse(R...)(R ranges)

複数のレンジを順番にたどっていきます。

auto r1 = [0,1,2,3,4];
auto r2 = repeat(5);
auto r3 = [-2.0, -1.0, 0.0, 1.0];

auto t = transverse(r1, r2, r3);
assert(is(ElementType!(typeof(t)) == double));          //全要素はdoubleに暗黙変換される
assert(equal(t, [0.0, 5, -2, 1, 5, -1, 2, 5, 0, 3, 5, 1, 4, 5]));   //r3がemptyになり終わる

auto t2 = transverse(r1, emptyRange!double, emptyRange!short);
assert(is(ElementType!(typeof(t2)) == double));
assert(equal(t2, [0][]));   //emptyRange!doubleにより[0]だけで終わる

dranges.range.interleave

Transverse!(B,Cycle!S) interleave(B, S)(B bigRange, S smallRange)

bigRangeとsmallRangeを受け取り、
[b[0], s[0], b[1], s[1], ... , b[n], s[n % s.length]]
というようなレンジを返します。

auto i = interleave("abcdef", ",");
assert(asString(i) == "a,b,c,d,e,f,");
auto i2 = interleave("abcdef", ",;.");
assert(asString(i2) == "a,b;c.d,e;f.");
auto r1 = [0, 1, 2, 3, 4];
auto i3 = interleave(r1, r1);
assert(equal(i3, [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]));

dranges.range.segment

template segment(size_t N : 1, Range)
if(isInputRange!(Unqual!Range))
{
    Segment segment(Range range)
}


template segment(size_t N, Range)
if (isInputRange!(Unqual!Range) 
&& (isForwardRange!(Unqual!Range) ? (!isBidirectionalRange!(Unqual!Range)
                                      && !isRandomAccessRange!(Unqual!Range)) : true))
{
    Segment segment(Range range)
}


template segment(size_t N, Range)
if(isRandomAccessRange!(Unqual!Range)
&& isBidirectionalRange!(Unqual!Range)
&& hasLength!(Unqual!Range))
{
    Segment segment(Range range)
}


template segment(size_t N, Range)
if(isRandomAccessRange!(Unqual!Range)
&& !isBidirectionalRange!(Unqual!Range)
&& isInfinite!(Unqual!Range))
{
    Segment segment(Range range)
}


template segment(size_t N, Range)
if(isBidirectionalRange!(Unqual!Range)
&& (isRandomAccessRange!(Unqual!Range) ? (!hasLength!(Unqual!Range) && isInfinite!(Unqual!Range)) : true))
{
    Segment segment(Range range)
}

segmentは、drangesの至る所で使われているレンジです。
重要であるにもかかわらず、実装が貧弱で、InputRangeにも対応していなかったために私が書き直しました。
簡単な例を示しましょう。

int[] a = [0, 1, 2, 3, 4, 5];

auto s1 = segment!1(r1);
assert(equal(s1, [tuple(0), tuple(1), tuple(2), tuple(3), tuple(4), tuple(5)]));

auto s2 = segment!2(r1);
assert(equal(s2, [tuple(0, 1), tuple(1, 2), tuple(2, 3), tuple(3, 4), tuple(4, 5)]));

auto s3 = segment!3(r2);
assert(equal(s3, [tuple(0, 1, 2), tuple(1, 2, 3), tuple(2, 3, 4), tuple(3, 4, 5)]));

s3[1] = tuple(-1, -2, -3);
assert(equal(s3, [tuple(0, -1, -2), tuple(-1, -2, -3), tuple(-2, -3, 4), tuple(-3, 4, 5)]));

dranges.range.delay

template delay(alias array)if(isArray!(typeof(array)) && array.length != 0)
{
    auto delay(R)(R range)if(isInputRange!R)
}

delayはarrayが[a, a, ...]の場合には、popFrontNと後に説明するparallelによって実装されています。
上記以外の場合にはsegmentとshredを用いています。
delayはsegmentに似ていますが、segmentとは少し違います。
返り値がtupleな要素なレンジであることは同じですが、
要素eのさらに要素であるe[i]はレンジrangeをpopFrontN(range, array[i])の要素となります。

auto r1 = [0, 1, 2, 3, 4, 5];

auto d = delay!([4,1,3,2])(r1);
assert(d.front == tuple(4,1,3,2));
assert(d.length == 2);              //[(4,1,3,2), (5,2,4,3)]

dranges.range.parallel

auto parallel(size_t n, R)(R range)if(n > 0 && isInputRange!R)

parallelはレンジをn個並列にtupleに入れ込んだものを返します。

auto r1 = [0, 1, 2, 3, 4, 5];
auto p = parallel!4(r1);
assert(equal(p, [tuple(0,0,0,0), tuple(1,1,1,1), tuple(2,2,2,2), tuple(3,3,3,3), tuple(4,4,4,4), tuple(5,5,5,5)][]));
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?