Thread overview
map question
Jan 22, 2022
forkit
Jan 22, 2022
Stanislav Blinov
Jan 22, 2022
forkit
Jan 23, 2022
Stanislav Blinov
Jan 23, 2022
Siarhei Siamashka
Jan 23, 2022
Stanislav Blinov
Jan 22, 2022
Ali Çehreli
January 22, 2022
trying to make sense of the below:


// ---
module test;

import std;

void main()
{
    auto rnd = Random(unpredictableSeed);

    int howManyTimes = 5;

    // ok - using 'e =>' makes sense
    writeln(howManyTimes.iota.map!(e => rnd.dice(0.6, 1.4)).format!"%(%s,%)");

    // ok - though using 'howManyTimes =>' doesn't make much sense??
    writeln(howManyTimes.iota.map!(howManyTimes => rnd.dice(0.6, 1.4)).format!"%(%s,%)");

    // NOT ok - using '5 =>' - but isn't this effectively the same as above line?
    //writeln(howManyTimes.iota.map!(5 => rnd.dice(0.6, 1.4)).format!"%(%s,%)");
}

// ---
January 22, 2022
On Saturday, 22 January 2022 at 19:32:07 UTC, forkit wrote:
> trying to make sense of the below:
>
>
> // ---
> module test;
>
> import std;
>
> void main()
> {
>     auto rnd = Random(unpredictableSeed);
>
>     int howManyTimes = 5;
>
>     // ok - using 'e =>' makes sense
>     writeln(howManyTimes.iota.map!(e => rnd.dice(0.6, 1.4)).format!"%(%s,%)");
>
>     // ok - though using 'howManyTimes =>' doesn't make much sense??
>     writeln(howManyTimes.iota.map!(howManyTimes => rnd.dice(0.6, 1.4)).format!"%(%s,%)");
>
>     // NOT ok - using '5 =>' - but isn't this effectively the same as above line?
>     //writeln(howManyTimes.iota.map!(5 => rnd.dice(0.6, 1.4)).format!"%(%s,%)");
> }
>
> // ---

No, it's not the same. 'Tis not really a "map question", looks more like a question about

https://dlang.org/spec/expression.html#function_literals (see #10).

In the second case, you're defining a lambda with single parameter named `howManyTimes`, which is not at all related to your local variable of the same name. Third case is invalid, as you're effectively trying to do this:

auto func(T)(T 5) { return rnd.dice(0.6, 1.4); }

Which, of course, doesn't make any sense, does it? :)

Given your use case (call a function N times), I think `generate` would be more appropriate here:

```d
import std.random;
import std.stdio;
import std.range : generate, take;

void main()
{
    auto rnd = Random(unpredictableSeed);
    int howManyTimes = 5;
    generate!(() => rnd.dice(0.6, 1.4)).take(howManyTimes).writeln;
}

```
January 22, 2022
On 1/22/22 11:32, forkit wrote:
> trying to make sense of the below:

The generate() solution shown by Stanislav Blinov is suitable here.

>      auto rnd = Random(unpredictableSeed);

Somebody else mentioned this before but none of the programs we've seen so far seemed to need a special random number generator. So, the default one, which is already randomized, would suffice. Just drop the line above.

>      // ok - using 'e =>' makes sense
>      writeln(howManyTimes.iota.map!(e => rnd.dice(0.6,
> 1.4)).format!"%(%s,%)");

When there is a syntactic need for a variable but that variable is not used, the idiomatic way of naming that variable is '_':

    howManyTimes.iota.map!(_ => dice(0.6, 1.4))

(Note: I did not write rnd.dice.)

Ali

January 22, 2022
On Saturday, 22 January 2022 at 19:55:43 UTC, Stanislav Blinov wrote:
>

thanks for the explanation. That really helped :-)

writeln( generate!(() => dice(0.6, 1.4)).take(howManyTimes) );
[1, 1, 1, 1, 0]

(or after reading Ali's response - getting rid of rnd, and using _ )

writeln( howManyTimes.iota.map!(_ => dice(0.6, 1.4)) );
[1, 0, 1, 1, 1]

They produce exactly the same thing, so I guess it comes down to personal choice now.

January 23, 2022
On Saturday, 22 January 2022 at 23:54:27 UTC, forkit wrote:
> On Saturday, 22 January 2022 at 19:55:43 UTC, Stanislav Blinov wrote:
>>
>
> thanks for the explanation. That really helped :-)
>
> writeln( generate!(() => dice(0.6, 1.4)).take(howManyTimes) );
> [1, 1, 1, 1, 0]
>
> (or after reading Ali's response - getting rid of rnd, and using _ )
>
> writeln( howManyTimes.iota.map!(_ => dice(0.6, 1.4)) );
> [1, 0, 1, 1, 1]
>
> They produce exactly the same thing, so I guess it comes down to personal choice now.

Using `iota` here incurs additional computation and argument copies that are actually never used, i.e. wasted work. So I'd say go with `generate`, as that seems the intent.
January 23, 2022

On Sunday, 23 January 2022 at 09:08:46 UTC, Stanislav Blinov wrote:

>

Using iota here incurs additional computation and argument copies that are actually never used, i.e. wasted work. So I'd say go with generate, as that seems the intent.

Isn't this normally a compiler's job to eliminate all unused computations and copies?

    auto foobar1(size_t n)
    {
        return n.iota.map!(_ => 123).array;
    }

    auto foobar2(size_t n)
    {
        return generate!(() => 123).take(n).array;
    }

LDC with "-O -release" command line options generates pretty much identical code for foobar1 and foobar2 (I'm only showing the main loop, but the rest is also the same):

  20:   48 39 c8                cmp    %rcx,%rax
  23:   74 18                   je     3d <_D2zz7foobar1FNaNbNfmZAi+0x3d>
  25:   c7 04 8a 7b 00 00 00    movl   $0x7b,(%rdx,%rcx,4)
  2c:   48 83 c1 01             add    $0x1,%rcx
  30:   48 39 cb                cmp    %rcx,%rbx
  33:   75 eb                   jne    20 <_D2zz7foobar1FNaNbNfmZAi+0x20>
  20:   48 39 c8                cmp    %rcx,%rax
  23:   74 18                   je     3d <_D2zz7foobar2FNaNbNfmZAi+0x3d>
  25:   c7 04 8a 7b 00 00 00    movl   $0x7b,(%rdx,%rcx,4)
  2c:   48 83 c1 01             add    $0x1,%rcx
  30:   48 39 cb                cmp    %rcx,%rbx
  33:   75 eb                   jne    20 <_D2zz7foobar2FNaNbNfmZAi+0x20>

Do you have a better example to demonstrate generate's superiority?

January 23, 2022

On Sunday, 23 January 2022 at 09:38:57 UTC, Siarhei Siamashka wrote:

>

On Sunday, 23 January 2022 at 09:08:46 UTC, Stanislav Blinov wrote:

>

Using iota here incurs additional computation and argument copies that are actually never used, i.e. wasted work. So I'd say go with generate, as that seems the intent.

Isn't this normally a compiler's job to eliminate all unused computations and copies?

It is the programmer's job long before it is the compiler's. It can do wonders on your "minimal" code but it's not its job to read your mind.

>
    auto foobar1(size_t n)
    {
        return n.iota.map!(_ => 123).array;
    }

    auto foobar2(size_t n)
    {
        return generate!(() => 123).take(n).array;
    }

LDC with "-O -release" command line options generates pretty much identical code for foobar1 and foobar2 (I'm only showing the main loop, but the rest is also the same):

  20:   48 39 c8                cmp    %rcx,%rax
  23:   74 18                   je     3d <_D2zz7foobar1FNaNbNfmZAi+0x3d>
  25:   c7 04 8a 7b 00 00 00    movl   $0x7b,(%rdx,%rcx,4)
  2c:   48 83 c1 01             add    $0x1,%rcx
  30:   48 39 cb                cmp    %rcx,%rbx
  33:   75 eb                   jne    20 <_D2zz7foobar1FNaNbNfmZAi+0x20>
  20:   48 39 c8                cmp    %rcx,%rax
  23:   74 18                   je     3d <_D2zz7foobar2FNaNbNfmZAi+0x3d>
  25:   c7 04 8a 7b 00 00 00    movl   $0x7b,(%rdx,%rcx,4)
  2c:   48 83 c1 01             add    $0x1,%rcx
  30:   48 39 cb                cmp    %rcx,%rbx
  33:   75 eb                   jne    20 <_D2zz7foobar2FNaNbNfmZAi+0x20>

Do you have a better example to demonstrate generate's superiority?

Try actual work instead of returning a literal :) Like e.g. calling dice(50, 50).

One thing to note though - generate will always eagerly call its function at least once. Which of course should also be considered in choosing the desired implementation. I.e. if your n comes from user and is allowed to be 0, then generate becomes an inferior choice.