Iota

H. S. Teoh hsteoh at qfbox.info
Thu Aug 4 19:06:43 UTC 2022


On Thu, Aug 04, 2022 at 05:52:46PM +0000, Paul Backus via Digitalmars-d wrote:
> On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
> > What I want to highlight, it's the fact that iota seems flawed: it
> > assumes that every type supporting increment operators will generate
> > a nice, consecutive, ordered sequence of values.
> > 
> > Iota specialises on floating point values exactly because of the
> > problem above, but assumes that *any* other type will nicely behave
> > as long you can put a ++ near a value.
> > 
> > We can ignore the hypotetical situation in which someone will find
> > an unusual usage of ++ (probably there is one person that never
> > imagined that << can be used for console output), but we already
> > have a candidate where iota will fail: CustomFloat from std.numeric.
> 
> Yeah, this is a classic case of what Andrei calls ["generality
> creep"][1].
> 
> You start out with the obviously-correct core functionality. Then you
> start gradually extending it to handle new edge cases. Each one seems
> reasonable enough on its own, but by the end of the process, you find
> yourself with an API that's complex and hard to use, wondering where
> it all went wrong.
> 
> [1]: https://forum.dlang.org/thread/q6plhj$1l9$1@digitalmars.com

I'm probably partly to blame for this. :-/   Cf.:

	https://issues.dlang.org/show_bug.cgi?id=6447

After thinking about this, I think in its barest essentials, the only
version of iota we need is the one that takes a single int argument, and
that generates that many numbers, starting from 0.  Everything else can
be built on top of this with other existing library functions.

For example, if you want a range that starts from 10 and ends at 20,
just write:

	iota(10).map!(i => i + 10)

If you want a range that starts at 3 and steps by 5 each time, just write:

	iota(n).map!(i => 3 + i*5)

If you want a floating-point range, just write:

	iota(n).map!(i => cast(double) i)

If you want a range of values of a custom type that generates values via
repeated applications of ++, just write:

	MyType startValue = ...;
	auto r = generate!({ return ++startValue; }).take(n);

	// Or, if you want to stop at some sentinel value:
	auto r = generate!({ return ++startValue; })
		.until!(e => e == MyType.sentinelValue);

This could be used, for example, to generate a range over an enum.

If your custom type supports + and you want to use that to provide
random access, just do:

	auto r = iota(n).map!(i => MyType.startValue + i);

And so forth.

Everything else is just icing, and not strictly necessary.


T

-- 
Give a man a fish, and he eats once. Teach a man to fish, and he will sit forever.


More information about the Digitalmars-d mailing list