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
Mittman
Level III

programming functionals (or functions that generate functions)

I have a problem where the solution I want is to use a function to generate a different function. Here's a toy example. I expect it to return [3,4,5]. it doesn't work as I hoped because apparently the passed argument (offset) isn't available to the anonymous function that is returned. My guess is that there is a way to do this with substitution, but I couldn't figure it out.

 

funWrapper = Function({array, fun},
	fun(array);
);

funFunctional = Function({offset},
	Function({x},
		x + offset;
	);
);

funWrapper([1,2,3], funFUnctional(2));
2 ACCEPTED SOLUTIONS

Accepted Solutions
Mittman
Level III

Re: programming functionals (or functions that generate functions)

Thanks for sharing these insightful vignettes, Craige.

 

Here are solutions using both approaches. Classes feel more intuitive to me (and "this" is certainly easier to type than namespace(expr(ns<<get name))).

Tested both in JMP 14 Pro and JMP 12 Pro. Classes aren't available in JMP 12 Pro.

 

funOffsetAdder = Function({offset},
	ns = NewNamespace();
	ns:offset = offset;
	ns:theMethod = EvalExpr(
		Function({x},
			this = namespace(expr(ns<<getname));
			x + this:offset;
		)
	);
	ns;
);

add10 = funOffsetAdder(2);
show(add10:theMethod([1,2,3]));

Define Class("offsetAdder",
	offset=.;
	_init_ = Method({offset},
		this:offset = offset;
	);
	theMethod = Method({input},
		this:offset + input;
	);
);

add10v2 = New Object(offsetAdder(2));
show(add10v2:theMethod([1,2,3]));

View solution in original post

ErraticAttack
Level VI

Re: programming functionals (or functions that generate functions)

Please note that JMP14 classes are not fully functional and you might be burned if you rely on them too heavily; however, in JMP12+ you can create classes (including inheritance, constructors, destructors, etc.) with namespaces and some meta-programming.

 

To answer the OP's initial question -- you can achieve your goal with only minimal edits to your script -- just note that the return value from a JSL function is evaluated (with the Eval() statement) before being returned.  Thus, if your last statement is an unevaluated expression (such as an Expr( Function( _definition_ ) ), then it will return the function as intended.  Only a slight trick in the second function is needed to be able to call the function (the "fun = fun" statement).

 

funWrapper = Function({array, fun},
	fun = fun;
	fun(array);
);

funFunctional = Function({offset},
	Substitute(
		Expr(
			Function({x},
				x + _offset_;
			)
		)
	,
		Expr( _offset_ ), offset
	)
);

funWrapper([1,2,3], funFUnctional(2));
Jordan

View solution in original post

8 REPLIES 8
Craige_Hales
Super User

Re: programming functionals (or functions that generate functions)

I did some experiments: How to use Define Class  and Functional programming using JSL objects 

Post back if you figure out more of the puzzle; at this point I'm not using the JSL classes much, but they may be the best way to do what you want. Namespaces may be equivalent, without some of the sub-classing ideas. Either one will let you package a function and data together, and pass the package to another function by reference.

Craige
Mittman
Level III

Re: programming functionals (or functions that generate functions)

Thanks for sharing these insightful vignettes, Craige.

 

Here are solutions using both approaches. Classes feel more intuitive to me (and "this" is certainly easier to type than namespace(expr(ns<<get name))).

Tested both in JMP 14 Pro and JMP 12 Pro. Classes aren't available in JMP 12 Pro.

 

funOffsetAdder = Function({offset},
	ns = NewNamespace();
	ns:offset = offset;
	ns:theMethod = EvalExpr(
		Function({x},
			this = namespace(expr(ns<<getname));
			x + this:offset;
		)
	);
	ns;
);

add10 = funOffsetAdder(2);
show(add10:theMethod([1,2,3]));

Define Class("offsetAdder",
	offset=.;
	_init_ = Method({offset},
		this:offset = offset;
	);
	theMethod = Method({input},
		this:offset + input;
	);
);

add10v2 = New Object(offsetAdder(2));
show(add10v2:theMethod([1,2,3]));
SDF1
Super User

Re: programming functionals (or functions that generate functions)

Hi @Mittman ,

 

  Not sure if this is exactly what you want, but it works to solve at least the example you give in your original post.

Names Default to Here (1);

fWrap = Function({array, offset}, fFun(array offset));

fFun = Function({x, offset}, x + offset);

fWrap([3, 4, 5, 6], 5 );

  This will return [8, 9, 10, 11].

  

  It's an interesting issue to look at. If you can share why you would need to do it this way, I'm quite curious.

 

Hope this helps!,

DS

Mittman
Level III

Re: programming functionals (or functions that generate functions)

Hi @SDF1 , thanks for the reply. This doesn't work for my real use case, however. I want to be able to pass an arbitrary (monotonic) function as an argument to a function that finds the zero. This is straightforward enough if I explicitly define each function, but I ran into trouble when I wanted to write a function for creating an instance of a function from a parameterized class. I grew up in R, which is not really OO, but where the scoping rules allow code like what I had in the original post.

 

- EM

SDF1
Super User

Re: programming functionals (or functions that generate functions)

Hi @Mittman ,

 

  It sounds like you're trying to solve for the roots of an equation (function in this case, not JSL Function()), is that correct?

 

  There is a built-in JSL function called Minimize() that you can pass your function to with starting points for the Minimize() function to begin it's optimization routine. This might be an easier path to go down than writing your own JSL Function() for that. You can read a blog post about it here.

 

Hope that helps!,

DS

Mittman
Level III

Re: programming functionals (or functions that generate functions)

@SDF1 , I'm trying to find the value of the parameter where the function is zero, similar to uniroot function in R. I'm not aware of a similar JMP function. In any case, I stole the code to do that, so it was easy enough

ErraticAttack
Level VI

Re: programming functionals (or functions that generate functions)

Please note that JMP14 classes are not fully functional and you might be burned if you rely on them too heavily; however, in JMP12+ you can create classes (including inheritance, constructors, destructors, etc.) with namespaces and some meta-programming.

 

To answer the OP's initial question -- you can achieve your goal with only minimal edits to your script -- just note that the return value from a JSL function is evaluated (with the Eval() statement) before being returned.  Thus, if your last statement is an unevaluated expression (such as an Expr( Function( _definition_ ) ), then it will return the function as intended.  Only a slight trick in the second function is needed to be able to call the function (the "fun = fun" statement).

 

funWrapper = Function({array, fun},
	fun = fun;
	fun(array);
);

funFunctional = Function({offset},
	Substitute(
		Expr(
			Function({x},
				x + _offset_;
			)
		)
	,
		Expr( _offset_ ), offset
	)
);

funWrapper([1,2,3], funFUnctional(2));
Jordan
ErraticAttack
Level VI

Re: programming functionals (or functions that generate functions)

Sorry to double-post here, but there is actually a better way to do this, which only requires modification of the functional:

 

funWrapper = Function({array, fun},
	fun(array);
);

funFunctional = Function({offset},
	Eval( Substitute(
		Expr(
			Function({x},
				x + _offset_;
			)
		)
	,
		Expr( _offset_ ), offset
	) )
);

funWrapper([1,2,3], funFUnctional(2));
Jordan