I'm wondering if there is some way to unpack a variable length argument list in AS3. Take for example this function:
public function varArgsFunc(amount:int, ...args):Array
{
if (amount == 3)
{
return args
}
else
{
return varArgsFunc(++amount, args)
}
}
If I call this:
var result:Array = varArgsFunc(0, [])
result now contains a nested set of arrays:
[[[[]]]]
The problem here is that the args parameter is treated as an array. So if i pass it onto a function with a variable argument list, it will be treated as a single argument.
In Scala there is the :_* operator that tells the compiler to break apart a list into a list of parameters:
var list:Array = ['A', 'B', 'C']
// now imagine we have this class, but I would like to pass each list element
// in as a separate argument
class Tuple {
public function Tuple(...elements)
{
// code
}
}
// if you do this, the list will become be passed as the first argument
new Tuple(list)
// in scala you can use the :_* operator to expand the list
new Tuple(list :_*)
// so the :_* operator essentially does this
new Tuple(list[0], list[1], list[2])
I would like to know if a technique/operator exists in AS3 to expand an array into an argument list.
The apply() method on all functions lets you pass in the arguments in an array instead of the "normal" way.
package {
import flash.display.Sprite;
public class Foo extends Sprite {
public function Foo() {
var args:Array = ["a", "b", "c"];
// "normal" call
varArgsFunc(args);
// what you wanted:
varArgsFunc.apply(null, args);
}
public function varArgsFunc(...args):Array {
trace("got", args);
}
}
}
As a side note to this answer
The original question was not only passing the "unrolled" ...args using the super but to also pass a regular variable first
public function varArgsFunc(amount:int, ...args):Array
If this was the actual method being called the code would be
public function varArgsFunc(amount:int, ...args):Array
{
super.varArgsFunc.apply(this, [amount].concat(args));
}
Related
I need to convert a function to an object. For example, when I need to use the variable called fn I want to be able to use it as a function fn() or as an object fn.json(). I have code to do it, but I think it's not correct.
package lib.smartic {
// import
import lib.smartic.smartic;
// constructor $
public var fn = function(s):smartic{
return new smartic(s);
};
Function.prototype.json = function (s) {
// call
};}
How can I apply the prototype to my variable fn, not just to the object class?
Actually, Function is an Object... and as said #The_asMan, you shouldn't use prototypes.
Simple example:
var smartic: Smartic = new Smartic("someValue");
trace(smartic.json());
And definition of your Smartic class, without prototypes:
public class Smartic {
private var _value:String;
public function Smartic(value:String) {
_value = value;
}
public function json():String {
return "Some json here";
}
}
I don't know what you want to do exactily but if you want smartic to be a function you can do something like this:
keep the code givent by Nicolas Siver and add this function:
public function smarticFunc(value:String):Smartic
{
return new Smartic(value);
}
so you can use smarticFunc as function or as Smartic as it returns a Smartic.
So according to this discussion there is no constant time method for getting the size of a Dictionary object.
I'm trying to implement a wrapper which adds this functionality. Nothing fancy - just a 'numPairs' property and overridden methods to keep it updated. The problem lies in that the [] operator is used to add key/value pairs, rather than a named method, so I don't know how to override this to keep my counter updated. I could just do something like...
public function addPair(key:*, val:*):void {
this[key] = val;
numPairs++;
}
...but it'd be really nice if I could keep the bracket notation. Does anyone know of a solution?
If you want to keep the bracket notation you can still use the Proxy class for that, wrapping a real dictionary.
Here an implementation using the Proxy class, but here i didn't use a weak dictionary because it can be tricky as the 'key' can be garbaged collected and you will not be aware of that. Of course performance operation (adding, removing, ...) will also be lower than the real dictionary.
here the live test : http://wonderfl.net/c/dstz
import flash.utils.Dictionary;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class MyDict extends Proxy {
private var _size:int = 0;
private var _dict:Dictionary = new Dictionary();
public function get size():int {
return _size;
}
flash_proxy override function getProperty(name:*):* {
return _dict[name];
}
flash_proxy override function setProperty(name:*, value:*):void {
if (!_dict.hasOwnProperty(name))
_size ++;
_dict[name] = value;
}
flash_proxy override function deleteProperty(name:*):Boolean {
if (_dict.hasOwnProperty(name)) {
_size --;
delete _dict[name];
return true;
}
return false;
}
}
var dict:MyDict = new MyDict();
dict[1] = 2;
dict["foo"] = "bar";
trace(dict.size, dict[1], dict["foo"]);
delete dict[1];
trace(dict.size, dict[1], dict["foo"]);
I understand you like that you want to keep actual pair count, and you should also check for undefined/null value passed that would indicate a removal of pair. So, you first check if there is a key in "this", then assign value.
public function addPair(key:String, val:*):void {
if (this[key]) {
// pair exists, updating
this[key]=val;
if (!val) numPairs--;
} else {
// pair does not exist, adding
if (val) {
this[key]=val;
numPairs++;
}
}
}
How do I use an unevaluated (and/or possibly undefined) variable as a parameter for a function? For example:
function myFun(a:int):void {
a = 5;
}
If you are familiar with Mathematica it would be equivalent to:
f[a_Integer]:=a=5
Attributes[f]={HoldAll};
The core idea being that it is the variable's name itself which I want to pass to the function, not the value which is current associated with the variable's name.
You can pass a string.
private function my_fun(name:String):void {
trace(this[name]);
}
Example of use:
public class Main extends Sprite {
public var a:int = 5;
....
public function Main():void {
my_fun("a");
}
According to these guys: get string representation of a variable name in as3 if it's a classe you can get it's name. If it's a local variable you cannot, the reason is probably related with efficience (the name gets lost on compiling phase)
Another way you could solve the problem is to use closures to change evaluation scopes.
public class A {
public static function myFun(setter:Function):void {
setter(5);
}
}
public class B {
function someOtherFunction() {
var val:Number;
A.myFun(function(v:Number):void { val = v; });
trace(val); // 5
}
}
Instead of passing a value, I'm passing a function that is bound in the calling scope that is evaluated in the callee's scope.
what does call(thisArg:*, ... args) first parameter mean?
Assuming f() is defined in a unnamed package as global function, following is the code snippet:
package {
public function f(message:String):void {
trace(message);
trace(this.watchedValue);
}
}
test code as following:
public function test():void {
var obj:Object = {watchedValue:100};
f("invoking f");
f.call(obj, "invoking f by call()");//actual result is undefined, but shouldn't be 100?
}
This param only used in closures and anonymous functions, like
var testFunc:Function = function():void{trace(this.watchedValue)}
EDIT:
in you case it will be
package {
public var f:Function = function(message:String):void {
trace(message);
trace(this.watchedValue);
}
}
EDIT2
first parameter of call will be this in called function. This is the way to call fauction like a method of object.
But when function is method or top level function first parameter of call() will be ignored. To use first param your function must be variable with anonymous function.
As far as I know Function.call() is the same as function(), except the fact you change the scope of this. Normally this referrers to the current class, but it could be another class. \
Your test function looks wrong, it should be obj instead of o
public function test():void {
var obj:Object = {watchedValue:100};
f("invoking f");
f.call(obj, "invoking f by call()");
}
This is a hard question to do, but I'll try to explain.
I have the Class and the parameters of its contructor as an object. What I need to do is a function that returns an instance of this class, passing this parameters to the constructor.
This is the code:
Some random and unmodifiable class:
public Foo {
public function Foo(a:int, b:String) {
// constructor
}
}
And some function (in some another class):
function bar(params:Object):* {
var baz:Foo = new Foo(params.a, params.b);
return baz;
}
What I need to do is make this function generic, without pass params as parameter to Foo constructor because I can't modify it. Something like:
function bar2(clazz:Class, params:Object):* {
var baz:* = new clazz(/*some magic way to transform params in comma separated parameters*/);
return baz;
}
Anyone can help me?
Thanks a lot.
This is called parameterized factory. First I thought about Function.apply, but it doesn't apply to constructors (he-he). So, people are making factories like this:
function create(what:Class, args:Array):* {
switch (args.length) {
case 0: return new what();
case 1: return new what(args[0]);
case 2: return new what(args[0], args[1]);
...
//PROFIT!
}
throw new Error("Need moar cases!");
}
what about using ByteArrayto copy the object ?
function clone(source:Object):* {
var copier:ByteArray = new ByteArray();
copier.writeObject(source);
copier.position = 0;
return(copier.readObject());
}
newObjectCopy = clone(originalObject);
source
If you have the option of not using a constructor, but adding an initialise() function to each class which can be constructed instead, you could use Function.apply - something like in the example below.
public class ThingCreator
{
public static function createTheThing(c:Class, params:Array):Object
{
var the_thing:Object = new c();
the_thing.initialise.apply(the_thing, params);
return the_thing;
}
}
As alxx pointed out above, Function.apply and AS3 reflection in this case does not seem to work with AS3's constructors.