www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - passing a variadic parameter to randomSample

reply forkit <forkit gmail.com> writes:
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") );
}

// --
Jan 25 2022
next sibling parent reply vit <vit vit.vit> writes:
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: ```d 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") ); } ```
Jan 25 2022
parent reply forkit <forkit gmail.com> writes:
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) 
);
}

//--
Jan 25 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Jan 25 2022
next sibling parent reply forkit <forkit gmail.com> writes:
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
}

// --
Jan 25 2022
parent reply forkit <forkit gmail.com> writes:
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.
Jan 25 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 25 2022
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jan 25 2022
prev sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Tuesday, 25 January 2022 at 22:07:43 UTC, Ali Çehreli wrote:
 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").
Huh? Perhaps you meant "for ("hello") vs. ("hello", "world")"? Because it would be the same instantiation for a single string argument :)
Jan 26 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/26/22 02:20, Stanislav Blinov wrote:
 On Tuesday, 25 January 2022 at 22:07:43 UTC, Ali Çehreli wrote:
 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").
Huh? Perhaps you meant "for ("hello") vs. ("hello", "world")"? Because it would be the same instantiation for a single string argument :)
You're right. I mistakenly assumed it would be the same as RandomChoice(string a, string b)(). But still, as you show, the instantiations could be too many for R... Ali
Jan 26 2022
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/26/22 07:44, Ali Çehreli wrote:

 the instantiations could be too many for R...
I am still wrong there. It is inconceivable to instantiate the following template for the same type (e.g. string) from "too many" places in a program: auto RandomChoice(R...)(R r) { // ... } There may be a few instantiations that call it with 1, 2, 4... maybe 10 arguments? But that would be it... Oh! Unless there is some code generation. :) Ali
Jan 26 2022
prev sibling next sibling parent WebFreak001 <d.forum webfreak.org> writes:
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: ```d 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: ```d 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: ```d 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); } } ```
Jan 25 2022
prev sibling parent WebFreak001 <d.forum webfreak.org> writes:
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: ```d 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: ```d 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: ```d 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); } } ```
Jan 25 2022