cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
Choose Language Hide Translation Bar
mat-ski
Level III

Is it possible to pass a reference to a Function?

For example when I run this script:

 

someFunc = Function( {arg},
	{},
	
	Print( arg );
);

executor = Function( {fn, arg},
	{},
	
	fn( arg );
);

executor( someFunc, "this string" )

I get an error, that seems to imply that I am calling someFunc when I am intending to pass a reference to it to executor.

 

I am able to pass a reference by defining the function in the invocation, e.g.

executor = Function( {fn, arg},
	{},
	
	Print(arg);
    fn();
);

executor( Function({}, {}, Print("anonymous function"); ), "wut" );

Outputs:

"wut"
"anonymous function"


Which accomplishes what I need, but it would be convenient to be able to pass functions using their variable names.

5 REPLIES 5
Craige_Hales
Super User

Re: Is it possible to pass a reference to a Function?

There is no simple way to pass by reference, but you can encapsulate functions and data in classes or namespaces and pass the object pointer or namespace name. Take a look at the picture at the top of Functional programming using JSL objects  to get an idea how that might work for classes.

 

Craige
ErraticAttack
Level VI

Re: Is it possible to pass a reference to a Function?

I use something like this a lot, and from my experience, using the NameExpr() function works quite well.  It can be tricky when returning a function from another function, and @Craige_Hales method is usually a better choice.

 

I have extensive background in Qt and really appreciate their signals / slots -- I created something quite similar in JSL and it is necessary to shuttle functions around by reference and not so practical to do it with namespace / class wrappers, so I use something very similar to this.

 

someFunc = Function( {arg},
	{},
	Print( "Within One Function" );
	Print( arg );
);

executor = Function( {fn, arg},
	{},
	Print( "Executing..." );
	fn( arg );
);

executor( Name Expr( someFunc ), "this string" )
Jordan
Craige_Hales
Super User

Re: Is it possible to pass a reference to a Function?

That does look simpler, and easier to understand. There is a tradeoff that will only make a difference for large functions that are called a lot. NameExpr is cloning the expression tree for the function, and passing the copy, not a reference. I don't think there is any way to tell it is a copy vs reference (like you can for arrays that are not modified on return) other than timing it:

loops=1e5; // ~10 seconds/100K loops
print = (loops==1e0);
someFunc = Function( {arg}, {},
	If( print,
		Print( "Within One Function" );
		Print( arg );
	);
	Return( 0 );
	// this creates a stiff penalty for nameexpr  
	// needing to clone this function. These
	// statements are simple and never executed but still
	// must be copied to the nameExpr result by allocating
	// a bunch of memory objects.
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
	0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
);

/////////////////////////////////////
Write( "\!nUsing NameExpr" );
executor = Function( {fn, arg}, {},
	If( print,
		Print( "Executing for NameExpr..." )
	);
	fn( arg );
);
start = HP Time();
For( i = 1, i <= loops, i += 1,
	executor( Name Expr( someFunc ), "this string" )
);
stop = HP Time();

Show( stop - start ); // ~7 seconds

/////////////////////////////////////
Write( "\!nUsing class" );
Define Class(
	"PassByRef",
	mFn = .;
	_init_ = Method( {fn},
		mFn = Name Expr( fn )
	);
);
// grab the identical function into a reference object >>>>> requires JMP 17, a bug was fixed after 16.2
somefuncRef = New Object( PassByRef( Name Expr( someFunc ) ) );

/* for JMP < 17 use this work-around:
somefuncRef = New Object( PassByRef( 0 ) );
somefuncRef:mFn=Name Expr( someFunc );
*/ /********** this also works and looks ... better? somefuncRef = New Object( PassByRef( Function( {arg}, {}, If( print, Print( "Within One Function" ); Print( arg ); ); Return( 0 ); // this creates a stiff penalty for nameexpr // needing to clone this function. These // statements are simple and never executed but still // must be copied to the nameExpr result by allocating // a bunch of memory objects. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; ); )); *********/ executor = Function( {fnRef, arg}, {}, If( print, Print( "Executing for class..." ) ); fnRef:mFn( arg ); ); start = HP Time(); For( i = 1, i <= loops, i += 1, executor( somefuncRef, "this string" ) ); stop = HP Time(); Show( stop - start ); // ~3 seconds

The ~2x performance boost for a large function vanishes as the 0;0;0; dummy statements are removed; for the short functions you might be using in a Qt-like way it won't make a difference.

 

Craige
hogi
Level XII

Re: Is it possible to pass a reference to a Function?

Where can I find further documentation on the PassByRef option?
argh.

Craige_Hales
Super User

Re: Is it possible to pass a reference to a Function?

@hogi  - sorry, I did kind of bury the important bit in the middle of that code! For anyone else,

Define Class(
	"PassByRef",
	mFn = .;
	_init_ = Method( {fn},
		mFn = Name Expr( fn )
	);
);
somefuncRef = New Object( PassByRef( Name Expr( someFunc ) ) );

is making a class definition named PassByRef and an instance of that class named somefuncRef. The instance is a wrapper around mFn, which holds a clone of someFunc. But that clone is reused again and again, without making more clones. The executor makes that call here

executor = Function( {fnRef, arg}, {},
	If( print,
		Print( "Executing for class..." )
	);
	fnRef:mFn( arg );
);
Craige