views:

812

answers:

4

Functions in Python can be called using keyword arguments of the form keyword = value. For instance, the following function:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"

could be called in any of the following ways:

parrot(1000)
    parrot(action = 'VOOOOOM', voltage = 1000000)
    parrot('a thousand', state = 'pushing up the daisies')
    parrot('a million', 'bereft of life', 'jump')

ActionScript 3 does not have this facility. How would I best emulate it?

A: 

You could implement something similar using variable number of arguments to a function shown here.

Brandon
Very interesting. You should add the code snippet from the site to your answer.
Soviut
But this is just a parameter array. This does not give you named parameters, or allow you to change to order of parameter, exclude parameters, etc.
bzlm
+5  A: 

It won't throw any errors at compile time if you mess up but you could do something like this:

public function awesomefunction(input:Object):void {
    if (input.foo) trace("got foo");
    if (input.bar) trace("got bar");
}

And then when calling it doing like this:

awesomefunction( { foo : 23, bar : 99 } );

The {} brackets create an inline object. A long winded version of the above code can be written like this:

var obj:Object = new Object();
obj.foo = 23;
obj.bar = 99;
awesomefunction( obj );

If you give it an array instead:

awesomefunction( [23, 99 ] );

( [] is shorthand for an array btw )

The indices will be accesible like this:

public function awesomefunction(input:Object):void {
    if (input[0]) trace("got foo");
    if (input[1]) trace("got bar");
}

So to bring it all toghether you can use the fact that the || operator returns the value of the object that first evaluates to true, not just true to do:

public function awesomefunction(input:Object):void {
 var foo:int = input.foo || input[0];
 var bar:int = input.bar || input[1];
 if (foo) trace("got foo:", foo);
 if (bar) trace("got bar:", bar);
}

So, the above function will give the same output for all these three calls:

awesomefunction( { foo : 23, bar :55 } );
awesomefunction( { bar : 55, foo : 23 } );
awesomefunction( [ 23, 55 ] );

This will however not allow you to mix named and numbered, but you can do like this:

awesomefunction( { foo : 23, 1 : 55 } );

It's not very pretty, but it works!

grapefrukt
A: 

There is a special type in actionscript 3.0 called ...(rest). Put this type in the parameter list for a function and then that function is able to accept any number of arguments. The proper sytanx is: ...arrayName, where arrayName is the name of the array which will hold the arguments.

public function forYou( ...args ) : void{
   for(var i : int = 0; i 
ForYourOwnGood
But this is just a parameter array. This does not give you named parameters, or allow you to change to order of parameter, exclude paramters, etc.
bzlm
+4  A: 

The short answer is that in Actionscript, the order of parameters is part of a function's signature. To get around that fact, you either have to do extra work, or indulge in bad coding practices that will bite you later. I'd say you have two "good" options for getting flexibility similar to what you're looking for. The formal way would be to define a data class to act as the function's parameter. That is:

public class ComplaintData {
    public var state:String = "a stiff";
    public var action:String = "voom";
    public var type:String = "Norwegian Blue";
    public var voltage:String;

    public function ComplaintData( v:String ) {
        voltage = v;
    }
}

// somewhere else....

function parrot( cd:ComplaintData ) {
    trace "-- This parrot wouldn't " + cd.action;
    // etc.
}

var cd:ComplaintData = new ComplaintData( "a million" );
cd.action = "jump";
parrot( cd );

If the situation is more ad-hoc, a less formal approach is to make liberal use of optional parameters, and allow the caller to pass null for any non-trailing parameter they want to omit. This gets you the flexibility, but you sacrifice a fair deal of cleanliness:

function parrot( voltage:String, 
                 state:String=null, 
                 action:String=null, 
                 type:String=null)  {
    if (state==null) { state = 'a stiff'; }
    if (action==null) { action = 'voom'; }
    if (type==null) { type = 'Norwegian Blue'; }
    // ....
}
parrot('1000')
parrot('1000000', null, 'jump')

But if both these options seem unwieldy, that's because they are. In the long run, I think it's ultimately best to internalize the idea that ordered parameter lists are part of Actionscript's grammar, and you're probably better of not trying to avoid them. Doing so either sacrifices simplicity (as above), or leads to poor practices (like using unstructured objects for parameters, as suggested in other answers, which sacrifices many benefits of type- and error-checking).

The most balanced way to work with the grammar is simply use optional parameters whenever possible, thus:

function parrot( voltage:String, 
                 state:String='a stiff', 
                 action:String='voom', 
                 type:String='Norwegian Blue')  {
    // ...
}
parrot('1000');
parrot('1000000', 'pushing up the daisies');
parrot('a million', 'bereft of life', 'jump')

You still have to live with the fact that only trailing parameters can be omitted, but that's how the language is made (and how its native methods are defined and used).

fenomas
+1 to using a typed object or just dealing with it. Going with ... or an anonymous object will just make your codebase more difficult to maintain.
Richard Szalay