www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Sellecting randomly from a range

reply Jesse Phillips <JesseKPhillips gmail.com> writes:
This is more a tutorial which I'll put up on a wiki, but one question I have is
why can't 0..8 work in a randomCover, and wouldn't it be nice to use regex for
creating a range ("[A-Z]" being all uppercase letters)?

Selecting randomly from a collection of elements is common and usually entails
selecting random numbers and making sure they haven't been used before. D2 has
several templated functions that when put together make this very easy.

The first thing to look at comes from std.random, randomCover(range, rnd). This
takes the range of items and creates a new range that is a random
representation of the elements.

Then found in std.range is take(count, range). This will give us the count
items from our range giving us yet another range. From here we are able to use
a foreach loop to grab each item, but what if we want to store this someplace
for latter use?

This brings us to std.algorithms where we find fill(range1, range2). It is
important to note that D arrays can be use as a range.

If we combine this with D's array slices you are able to fill the results from
two ranges. The code below is an example using all of this and selecting a
random number of elements from each list.

(If this ends up being poorly formatted I apologize
http://paste.dprogramming.com/dpi1a1wn )

import std.algorithm;
import std.random;
import std.range;
import std.stdio;

string[] list =  ["one", "two", "three", "four", "five"];
string[] list2 = ["1", "2", "3", "4", "5"];

void main(string[] args) {
   auto rnd = new Random(unpredictableSeed);

   int count = uniform(1, list.length, rnd);

   string[] result = new string[count*2];

   fill(result[0..count], take(count, randomCover(list, rnd)));
   fill(result[count..$], take(count, randomCover(list2, rnd)));

   writeln(result);
}
May 17 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jesse Phillips:

 import std.algorithm;
 import std.random;
 import std.range;
 import std.stdio;
 
 string[] list =  ["one", "two", "three", "four", "five"];
 string[] list2 = ["1", "2", "3", "4", "5"];
 
 void main(string[] args) {
    auto rnd = new Random(unpredictableSeed);
 
    int count = uniform(1, list.length, rnd);
 
    string[] result = new string[count*2];
 
    fill(result[0..count], take(count, randomCover(list, rnd)));
    fill(result[count..$], take(count, randomCover(list2, rnd)));
 
    writeln(result);
 }
It's essential to compare such code with other ways/languages to produce a similar output: Something similar in Python: from random import randint, choice list1 = ["one", "two", "three", "four", "five"] list2 = ["1", "2", "3", "4", "5"] count = randint(1, len(list1)) result = [choice(list1) for _ in xrange(count)] result.extend(choice(list2) for _ in xrange(count)) print result In D1 with my dlibs: import d.string: putr; import d.random: choice, randInt; import d.func: table; void main() { string[] list1 = ["one", "two", "three", "four", "five"]; string[] list2 = ["1", "2", "3", "4", "5"]; int count = randInt(1, list1.length); auto result = table(choice(list1), count) ~ table(choice(list2), count); putr(result); } There are ways to do something similar without array concat too, and to create a lazy result too: import d.string: putr; import d.random: choice, randInt; import d.func: xmap, array, xrange; void main() { string[] list1 = ["one", "two", "three", "four", "five"]; string[] list2 = ["1", "2", "3", "4", "5"]; int count = randInt(1, list1.length); auto result = xmap((int i){return choice(i < count ? list1 : list2);}, xrange(count*2)); putr(array(result)); } Or just: import d.string: putr; import d.random: choice, randInt; import d.func: xmap, array, xrange; void main() { string[] list1 = ["one", "two", "three", "four", "five"]; string[] list2 = ["1", "2", "3", "4", "5"]; int count = randInt(1, list1.length); auto result = xmap((int i){return choice(list1);}, xrange(count)) ~ xmap((int i){return choice(list2);}, xrange(count)); putr(array(result)); } I'm waiting for lazy/eager array comps still in D. Bye, bearophile
May 18 2009
prev sibling parent Jesse Phillips <JesseKPhillips gmail.com> writes:
On Sun, 17 May 2009 18:08:26 -0400, Jesse Phillips wrote:

 This is more a tutorial which I'll put up on a wiki, but one question I
 have is why can't 0..8 work in a randomCover, and wouldn't it be nice to
 use regex for creating a range ("[A-Z]" being all uppercase letters)?
 
 Selecting randomly from a collection of elements is common and usually
 entails selecting random numbers and making sure they haven't been used
 before. D2 has several templated functions that when put together make
 this very easy.
 
 The first thing to look at comes from std.random, randomCover(range,
 rnd). This takes the range of items and creates a new range that is a
 random representation of the elements.
 
 Then found in std.range is take(count, range). This will give us the
 count items from our range giving us yet another range. From here we are
 able to use a foreach loop to grab each item, but what if we want to
 store this someplace for latter use?
 
 This brings us to std.algorithms where we find fill(range1, range2). It
 is important to note that D arrays can be use as a range.
 
 If we combine this with D's array slices you are able to fill the
 results from two ranges. The code below is an example using all of this
 and selecting a random number of elements from each list.
 
 (If this ends up being poorly formatted I apologize
 http://paste.dprogramming.com/dpi1a1wn )
 
 import std.algorithm;
 import std.random;
 import std.range;
 import std.stdio;
 
 string[] list =  ["one", "two", "three", "four", "five"]; string[] list2
 = ["1", "2", "3", "4", "5"];
 
 void main(string[] args) {
    auto rnd = new Random(unpredictableSeed);
 
    int count = uniform(1, list.length, rnd);
 
    string[] result = new string[count*2];
 
    fill(result[0..count], take(count, randomCover(list, rnd)));
    fill(result[count..$], take(count, randomCover(list2, rnd)));
 
    writeln(result);
 }
Actually I've some unneeded code, there was no need for using take. fill(result[0..count], randomCover(list, rnd)); fill(result[count..$], randomCover(list2, rnd));
May 18 2009