Jump to page: 1 2
Thread overview
passing a variadic parameter to randomSample
Jan 25, 2022
forkit
Jan 25, 2022
vit
Jan 25, 2022
forkit
Jan 25, 2022
Ali Çehreli
Jan 25, 2022
forkit
Jan 25, 2022
forkit
Jan 25, 2022
H. S. Teoh
Jan 25, 2022
H. S. Teoh
Jan 26, 2022
Stanislav Blinov
Jan 26, 2022
Ali Çehreli
Jan 26, 2022
Ali Çehreli
Jan 25, 2022
WebFreak001
Jan 25, 2022
WebFreak001
January 25, 2022
so I'm trying to write (or rather learn how to write) a 'variadic template function', that returns just one of its variadic parameter, randomly chosen.

But can't get my head around the problem here :-(

.. Error: template `std.random.randomSample` cannot deduce function from argument types `

// --

module test;
import std;

string RandomChoice(R...)(R r)
{
    auto rnd = MinstdRand0(42);
    return r.randomSample(1, rnd).to!string;
}

void main()
{
    writeln( RandomChoice("typeA", "typeB", "typeC") );
}

// --
January 25, 2022

On Tuesday, 25 January 2022 at 09:48:25 UTC, forkit wrote:

>

so I'm trying to write (or rather learn how to write) a 'variadic template function', that returns just one of its variadic parameter, randomly chosen.

But can't get my head around the problem here :-(

.. Error: template std.random.randomSample cannot deduce function from argument types `

// --

module test;
import std;

string RandomChoice(R...)(R r)
{
auto rnd = MinstdRand0(42);
return r.randomSample(1, rnd).to!string;
}

void main()
{
writeln( RandomChoice("typeA", "typeB", "typeC") );
}

// --

r is not input range, try this:

module test;
import std;


string RandomChoice1(R...)(R r)
{
    auto rnd = MinstdRand0(unpredictableSeed);
    return only(r).randomSample(1, rnd).front;
}

string RandomChoice2(R...)(R r)@nogc
{
	auto rnd = MinstdRand0(unpredictableSeed);

    switch(rnd.front % R.length){
        static foreach(enum I, alias arg; r){
        	case I:
            	return arg;
        }

        default:
        	assert(0, "no impl");
    }
}

void main()
{
    writeln( RandomChoice1("typeA", "typeB", "typeC") );
    writeln( RandomChoice2("typeA", "typeB", "typeC") );
}
January 25, 2022

On Tuesday, 25 January 2022 at 09:48:25 UTC, forkit wrote:

>

so I'm trying to write (or rather learn how to write) a 'variadic template function', that returns just one of its variadic parameter, randomly chosen.

But can't get my head around the problem here :-(

.. Error: template std.random.randomSample cannot deduce function from argument types `

// --

module test;
import std;

string RandomChoice(R...)(R r)
{
auto rnd = MinstdRand0(42);
return r.randomSample(1, rnd).to!string;
}

void main()
{
writeln( RandomChoice("typeA", "typeB", "typeC") );
}

// --

With R... each value could be of different type, so passing RandomChoice("typeA", 4) would break the current code.

I think there are 2 different ways that can solve this elegantly:

  1. restrict the parameters to all be the same parameter type:

    string RandomChoice(T)(T[] r...)
    {
        auto rnd = MinstdRand0(42);
        return r.randomSample(1, rnd).to!string;
    }
    

    and code compiles with no changes. Instead of using .to!string you could also return the parameter type itself if you want to get the value itself:

    T RandomChoice(T)(T[] r...)
    {
        auto rnd = MinstdRand0(42);
        return r.randomSample(1, rnd).front;
    }
    
  2. generate a random number between 0 and r.length, add a switch case and dynamically generate a case for each number (static foreach) and return the processed value using .to!string:

    string RandomChoice(R...)(R r)
    {
        auto rnd = MinstdRand0(42);
        switch (uniform(0, R.length, rnd))
        {
        	static foreach (i, value; r)
            {
                case i: // this code is duplicated for each parameter
                	// use this if you want to support different argument types
                	return value.to!string;
            }
            default: assert(false);
        }
    }
    
January 25, 2022

On Tuesday, 25 January 2022 at 09:48:25 UTC, forkit wrote:

>

so I'm trying to write (or rather learn how to write) a 'variadic template function', that returns just one of its variadic parameter, randomly chosen.

But can't get my head around the problem here :-(

.. Error: template std.random.randomSample cannot deduce function from argument types `

// --

module test;
import std;

string RandomChoice(R...)(R r)
{
auto rnd = MinstdRand0(42);
return r.randomSample(1, rnd).to!string;
}

void main()
{
writeln( RandomChoice("typeA", "typeB", "typeC") );
}

// --

On Tuesday, 25 January 2022 at 09:48:25 UTC, forkit wrote:

>

so I'm trying to write (or rather learn how to write) a 'variadic template function', that returns just one of its variadic parameter, randomly chosen.

But can't get my head around the problem here :-(

.. Error: template std.random.randomSample cannot deduce function from argument types `

// --

module test;
import std;

string RandomChoice(R...)(R r)
{
auto rnd = MinstdRand0(42);
return r.randomSample(1, rnd).to!string;
}

void main()
{
writeln( RandomChoice("typeA", "typeB", "typeC") );
}

// --

With R... each value could be of different type, so passing RandomChoice("typeA", 4) would break the current code.

I think there are 2 different ways that can solve this elegantly:

  1. restrict the parameters to all be the same parameter type:

    string RandomChoice(T)(T[] r...)
    {
        auto rnd = MinstdRand0(42);
        return r.randomSample(1, rnd).to!string;
    }
    

    and code compiles with no changes. Instead of using .to!string you could also return the parameter type itself if you want to get the value itself:

    T RandomChoice(T)(T[] r...)
    {
        auto rnd = MinstdRand0(42);
        return r.randomSample(1, rnd).front;
    }
    
  2. generate a random number between 0 and r.length, add a switch case and dynamically generate a case for each number (static foreach) and return the processed value using .to!string:

    string RandomChoice(R...)(R r)
    {
        auto rnd = MinstdRand0(42);
        switch (uniform(0, R.length, rnd))
        {
        	static foreach (i, value; r)
            {
                case i: // this code is duplicated for each parameter
                	// use this if you want to support different argument types
                	return value.to!string;
            }
            default: assert(false);
        }
    }
    
January 25, 2022
On Tuesday, 25 January 2022 at 11:50:08 UTC, vit wrote:
>

thanks. problem solved (providing all parameters are of the same type).

// ---

module test;
import std;

auto RandomChoice(R...)(R r)
{
    auto rnd = MinstdRand0(unpredictableSeed);
    return only(r).randomSample(1, rnd).front;
}

void main()
{
    writeln( RandomChoice("typeA", "typeB", "typeC") );
    writeln( RandomChoice(5, 8, 2) );
    writeln( RandomChoice(100.05, 110.8, 109.54) );

    //writeln( RandomChoice("typeA", 5, 100.14) ); // nope. they all need to be of the same type.
    writeln( RandomChoice("typeA", 5.to!string, 100.14.to!string) );
}

//--
January 25, 2022
On 1/25/22 13:55, forkit wrote:

> auto RandomChoice(R...)(R r)

Watch out though: The compiler will compile a different function per set of values. For example, there will be separate RandomChoice instances for ("hello") vs. ("world").

D has a simple variadic parameter syntax as well:

auto RandomChoice(R)(R[] r...)

> {
>      auto rnd = MinstdRand0(unpredictableSeed);
>      return only(r).randomSample(1, rnd).front;

Which makes that simpler as well because being a slice, r is already a range. And there is choice():

    return r.choice(rnd);

Something is very important though: The 'r' slice is short-lived; it does not live in dynamic memory. RandomChoice() should not save it for later use nor return it. (The compiler may have protection against that; I am not sure.)

Ali

January 25, 2022
On Tuesday, 25 January 2022 at 22:07:43 UTC, Ali Çehreli wrote:
>

thanks. makes it even shorter and simpler :-)

// --

module test;
@safe:

import std;

auto RandomChoice(R...)(R r)
{
    auto rnd = MinstdRand0(unpredictableSeed);
    return only(r).choice(rnd);
}

void main()
{
    writeln( RandomChoice("typeA", "typeB", "typeC") );
    writeln( RandomChoice(5, 8, 2) );
    writeln( RandomChoice(1.3, 5.09, 8, 2) );
    writeln( RandomChoice(100.05, 110.8, 109.54) );

    //writeln( RandomChoice("typeA", 5, 100.14) );
    // nope. some issue with mixing strings with numeric types

    writeln( RandomChoice("typeA", 5.to!string, 100.14.to!string) );
    // NOTE: This registers with -profile=gc
}

// --


January 25, 2022
On Tue, Jan 25, 2022 at 02:07:43PM -0800, Ali Çehreli via Digitalmars-d-learn wrote: [...]
> auto RandomChoice(R)(R[] r...)
> 
> > {
> >      auto rnd = MinstdRand0(unpredictableSeed);
> >      return only(r).randomSample(1, rnd).front;
> 
> Which makes that simpler as well because being a slice, r is already a
> range. And there is choice():
> 
>     return r.choice(rnd);
> 
> Something is very important though: The 'r' slice is short-lived; it does not live in dynamic memory. RandomChoice() should not save it for later use nor return it. (The compiler may have protection against that; I am not sure.)

I think with -dip1000 the compiler should refuse to compile the code if you try to save a slice of r past the function body.


T

-- 
Philosophy: how to make a career out of daydreaming.
January 25, 2022
On Tuesday, 25 January 2022 at 22:35:29 UTC, forkit wrote:
>

I should point out (to anyone looking at that code I posted), that it's easier, and makes more sense, to just write:

writeln( ["typeA", "typeB", "typeC"].choice );

... but my main focus here, was learning about variadic template functions.

January 25, 2022
On Tue, Jan 25, 2022 at 10:48:26PM +0000, forkit via Digitalmars-d-learn wrote: [...]
> ... but my main focus here, was learning about variadic template functions.

D has several flavors of variadics:

1) C-style variadics (not type-safe, not recommended):

	int func(int firstArgc, ...)

2) D-style type-safe variadics (non-templated):

	int func(int[] args...)

   All arguments must be of the same type, and the function receives
   them as an array of that type, so .length can be used to ensure you
   don't overrun the array.

3) Variadic template functions:

	int func(Args...)(Args args)

   Arguments can be of any type, the type can be obtained with Args[i].
   (Not to be confused with lowercase args[i], which gives you the
   argument value itself.)  The most flexible of the lot, but may also
   lead to template bloat if used excessively (one instantiation is
   generated for every different combination of argument types).

There's also the lazy variant of (2) with delegates, but I've never used
them before.


T

-- 
An elephant: A mouse built to government specifications. -- Robert Heinlein
« First   ‹ Prev
1 2