challenge: implement the max function

Andrei Alexandrescu (See Website For Email) SeeWebsiteForEmail at erdani.org
Sun Jan 21 18:25:50 PST 2007


Frits van Bommel wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> Here's a simple challenge: implement the max function. Requirements:
>>
>> a) generic
>> b) efficient
>> c) preserve lvalueness when possible such that one can write e.g.
>>
>> max(arr[0], arr[1]) *= 0.9;
>>
>> d) should accept two or more arguments
>> e) should return the "smartest" type, e.g. max of an unsigned int and 
>> unsigned long should return unsigned long
>> f) short and easy to understand
>>
>> I don't think it's possible to implement the function to the spec in 
>> current D, so designs are allowed to invent new features, as long as 
>> they define them thoroughly.
> 
> 
> Some first thoughts:
> 
> Presumably, (b) is only required when using full optimization options?

That is correct.

> I was going to ask "What is the 'smartest type' for the max of long and 
> ulong?" but then I thought of the answer: ulong. Since the ulong is 
> non-negative, the max of a long an an ulong will always be non-negative 
> itself.

Exactly right. max makes for a great example in type manipulation 
abilities because it's simple yet gives the type system a good workout.

> (c) & (e) are exclude each other in current D since return values are 
> always rvalues, and a forwarding struct goes against requirement (e).

That's why you are kindly invited to invent the feature(s) that you find 
fit to comply to the spec.

> C++-like references won't work even if added, since the return type 
> needs to be determined at compile time. This means you can't return an 
> ulong& for max(uint, ulong) since if the uint happens to be larger you 
> can't create the ulong& return value from the uint parameter.

True. But read (c) again: there is a "where possible". When the types 
are different, you always return an rvalue. It's not possible to return 
an lvalue, because you don't know statically which argument is larger.

> Adding implicit casts and bending requirement (e) a little to return a 
> forwarding struct that implicitly converts to the wanted type is another 
> option.
> This will probably breaks requirement (f) because of too much required 
> operator overloading.
> Also, you'll need to perform runtime checks for each operation if there 
> were multiple types and you want to preserve lvalue-ness. This will 
> likely break requirement (b).
> That last objection could perhaps be mitigated by using a forwarding 
> class instead of a struct though; runtime checks could then be replaced 
> by virtual function calls. If that's efficient enough (b) is preserved.

This reasoning strays a bit away from the spec because it wants to do 
(c) too often.

> Overall, I don't think this is possible in any statically typed 
> programming language I know. I don't think even a C preprocessor macro 
> can fulfill both (c) and (e) unless that "when possible" in (c) means 
> "when possible in the language" instead of "when passed lvalues".

Heck, I did it in C++. It takes some hundred plus lines of code though 
:o). The obvious purpose of my challenge is to find what it takes to 
make it a trivial few-liner in D.


Andrei



More information about the Digitalmars-d mailing list