ENIGMA Forums
Contributing to ENIGMA => Function Peer Review => Topic started by: fredcobain on July 03, 2011, 04:15:35 PM

Guys, in GM we have a very usefull builtin function called "choose()";
From GM Help: choose(val1,val2,val3,...) Returns one of the arguments choosen randomly. The function can have up to 16 arguments
I'd like to see some suggestions to perform the same results in Enigma.
Thanks
Fred

pigeonhole each of the options, then choose one with random(n). This is a simple but somewhat inefficient way to do it.
A more efficient way to do it would be the same way we plan on implementing max, which also isn't implemented at this point.
max(a) = a
max(a,b) = a > b ? a : b;
max(a,b,c) = max(a,max(b,c));
max(a,b,c,d) = max(a,max(b,max(c,d)))
likewise, instead of doing the > comparator for the secondary base case, you'd simply do a random() > 0.5 type of thing.

pigeonhole each of the options, then choose one with random(n). This is a simple but somewhat inefficient way to do it.
A more efficient way to do it would be the same way we plan on implementing max, which also isn't implemented at this point.
max(a) = a
max(a,b) = a > b ? a : b;
max(a,b,c) = max(a,max(b,c));
max(a,b,c,d) = max(a,max(b,max(c,d)))
likewise, instead of doing the > comparator for the secondary base case, you'd simply do a random() > 0.5 type of thing.
mmm... interesting...
does the functions round() and random() are is implemented in enigma (built in) ?

ENIGMA will have one shortly. I need to make modifications to the syntax checker to allow functions to accept enigma::varargs.

pigeonhole each of the options, then choose one with random(n). This is a simple but somewhat inefficient way to do it.
A more efficient way to do it would be the same way we plan on implementing max, which also isn't implemented at this point.
max(a) = a
max(a,b) = a > b ? a : b;
max(a,b,c) = max(a,max(b,c));
max(a,b,c,d) = max(a,max(b,max(c,d)))
likewise, instead of doing the > comparator for the secondary base case, you'd simply do a random() > 0.5 type of thing.
I don't get how this is more efficient.

I suppose, yes, pigeonholing, if done right, would be no less efficient than inlines.

Why can't we use stdarg.h? Is this nested max() is really better than this:
double max(int n, ...){
double max, tmp;
va_list ap;
va_start(ap, n);
max = va_arg(ap, double);
for(int i = 2; i <= n_args; i++) {
if((tmp = va_arg(ap, double)) > max)
max = tmp;
}
va_end(ap);
return max;
}
edit: Didn't see this: ENIGMA will have one shortly. I need to make modifications to the syntax checker to allow functions to accept enigma::varargs.
And whats the difference exactly between this and stdarg.h provided functionality? I guess its that Josh's thing uses variants, so I don't need to cast to double or something. So max like this:
max(int 5, double 3.14) would return integer, while this:
max(int 5, double 6) would return a double. Using the simple stdarg would only return doubles or integers.
edit: Also, if doesn't really do what I think it does, then can we finally implement them with stdarg? Its like 10min of work to implement max, min, average, median and so on. Thous are some basic functions I really need.

For starters, HaRRi, stdargs.h will kill the program if you pass it a var or a variant.
Also, good luck getting the argument count.

stdargs.h will kill the program if you pass it a var or a variant.
Yeah, that is what I meant. :D
Also, good luck getting the argument count.
Indeed, though if you made compiler parse this:
max(10,25,34.2);
into this:max(4,10,25,34.2);
where the first argument is the argument number, then stdargs.h could be used. You already parse that string into this:max(varargs(), (varargs(), 10, 25, 34.2));
, so it seems that you can do it (and arguments are counted anyway for error reporting purposes, but I guess GCC is the one that does it).
Also, it seems that you have implemented the max function, but its chaotic in that definitions in mathc.h and .cpp are not used, but required. So if I delete max(double x,double y){ return fmax(x,y); }, I will get an error, while the function itself isn't called. Also, it will take only 3 arguments if its written like that. I still need to change it into:
double max(double numbers, ...){}
to take any number of arguments and still work.
edit: Also, if max(10,25,34.2) is called then it will return 34, not 34.2.
edit2: And I would want to study your method on how you implemented max, so I can add median and others.
edit3: Also, if it seems I'm just mumbling then ignore me. I'm in a habit of not explaining myself clearly.

max(varargs(), (varargs(), 10, 25, 34.2)); is incorrect; the string should come out as
max((varargs(), 10, 25, 34.2));
But I don't parse that.
#define max(x...) max((varargs(),x))
and #define won't give me the number of arguments passed.
Again, double max(double numbers, ...){} won't work. It will kill the program as soon as the user calls max(somevar,someothervar). That wasn't a joke. It's possible that C++0x offers a functionality to overload an stdargs pass, but I don't know how well it is supported.

is incorrect; the string should come out as
It still works though.
Again, double max(double numbers, ...){} won't work.
That was actually the whole working code. :D I have this in mathnc.h:double max(double n, ...);
And this in mathnc.cpp:#include "stdarg.h"
double max(double n, ...){}
So nothing inside the function. And when I do this in EDL:
draw_text(100,100,string(max(12,329,21,343,1000,2981)));
It correctly draws 2981. And IDE_EDIT_objectfunctionality.h has it parsed like this:
draw_text(100, 100, toString(max(varargs(), (varargs(), 12, 329, 21, 343, 1000, 2981))));
That is why its weird. I don't have any code inside that would actually do the comparing, but it still works. On the other hand if I deleted the code from mathnc.cpp it would trow an error saying that the function max is undefined.

Ah; you've stumbled on a way to make ENIGMA honor max() having unlimited parameters. :P
That's... typically kind of dangerous, but I guess it's as good a hack as any. For the time being, you can implement those functions like this:
variant choose(...);
int choose(const enigma::varargs& args) {
return args.get(rand() % args.argc);
}
#define choose(x...) choose((varargs(), x))
You must declare and implement choose before defining it. In the source, you may be wise to #undef choose, then implement it. DO NOT implement choose(...); it's too segfaultprone. This way, worstcase scenario is link error, not random abort().

It is possible to do choose() with variadic templates, but probably not very efficiently:
template<typename... types>
double choose(types... vals)
{
return choose_impl(random(sizeof...(vals)), vals...);
}
template<typename... types>
double choose_impl(unsigned counter, double head, types... vals)
{
return counter == 0 ? head : choose_impl(counter  1, vals...);
}
Untested, but should work. And requires the std=gnu++0x flag. Also will give very odd errors if you have arguments that can't cast to double.

Bump because I thought of a better idea.
Parse choose(x, y, z...) to choose({x, y, z...}).
And have some code:
template<unsigned n> inline double choose(double vals[n])
{
return vals[random(n)];
}
EDIT: Fix the [tt] tag. :/