Subscribe Bookmark RSS Feed

Optional arguments in functions

paologrigis

Community Trekker

Joined:

Oct 19, 2011

HI folks,

I just started using JMP after spending almost 10 years using IDL,

therefore transistioning from expert to beginner.

I am mostly in the process of learning the scripting language right now.

Hopefully you'll pardon me for the occasional silly question

So I'll start with a simple question:

What is the best way to use optional arguments in functions?

When I run scripts they do complain if I use less then the number

of arguments specified in the argument list of the function.

One could conceivably use associative arrays as input instead,

and have optional keys in there, but that requires the user to

create one every time the function is called, which seems a bit

cumbersome....

Thanks!

Paolo Grigis

ABB Switzerland

1 ACCEPTED SOLUTION

Accepted Solutions
Solution

Paulo

If you create a user-defined function then you have the option of specifying default values for arguments. If defaults are specified then the arguments become optional.

So in the example below the 3rd argument is optional and defaults to missing (indicated by a dot in JSL)

     My Function = Function( {a,b,c=.}, {Default Local},

         show(IsMissing(c));

         show(a,b,c)

     );

     My Function( 1, 2 )

Regards,

Dave

-Dave
8 REPLIES
David_Burnham

Super User

Joined:

Jul 13, 2011

Hi Paolo

You will probably get some more helpful responses if you can give some examples of the type of functions that you are using.

Typically functions have required arguments that need to be parsed in a specific order, but there are additional optional arguments, often specified by keywords, that can be specified in any position and omitted if not required.

As far as I am aware it is not possible to parse the arguments as a list, they have to be specified indivdually, except of course where the argument item is itself a list.  However, there is a JSL function called FUNCTION that allows you to create user-defined functions.  I often use this capability to "wrap" an existing JSL function so that I have more control of the input arguments.

Regards,

Dave

-Dave
paologrigis

Community Trekker

Joined:

Oct 19, 2011

Hi Dave,

thanks for your answer.

That's exactly what I am looking for - optional keywords - only

I am not sure how to implement them in my functions.

For instance, if I am creating a user-defined function called myplot

to do a plot, and I would like to be able to call it using

myplot(x,y)

or

myplot(x,y,xRange,yRange)

(or whatever is the required syntax is to specify xRange and yRange

as keywords).

If xRange and yRange are specified, I will use them, if not

I will compute some default values for them.

My question was how to implement that in my user defined function.

If I use

function({x,y,xRange,yRange},

my commands...

);

it will complain if I call it with only two arguments as in

dummy=myplot(x,y)

and throw an error message about insufficent arguments.

I hope this make it a bit more clear. Maybe I am missing

something obvious but I find the reference for the commands

at the end of the scripting guide somewhat less detailed then

what I am used to.

Paolo Grigis

ABB Switzerland

Solution

Paulo

If you create a user-defined function then you have the option of specifying default values for arguments. If defaults are specified then the arguments become optional.

So in the example below the 3rd argument is optional and defaults to missing (indicated by a dot in JSL)

     My Function = Function( {a,b,c=.}, {Default Local},

         show(IsMissing(c));

         show(a,b,c)

     );

     My Function( 1, 2 )

Regards,

Dave

-Dave
paologrigis

Community Trekker

Joined:

Oct 19, 2011

Ah yes, that's what I was looking for thanks!

paologrigis

Community Trekker

Joined:

Oct 19, 2011

well, on second thought, what if I have a function with multiple optional

arguments, say,

PGtest=function({a,b,c=1,d=2,e=3},{Default Local},

    Show(a,b,c,d,e);

);

I want to specify arguments a,b and e but not c and d

(in this call, maybe in future calls i will specify a,b and d

instead...).

Do I have to use

PGTest(a,b,  ,  , e=11)

? That becomes unwieldy with more than a few arguments to skip...

Is there a way to do this without having to specify the middle

arguments? 

Thanks,

Paolo

Taking up Dave's earlier point about lists, something I often do myself is to supply my user-defined functions with a single list containing as many arguments as I want to supply, as opposed to simply a set of them. I can then perform tests on the list within the function to see what's there and what isn't. For example, the following function tests first of all to see if what I've supplied it with actually is a list, and then if it is, either raises the first number in the list to the power of the second if a second number is supplied, or square-roots it otherwise. If the argument isn't a list but is simply a number, that number is square-rooted anyway. That provides me with quite a bit of flexibility in terms of how I can use the function:

RootFn = function({Params},

     show(Params);

     if(is list(Params),

          if(nItems(Params) < 2, Result = Params[1]^0.5, Result = Params[1]^Params[2])

          ,

          Result = Params^0.5

     );

Result

);

MyParams = {6, 3}; show(RootFn(MyParams));

MyParams = {6, 2}; show(RootFn(MyParams));

MyParams = {6, 0.5}; show(RootFn(MyParams));

MyParams = {6}; show(RootFn(MyParams));

MyParams = 6; show(RootFn(MyParams));

You could try something like this, inserting a call to your myplot function into the user-defined function wherever appropriate.

David_Burnham

Super User

Joined:

Jul 13, 2011

If you don't want to parse all th arguments then you could use a list a David suggested above.  You would still need to insert the values in the correct position in the list.  Alternatively use an associative array which gives you key-value pairs, so you could use the key as the label for the parameter  e.g. myArray["c"] = 10. 

If you are familiar with object-oriented programming then you can implement the equivalent of a protected class.  You can then have a function that acts as a method call (i.e. do something ) which uses properties set using functions that act as accessors (i.e. set value / get value).  These properties can be stored in a namespace (and if you want to can put all these definitions into a separate file and use the Include function to reference them).

Below is some example code that illustrates this principle. There is a function (method call) called Standard Capability Analysis that takes 2 arguments - a reference to the data table column, and the type of distribution to use for the calculation.  The method call also requires specification values, but these are set using accessor methods Set LSL / Get LSL etc.  The values associated with these properties are stored in a namespace.

     capdat = New Namespace( "MyCapabilityAnalysis" );

     capdat:lsl = List();

     capdat:target = List();

     capdat:usl = List();

     Get LSL = Function({colIndex},{Default Local},

                    retVal = .;

                    If (!IsMissing(capdat:lsl[colIndex]),

                        retVal = capdat:lsl[colIndex]

                    ,

                        Get Specs(colIndex);

                        retVal = capdat:lsl[colIndex]

                    );

               retVal

     );

     Get Target = Function({colIndex},{Default Local},

               retVal = .;

               If (!IsMissing(capdat:target[colIndex]),

                   retVal = capdat:target[colIndex]

               ,

                   Get Specs(colIndex);

                   retVal = capdat:target[colIndex]

               );

               retVal

     );

     Get USL = Function({colIndex},{Default Local},

               retVal = .;

               If (!IsMissing(capdat:usl[colIndex]),

                   retVal = capdat:usl[colIndex]

               ,

                   Get Specs(colIndex);

                   retVal = capdat:usl[colIndex]

               );

               retVal

     );

     Standard Capability Analysis = Function({colIndex,distType},{Defalt Local},

               lsl = Get LSL(colIndex);

               target = Get Target(colIndex);

               USL = Get USL(colIndex);

               pY = Column( this:yCols[colIndex] ) << Get Name;

               funcDef = Eval Insert( "\[

                       this:dd = this:tempDt << Distribution(

                       Continuous Distribution(

                           Column( ^pY^ ),

                           Quantiles( 0 ),

                           Moments( 0 ),

                           Vertical( 0 ),

                           Outlier Box Plot( 0 ),

                           Capability Analysis( LSL(^lsl^),USL(^usl^),Target(^target^)  ),

                           Fit Distribution(

                               ^distType^(

                                   Goodness of Fit( 1 )

                                ),

                            )

                         )

                      );

                ]\");

               Eval( Parse( funcDef ) );

       );

-Dave
paologrigis

Community Trekker

Joined:

Oct 19, 2011

Well,

in the end I decided to have a procedure that converts a list

of strings like, say,

{"xrange=[-1,6]","WinSize=[200,400]","yRange=[-3.5, 7.77]"}

into an associative array and then use the associative array as

input in my plot function.

The converter checks that the key=value pairs have keys that belong

to the list of "allowed" keys and doesn't care about the order, such that

{"yRange=[-3.5, 7.77]","xrange=[-1,6]"}

is also an acceptable input.

Arguably that's only marginally different then creating the array

directly... but it does have the advantage to reject keywords that

aren't allowed and can also set default values if needed.

In the plot function I also check for missing keys and set them to a

default value.

This would be easier if we could create objects directly

Ciao,

Paolo