Range of n lines from stdin
Jakob Ovrum
jakobovrum at gmail.com
Sat Dec 28 00:56:52 PST 2013
On Friday, 27 December 2013 at 20:30:52 UTC, Ivan Kazmenko wrote:
> Hmm?.. From my experience, attempting to use a range in a
> wrong way usually results in a compilation error. For example,
> I can't do
> n.iota.map!(_ => readln).sort())
> since MapResult isn't a random access range with swappable
> elements. I can instead do
> n.iota.map!(_ => readln).array().sort())
> and it allocates an array and works as expected. So, how do I
> misuse that range?
Yes, the idea is that ranges only present interfaces that make
sense, so cases of misuse will result in a compilation error.
However, hacks like using map with functions that ignore their
argument(s) throws that out of the window: `r` in `auto r =
n.iota.map!(_ => readln);` claims to support forward,
bidirectional and random access (all read-only, as the argument
function returns by value) as well as slicing, but none of these
make any sense; all access primitives do exactly the same thing,
with the result being different every time. Even the simplest
invariants fail, such as `r.front == r.front`, and `popFront`,
`popBack` and slicing only has a binary effect, whether or not
the range is empty yet.
> Hmm. For example, that could be a RNG emitting (a range of)
> random numbers, then "empty" is always false. But we still
> want a new random number each time. Something like
> n.iota.map!(_ => uniform(0, 10))
That would only provide `n` random numbers, not an infinite
number.
All the random number generator types in `std.random` are
infinite forward ranges of random numbers, which is completely
fine. For any PRNG `r`, `r.front == r.front` is true, and remains
the same number until `r.popFront()`, it correctly has no length
and is always non-empty (infinite range), and `r.save` works
correctly etc.
> So, something like
> n.iota.map !(_ => readln).writeln;
> is bad style but
> writeln (n.iota.map !(_ => readln));
> better shows what's the main action? Makes sense for me.
No, it has nothing to do with syntax. The two examples are
completely equivalent, and the only problem is that it breaks the
invariant that the result of map's transformation function should
be derived from the arguments it was given. The fact that the
transformation function is impure is not in itself a problem:
pure functions can also ignore arguments, and impure functions
can return consistent results while still being necessarily
impure.
> Perhaps there's a wholly different way of thinking about this
> in which the first definition makes much more sense than then
> second one from the start. If so, please share it.
All you have to do is look at the signature of the function,
which is the primary part of its documentation:
Repeat!T repeat(T)(T value);
It takes one value of any type T, not a function pointer or
delegate that returns T. Even if you give it a function pointer
or delegate (which your example does not), it will simply repeat
that function pointer or delegate, never calling it.
As I already explained, `readln.repeat(n)` is just a different
way of writing `readln().repeat(n)` which in turn is also
equivalent to `repeat(readln(), n)`. This should make it
perfectly clear what it does - `readln` is called and its return
value is passed to `repeat`. Barring one relatively obscure
exception[1], this is the only way to interpret the expression
regardless of the signature of the function, as a consequence of
basic languages rules common to the entire C family of
programming languages.
[1] ... in D we have something (slightly controversial) called
the `lazy` parameter storage class, but when used, it is clearly
visible in the signature of the function.
http://dlang.org/function.html#parameters
More information about the Digitalmars-d-learn
mailing list