Here is the full story.
This was triggered by the recent request for advice on defadvice.
I had this userfunction (see below) lying around, incomplete for the same
reason I cannot complete it now. The idea is to create, by
a call
(tolnil x f1 f2 f3)
the functions f1x, f2x, f3x which will do the equivalent of
(deffunction f1x ($?args)
(if (eq nil $?args) then (return nil))
(return (f1 $?args))
)
This looks simple enough, but the problem is that f1, f2,...
won't accept a multivariable for an arbitrary list of arguments.
So I wrote the deffunction "bc" (see below) which creates a
Funcall, adds the components of a multivariable as individual
args and calls Funcall.execute(). I used this in TolerateNil
to call (eq nil...) and (f1 ...). "bc" works nicely, except...
The problem I've been running into is that nil is turned into
NULL when "bc" calls the function it should call. I tried several
different ways of representing nil (Funcall.NIL, "nil"/RU.STRING,
and a variable), all to no avail. I don't see an easy way to avoid
the conversion to NULL, since this is what has to be passed in if a
POJO method is called.
Perhaps there is a way to avoid the "bc" approach, perhaps by
a (new) method that adds all the elements of a multivariable
as individual arguments to a Funcall, e.g.
Funcall.argsFromMultivariable( Value mv )
But while all of this is quite interesting, it's not all that
important. I'm just unloading ;-)
---------------------------------------- TolerateNil.java
package at.laune.jess;
import jess.Context;
import jess.Deffunction;
import jess.Funcall;
import jess.JessException;
import jess.RU;
import jess.Userfunction;
import jess.Value;
import jess.ValueFactory;
import jess.ValueVector;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
/**
* A Jess Userfunction that wraps functions with a check for nil
* argument(s), returning nil if one is found and otherwise
* calls the function and returns that result.
*
* (tolnil suffix function+)
*
* The first argument must be a string, and all others
* must be function names. The suffix is appended to the function names
* for naming the wrapped functions.
*
* @author Wolfgang Laun
*/
public class TolerateNil implements Userfunction {
private static final String NAME = "tolnil";
/** Get the name of the Jess function.
* @see jess.Userfunction#getName()
*/
public String getName(){
return NAME;
}
/** Call the userfunction
* @see jess.Userfunction#call(jess.ValueVector, jess.Context)
*/
public Value call( ValueVector vv, Context context ) throws
JessException {
int narg = vv.size() - 1;
// We must have one argument.
if( narg < 2 )
throw new JessException( NAME, "not enough arguments",
Integer.toString( narg ) );
String suffix = vv.get( 1 ).stringValue( context );
jess.Rete rete = context.getEngine();
ValueFactory vFact = new ValueFactory();
StringBuffer log = new StringBuffer( "HUHU:" );
int nDef = 0;
for( int iArg = 2; iArg <= narg; iArg++ ){
String fname = vv.get( iArg ).stringValue( context );
Userfunction ffunc = rete.findUserfunction( fname );
if( ffunc == null ){
throw new JessException( NAME, "no such userfunction", fname
);
}
String xname = fname + suffix;
if( rete.findUserfunction( xname ) != null ){
throw new JessException( NAME, "won't redefine", xname );
}
Deffunction xfunc =
new Deffunction( xname, "tolerate nil as an argument in " +
fname );
xfunc.addArgument( "args", RU.MULTIVARIABLE );
Funcall bind = new Funcall( "bind", rete );
bind.arg( vFact.get( " nil ", RU.VARIABLE ) );
bind.arg( Funcall.NIL );
xfunc.addAction( bind );
Funcall ifTest = new Funcall( "if", rete );
xfunc.addAction( ifTest );
Funcall eqNil = new Funcall( "bc", rete );
ifTest.arg( eqNil );
eqNil.arg( vFact.get( "eq", RU.STRING ) )
.arg( vFact.get( " nil ", RU.VARIABLE ) ) // symbol nil
-> NULL
.arg( vFact.get( "args", RU.MULTIVARIABLE ) );
Funcall returnFalse = new Funcall( "return", rete );
returnFalse.arg( Funcall.FALSE );
ifTest.arg( vFact.get( "then", RU.SYMBOL ) )
.arg( returnFalse );
Funcall propagate = new Funcall( "bc", rete );
propagate.arg( vFact.get( fname, RU.STRING ) )
.arg( vFact.get( "args", RU.MULTIVARIABLE ) );
Funcall returnResult = new Funcall( "return", rete );
returnResult.arg( propagate );
xfunc.addAction( returnResult );
rete.addUserfunction( xfunc );
nDef++;
}
// return vFact.get( nDef );
return vFact.get( log.toString(), RU.STRING );
}
}
----------------------------------------------- bc
(import jess.Funcall)
(import jess.ValueFactory)
(import jess.RU)
(deffunction bc ($?args)
(printout t (first$ $?args) " - " (rest$ $?args) crlf)
(bind ?fname (nth$ 1 $?args))
(bind ?fargs (rest$ $?args))
(bind ?context (context))
(bind ?valFact (new jess.ValueFactory))
(bind ?funcall (new jess.Funcall ?fname (engine)))
(while (> (length$ ?fargs) 0)
(bind ?arg (nth$ 1 ?fargs))
(bind ?fargs (rest$ ?fargs))
(printout t "adding argument " ?arg crlf)
(if (eq nil ?arg) then
(?funcall arg (?valFact get "nil" (RU.SYMBOL)))
else
(?funcall arg ?arg)
)
)
(return (?funcall execute ?context))
)
--------------------------------------------------------------
On Thu, Nov 20, 2008 at 4:03 AM, Ernest Friedman-Hill <[EMAIL PROTECTED]>wrote:
> This might not be the right answer to your question, but: if an "action" in
> the Deffunction has variables with the same names as the arguments to the
> Deffunction, then when the action is invoked by the Deffunction, it will get
> the actual values; i.e., this prints "5":
>
>
> Rete engine = new Rete();
> Deffunction xfunc = new Deffunction( "func", "comment" );
> xfunc.addArgument( "n1", RU.VARIABLE);
> xfunc.addArgument( "n2", RU.VARIABLE);
> Funcall fc1 = new Funcall("+", engine);
> fc1.arg(new Variable("n1", RU.VARIABLE));
> fc1.arg(new Variable("n2", RU.VARIABLE));
> xfunc.addAction( fc1 );
> engine.addUserfunction(xfunc);
> System.out.println(engine.eval("(func 2 3)"));
>
>
>
> On Nov 19, 2008, at 11:46 AM, Wolfgang Laun wrote:
>
> To build a deffunction by API calls, you use s.th. like
>> Deffunction xfunc = new Deffunction( "func", "comment" );
>> xfunc.addArgument( "arg",... );
>> Funcall fc1 = new Funcall(...);
>> fc1.arg( ... );
>> xfunc.addAction( fc1 );
>> And so on.
>>
>> What I'd like to do is to pass all the parameters of the defined
>> function "func" to some Funcall within the body of that function.
>>
>> I tried to use an RU.MULTIVARIABLE as a "func" argument and
>> pass that to the Funcall, but intrinsics don't seem to like that.
>>
>> Is there a way to pass the multivariable as a single argument?
>>
>> I can - vaguely - imagine that it's possible to call the API from the
>> composed
>> function using calls to (call) to build a Funcall that has as many
>> arguments as the multivariable contains simple variables, and
>> then call that function. But this seems to be a lot of effort for a simple
>> task.
>>
>> Cheers
>> Wolfgang
>>
>>
>>
> ---------------------------------------------------------
> Ernest Friedman-Hill
> Informatics & Decision Sciences, Sandia National Laboratories
> PO Box 969, MS 9012, Livermore, CA 94550
> http://www.jessrules.com
>
>
>
>
>
>
>
> --------------------------------------------------------------------
> To unsubscribe, send the words 'unsubscribe jess-users [EMAIL PROTECTED]'
> in the BODY of a message to [EMAIL PROTECTED], NOT to the list
> (use your own address!) List problems? Notify [EMAIL PROTECTED]
> --------------------------------------------------------------------
>
>