cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
  • Learn how to build custom Python data connectors and further customize JMP’s Data Connector Framework with the Python Data Connector Demo, available now in the JMP Marketplace!
  • See how to create experiments to support product design and ID useful product features. Register for June 12 webinar, 2pm US Eastern Time.

Discussions

Solve problems, and share tips and tricks with other JMP users.
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 XIII

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

Recommended Articles