Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 1755

Show
Ignore:
Timestamp:
07/14/10 01:16:18 (14 years ago)
Author:
andrei
Message:

put(R, E) definition and fix for bugzilla 4456

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/phobos/std/range.d

    r1749 r1755  
    218218        void popFront(); 
    219219        bool empty(); 
    220220        int front(); 
    221221    } 
    222222    static assert(isInputRange!(B)); 
    223223    static assert(isInputRange!(int[])); 
    224224    static assert(isInputRange!(char[])); 
    225225} 
    226226 
    227227/** 
    228 Returns $(D true) if $(D R) is an output range. An output range can be 
    229 defined in three ways. 
    230  
    231 $(OL 
    232  
    233 $(LI $(D R) might define the primitive $(D put) that accepts an object 
    234 convertible to $(D E). The following code should compile for such an 
    235 output range: 
    236  
    237 ---- 
    238 R r; 
    239 E e; 
    240 r.put(e);       // can write an element to the range 
    241 ---- 
    242  
    243 The semantics of $(D r.put(e)) an output range (not checkable during 
    244 compilation) are assumed to output $(D e) to $(D r) and advance to the 
    245 next position in $(D r), such that successive calls to $(D r.put) add 
    246 extra elements to $(D r).) 
    247  
    248 $(LI An input range with assignable elements is also an output 
    249 range. In that case, inserting elements into the range is effected 
    250 with two primitive calls: $(D r.front = e, r.popFront()). Such a range 
    251 functions for output only as long as it is not empty.) 
    252  
    253 ---- 
    254 R r; 
    255 E e; 
    256 r.front = e;    // can write an element to the range 
    257 ---- 
    258  
    259 $(LI Any $(D function) or $(D delegate) accepting arrays of $(D E) as 
    260 input is also a valid output range.)) 
    261  
    262 To write elements to either kind of output range, call the free 
    263 function $(D put(r, e)) defined below. That function takes the 
    264 appropriate cource of action depending on the range's kind. 
    265  */ 
    266 template isOutputRange(R, E) 
    267 
    268     enum bool isOutputRange = is(typeof( 
    269     { 
    270         R r; 
    271         E e; 
    272         r.put(e);          // can write element to range 
    273     }())) 
    274         || 
    275     isInputRange!R && is(typeof( 
    276     { 
    277         R r; 
    278         E e; 
    279         r.front = e;       // can assign to the front of range 
    280     }())) 
    281         || 
    282         is(typeof( 
    283     { 
    284         R r; 
    285         E[] es; 
    286         r(es); 
    287     }())); 
    288 
    289  
    290 /** 
    291 Outputs $(D e) to $(D r), which must be an output range. Depending on 
    292 the range's kind, it evaluates one of the following: 
    293  
    294 $(OL 
    295  
    296 $(LI If $(D R) defines $(D put), then output is effected by evaluating 
    297 $(D r.put(e)).) 
    298  
    299 $(LI Else if $(D R) is an input range with assignable front, then 
    300 output is effected by evaluating $(D (r.front = e, r.popFront())).) 
    301  
    302 $(LI Else if $(D R) is a function type, a delegate type, or a type 
    303 defining $(D opCall), then output is effected by evaluating $(D 
    304 r(e)).) 
    305  */ 
    306 void put(R, E)(ref R r, E e) if (isOutputRange!(R, E)) 
    307 
    308     static if (!isArray!R && is(typeof(r.put(e)))) 
    309     { 
    310         r.put(e); 
    311     } 
    312     else static if (isInputRange!R && is(typeof(r.front = e))) 
    313     { 
    314         r.front = e; 
    315         r.popFront(); 
    316     } 
    317     else static if (isArray!E && is(typeof(r(e)))) 
    318     { 
    319         r(e); 
    320     } 
    321     else static if (is(typeof(r(new E[])))) 
    322     { 
    323         r((&e)[0 .. 1]); 
     228Outputs $(D e) to $(D r). The exact effect is dependent upon the two 
     229types. which must be an output range. Several cases are accepted, as 
     230described below. The code snippets are attempted in order, and the 
     231first to compile "wins" and gets evaluated. 
     232 
     233$(BOOKTABLE , 
     234 
     235$(TR $(TH Code Snippet) $(TH Scenario)) 
     236 
     237$(TR $(TD $(D r.put(e);)) $(TD $(D R) specifically defines a method 
     238$(D put) accepting an $(D E).)) 
     239 
     240$(TR $(TD $(D r.put([ e ]);)) $(TD $(D R) specifically defines a 
     241method $(D put) accepting an $(D E[]).)) 
     242 
     243$(TR $(TD $(D r.front = e; r.popFront();)) $(TD $(D R) is an input 
     244range and $(D e) is assignable to $(D r.front).)) 
     245 
     246$(TR $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);)) $(TD 
     247Copying range $(D E) to range $(D R).)) 
     248 
     249$(TR $(TD $(D r(e);)) $(TD $(D R) is e.g. a delegate accepting an $(D 
     250E).)) 
     251 
     252$(TR $(TD $(D r([ e ]);)) $(TD $(D R) is e.g. a $(D delegate) 
     253accepting an $(D E[]).)) 
     254 
     255
     256 */ 
     257void put(R, E)(ref R r, E e) 
     258
     259    static if (hasMember!(R, "put")) 
     260    { 
     261        // commit to using the "put" method 
     262        static if (!isArray!R && is(typeof(r.put(e)))) 
     263        { 
     264            r.put(e); 
     265        } 
     266        else static if (!isArray!R && is(typeof(r.put((&e)[0..1])))) 
     267        { 
     268            r.put((&e)[0..1]); 
     269        } 
    324270    } 
    325271    else 
    326272    { 
    327         static assert(false); 
     273        static if (isInputRange!R) 
     274        { 
     275            // Commit to using assignment to front 
     276            static if (is(typeof(r.front = e, r.popFront()))) 
     277            { 
     278                r.front = e; 
     279                r.popFront(); 
     280            } 
     281            else static if (isInputRange!E && is(typeof(put(r, e.front)))) 
     282            { 
     283                for (; !e.empty; e.popFront()) put(r, e.front); 
     284            } 
     285        } 
     286        else 
     287        { 
     288            // Commit to using opCall 
     289            static if (is(typeof(r(e)))) 
     290            { 
     291                r(e); 
     292            } 
     293            else static if (is(typeof(r((&e)[0..1])))) 
     294            { 
     295                r((&e)[0..1]); 
     296            } 
     297            else 
     298            { 
     299                static assert(false, 
     300                        "Cannot put a "~E.stringof~" into a "~R.stringof); 
     301            } 
     302        } 
    328303    } 
    329304} 
    330305 
    331306unittest 
    332307{ 
    333308    struct A {} 
    334309    static assert(!isInputRange!(A)); 
    335310    struct B 
    336311    { 
    337         void put(int); 
    338     } 
    339     static assert(isOutputRange!(B, int)); 
    340     static assert(isOutputRange!(int[], int)); 
     312        void put(int) {} 
     313    } 
     314    B b; 
     315    put(b, 5); 
     316
     317 
     318unittest 
     319
     320    int[] a = [1, 2, 3], b = [10, 20]; 
     321    auto c = a; 
     322    put(a, b); 
     323    assert(c == [10, 20, 3]); 
     324    assert(a == [3]); 
     325
     326 
     327unittest 
     328
     329    int[] a = new int[10]; 
     330    int b; 
     331    static assert(isInputRange!(typeof(a))); 
     332    put(a, b); 
     333
     334 
     335unittest 
     336
     337    void myprint(in char[] s) { } 
     338    auto r = &myprint; 
     339    put(r, 'a'); 
     340
     341 
     342/** 
     343Returns $(D true) if $(D R) is an output range for elements of type 
     344$(D E). An output range can be defined functionally as a range that 
     345supports the operation $(D put(r, e)) as defined above. 
     346 */ 
     347template isOutputRange(R, E) 
     348
     349    enum bool isOutputRange = is(typeof({ R r; E e; put(r, e); }())); 
    341350} 
    342351 
    343352unittest 
    344353{ 
    345354    void myprint(in char[] s) { writeln('[', s, ']'); } 
    346355    static assert(isOutputRange!(typeof(&myprint), char)); 
     356 
     357    auto app = appender!string; 
     358    string s; 
     359    static assert(isOutputRange!(Appender!string, string)); 
    347360} 
    348361 
    349362/** 
    350363Returns $(D true) if $(D R) is a forward range. A forward range is an 
    351364input range that can save "checkpoints" by simply copying it to 
    352365another value of the same type. Notable examples of input ranges that 
    353366are $(I not) forward ranges are file/socket ranges; copying such a 
    354367range will not save the position in the stream, and they most likely 
    355368reuse an internal buffer as the entire stream does not sit in 
    356369memory. Subsequently, advancing either the original or the copy will 
     
    23392352struct Recurrence(alias fun, StateType, size_t stateSize) 
    23402353{ 
    23412354    StateType[stateSize] _state; 
    23422355    size_t _n; 
    23432356 
    23442357    this(StateType[stateSize] initial) { _state = initial; } 
    23452358 
    23462359    void popFront() 
    23472360    { 
    23482361        _state[_n % stateSize] = binaryFun!(fun, "a", "n")( 
    2349             cycle(_state, _n), _n + stateSize); 
     2362            cycle(_state), _n + stateSize); 
    23502363        ++_n; 
    23512364    } 
    23522365 
    23532366    @property StateType front() 
    23542367    { 
    23552368        return _state[_n % stateSize]; 
    23562369    } 
    23572370 
    23582371    enum bool empty = false; 
    23592372} 
     
    23632376recurrence(alias fun, State...)(State initial) 
    23642377{ 
    23652378    CommonType!(State)[State.length] state; 
    23662379    foreach (i, Unused; State) 
    23672380    { 
    23682381        state[i] = initial[i]; 
    23692382    } 
    23702383    return typeof(return)(state); 
    23712384} 
    23722385 
    2373 version(none) unittest 
     2386unittest 
    23742387{ 
    23752388    auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); 
    23762389    int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]; 
    23772390    //foreach (e; take(fib, 10)) writeln(e); 
    23782391    assert(equal(take(fib, 10), witness)); 
    23792392    foreach (e; take(fib, 10)) {}//writeln(e); 
    23802393    //writeln(s.front); 
    23812394    auto fact = recurrence!("n * a[n-1]")(1); 
    23822395    assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6, 
    23832396                            2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) ); 
    23842397    auto piapprox = recurrence!("a[n] + (n & 1 ? 4. : -4.) / (2 * n + 3)")(4.); 
    23852398    foreach (e; take(piapprox, 20)) {}//writeln(e); 
     2399 
     2400    // Thanks to yebblies for this test and the associated fix 
     2401    auto r = recurrence!"a[n-2]"(1, 2); 
     2402    witness = [1, 2, 1, 2, 1]; 
     2403    assert(equal(take(r, 5), witness)); 
    23862404} 
    23872405 
    23882406/** 
    23892407$(D Sequence) is similar to $(D Recurrence) except that iteration is 
    23902408presented in the so-called $(WEB en.wikipedia.org/wiki/Closed_form, 
    23912409closed form). This means that the $(D n)th element in the series is 
    23922410computable directly from the initial values and $(D n) itself. This 
    23932411implies that the interface offered by $(D Sequence) is a random-access 
    23942412range, as opposed to the regular $(D Recurrence), which only offers 
    23952413forward iteration.