February 23, 2013
Andrei Alexandrescu:

> Bearophile has long asked for a one-parameter uniform(n) that does what uniform(0, n) does.

I think that's false (you are maybe confused with my old request for iota(n)).

I have asked for a choice():
http://d.puremagic.com/issues/show_bug.cgi?id=4851


And I have asked for a uniform01() that does what uniform(0.0, 1.0) does. It's a commonly useful need, and it can be generated faster than the generic uniform(a, b):
http://d.puremagic.com/issues/show_bug.cgi?id=5240

Bye,
bearophile
February 23, 2013
>     string randomString;
>     {
>         auto tmp = new char[](10);
>         foreach(ref char c ; tmp)
>             c = letters[uniform(0, letters.length)];
>         randomString = cast(string)letters;
>     }
>
> All those scope blocks I put in are not mandatory. At the end of the day, it is 4 lines of code. Very efficient, and the intent is crystal clear.

Or you could simply do this:

string randomString = iota(10).map!(_ => letters[uniform(0, $)]).array;

It's one line off code and the intent seems crystal clear to me. It's also equally efficient. I've benchmark those to pieces of code for various values of n with both DMD and GDC, using -O -inline -release:

auto tmp = iota(n).map!(_ => letters[uniform(0, $)]).array;

....

auto tmp = new char[](n);
foreach(ref char c ; tmp)
    c = letters[uniform(0, letters.length)];

For n=10 and when compiled with GDC, the first snippet actually performed a little bit (about 10%) better, but for larger sizes there was no significant difference.
February 23, 2013
On Saturday, 23 February 2013 at 19:08:04 UTC, jerro wrote:
>>    string randomString;
>>    {
>>        auto tmp = new char[](10);
>>        foreach(ref char c ; tmp)
>>            c = letters[uniform(0, letters.length)];
>>        randomString = cast(string)letters;
>>    }
>>
>> All those scope blocks I put in are not mandatory. At the end of the day, it is 4 lines of code. Very efficient, and the intent is crystal clear.
>
> Or you could simply do this:
>
> string randomString = iota(10).map!(_ => letters[uniform(0, $)]).array;
>
> It's one line off code and the intent seems crystal clear to me. It's also equally efficient. I've benchmark those to pieces of code for various values of n with both DMD and GDC, using -O -inline -release:
>
> auto tmp = iota(n).map!(_ => letters[uniform(0, $)]).array;
>
> ....
>
> auto tmp = new char[](n);
> foreach(ref char c ; tmp)
>     c = letters[uniform(0, letters.length)];
>
> For n=10 and when compiled with GDC, the first snippet actually performed a little bit (about 10%) better, but for larger sizes there was no significant difference.

In my defense, I had started writing that when the "1 liner" was still:

auto randomLetter = () => randomSample(letters, 1, letters.length).front;
writeln(iota(10).map!(_ => randomLetter()));

The "new" 1-liner is indeed good, and what I would actually use. I still think though that there is a point where you need to stop and think "is my 1 liner actually understandable and maintainable". In this first case, the answer (IMO), was no.

Now it is, so good for us.

BTW, I think the clearest remains my generator proposal:
string randomString =
    fastGenerator!(() => letters[uniform(0, $)]).take(9).array;

Any chance you could tell me how it fares in your bench?
February 23, 2013
Can't this do the trick ?

sequence!(_ => letters[uniform($)]).take(length).array() ?
February 23, 2013
deadalnix:

> Can't this do the trick ?
>
> sequence!(_ => letters[uniform($)]).take(length).array() ?

uniform() requires two arguments.

Bye,
bearophile
February 23, 2013
> BTW, I think the clearest remains my generator proposal:
> string randomString =
>     fastGenerator!(() => letters[uniform(0, $)]).take(9).array;

If the goal was to replace iota(n).map, it may be better to just have something like:

    generate!(() => letters[uniform(0, $)])(n).array

But of course, your fastGenerator is more general. I personally don't often have a need to generate an infinite range in this way, but other people may.

> Any chance you could tell me how it fares in your bench?

I used this code:

auto tmp = FastGenerator!(() => letters[uniform(0, $)])().take(n).array;

When I build it with GDC, it performs about the same as the code that uses iota and map. When I build it with DMD it's about 10% slower than the code that uses iota and map. It seems that DMD fails to inline the lambda in this case.

By the way, this is not a very good benchmark for ranges because most of the time is spent generating random numbers.
February 23, 2013
On Saturday, 23 February 2013 at 20:21:18 UTC, jerro wrote:
>> BTW, I think the clearest remains my generator proposal:
>> string randomString =
>>    fastGenerator!(() => letters[uniform(0, $)]).take(9).array;
>
> If the goal was to replace iota(n).map, it may be better to just have something like:
>
>     generate!(() => letters[uniform(0, $)])(n).array

Arguably, the goal is to actually replace "repeat+map". I view the fact that iota not being infinite as a drawback. And even if you do want a bound to the length of your range, I believe it should be the "top" step in your types, as opposed to the "bottom" step. Eg:

iota(n).map!(_ => letters[uniform(0, $)]).array;
vs
repeat(0).map!(_ => letters[uniform(0, $)]).take(n).array;

*Personally*, I prefer the logic behind the repeat+map+take, over iota+map's. It might actually even be faster. It is slightly more verbose though :/

------
Another advantage "generate" would have over repeat+map is if you want to pass an actual named impure function (as opposed to lambda):

iota(0, n).map(_ => fun());
or
repeat(0).map(_ => fun()).take(n);
vs
fastGenerate!fun().take(n);

Here, the generate would be much more "idiomatic", and also probably easier on the compile (function vs lamba: easier inlining, as you mention below).

> But of course, your fastGenerator is more general. I personally don't often have a need to generate an infinite range in this way, but other people may.
>
>> Any chance you could tell me how it fares in your bench?
>
> I used this code:
>
> auto tmp = FastGenerator!(() => letters[uniform(0, $)])().take(n).array;
>
> When I build it with GDC, it performs about the same as the code that uses iota and map. When I build it with DMD it's about 10% slower than the code that uses iota and map. It seems that DMD fails to inline the lambda in this case.
>
> By the way, this is not a very good benchmark for ranges because most of the time is spent generating random numbers.

Another thing I'd like to re-bring up (which I've partially implemented already), was bearophile's pass/tee suggestion:
http://forum.dlang.org/thread/zkioveywsgxiovzvdypq@forum.dlang.org

It's those little functions that don't seem like much at first, but that really add up to a language's expressiveness (IMO).
February 23, 2013
> Arguably, the goal is to actually replace "repeat+map". I view the fact that iota not being infinite as a drawback. And even if you do want a bound to the length of your range, I believe it should be the "top" step in your types, as opposed to the "bottom" step. Eg:
>
> iota(n).map!(_ => letters[uniform(0, $)]).array;
> vs
> repeat(0).map!(_ => letters[uniform(0, $)]).take(n).array;
>
> *Personally*, I prefer the logic behind the repeat+map+take, over iota+map's. It might actually even be faster. It is slightly more verbose though :/

There is a way to have an infinite generate range and also a convenient way to initialize arrays - just add an overload of array() that takes the length as the second parameter. Then we can do:

auto randomString = generate!(() => letters[uniform(0, $)]).array(10);

> ------
> Another advantage "generate" would have over repeat+map is if you want to pass an actual named impure function (as opposed to lambda):
>
> iota(0, n).map(_ => fun());
> or
> repeat(0).map(_ => fun()).take(n);
> vs
> fastGenerate!fun().take(n);
>
> Here, the generate would be much more "idiomatic", and also probably easier on the compile (function vs lamba: easier inlining, as you mention below).

In general, named functions and lambdas are pretty much equivalent in regard to inlining (see http://goo.gl/XaOUP). In your example, the last line could  be more efficient than the first two, because in the first two lines there could be one extra function call in the worst case.

> Another thing I'd like to re-bring up (which I've partially implemented already), was bearophile's pass/tee suggestion:
> http://forum.dlang.org/thread/zkioveywsgxiovzvdypq@forum.dlang.org

I agree this is a good idea.
February 24, 2013
On 23 Feb 2013 20:25, "jerro" <a@a.com> wrote:
>>
>> BTW, I think the clearest remains my generator proposal:
>> string randomString =
>>     fastGenerator!(() => letters[uniform(0, $)]).take(9).array;
>
>
> If the goal was to replace iota(n).map, it may be better to just have
something like:
>
>     generate!(() => letters[uniform(0, $)])(n).array
>
> But of course, your fastGenerator is more general. I personally don't
often have a need to generate an infinite range in this way, but other people may.
>
>
>> Any chance you could tell me how it fares in your bench?
>
>
> I used this code:
>
> auto tmp = FastGenerator!(() => letters[uniform(0, $)])().take(n).array;
>
> When I build it with GDC, it performs about the same as the code that
uses iota and map. When I build it with DMD it's about 10% slower than the code that uses iota and map. It seems that DMD fails to inline the lambda in this case.
>

That is deliberate on gdc's part to mark all lambdas as inlineable as most just do one computation and don't require any custom static chain built to access locals in parent functions.  :)

Regards
----
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';


June 17, 2022

On Saturday, 23 February 2013 at 01:07:23 UTC, bearophile wrote:

>

So it becomes something like:

10.table!({ return letters.choice; }).writeln;

Dear bearophile, without the table() function in the standard library, this code does not work.

After hours, I managed to write code to generate random strings based on your code.

import std.array : array;
import std.ascii : letters, digits;
import std.random : choice, Random, unpredictableSeed;
import std.range : generate, take;
import std.stdio : writeln;

auto rnd = Random(unpredictableSeed);
auto symbols = array(letters ~ digits);

generate!({ return symbols.choice(rnd); }).take(10).writeln;