[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Default arguments
From: |
John W. Eaton |
Subject: |
Default arguments |
Date: |
Thu, 14 Dec 2006 22:23:47 -0500 |
On 14-Dec-2006, Søren Hauberg wrote:
| I'd really like Octave to have some way of having default function
| arguments. In C++ you can do
|
| void fn(int arg = 7) {}
|
| I think R supports the same syntax. I don't think matlab has something
| similar, so if Octave adds support for default it should be something
| simple that won't break whatever syntax matlab decides to add in the
| future that'll handle default arguments.
|
| So I suggest that Octave just adds some functions to make it easy to
| work with default arguments, instead of changing the syntax of the
| language. The C++ example above would then be written something like this
|
| function fn(arg)
| arg = default_value("arg", 7);
| endfunction
|
| The function default_value should then expand (like a macro in C/C++)
| into this code
|
| if (!exist(arg, "var")
| arg = val;
| endif
Wouldn't it be better to have the syntax
function foo (arg = default_val)
? The patch below (only very slightly tested) implements
this syntax. It doesn't introduce any new conflicts in the parser, so
I think it should be OK. With the patch, you can write things like
this:
octave:1> function x = bar () persistent retval = 0; x = retval++; end
octave:2> function y = foo (x = bar ()) y = x; end
octave:3> foo ()
ans = 0
octave:4> foo (10)
ans = 10
octave:5> foo ()
ans = 1
It also works for anonymous functions:
octave:6> f = @(x=1) x
f =
@(x = 1) x
octave:7> f ()
ans = 1
As you note, there is the possiblity of a future incompatibility with
Matlab. Is this feature worth the risk? I think I would want to use
it in functions that are distributed with Octave to simplify code that
needs to set default values.
| One thing that's missing from this idea, is a way to tell the function
| to use the default argument. As an example, think of a function that
| takes three arguments
|
| function fn2(arg1, arg2, arg3)
| ...
| endfunction
|
| How does the user tell fn2 that he wants to set the value of arg1 and
| arg3, but use the default value for arg2? I suggest adding a function
| called "default" that returns an object of type "default". That way the
| user can write
|
| fn2(arg1, default, arg3)
|
| I don't know how to handle varargin, but I don't think there is much one
| can do...
My patch overloads ":" for this purpose:
octave:1> function f (x = 1, y = 2, z = 3) x, y, z, end
octave:2> f (10, :, 20)
x = 10
y = 2
z = 20
octave:3> function f (x = 1, y, z = 3) x, y, z, end
octave:4> f (10, :, 20)
error: no default value for argument 2
Is that reasonable?
Or, maybe what you really want is keyword arguments? I think that
would be harder to add.
Although I've checked this patch in to the CVS archive, I consider it
to be quite experimental. If any nontrivial problems show up that are
related to this change, I'll simply remove it.
jwe
src/ChangeLog:
2006-12-14 John W. Eaton <address@hidden>
* pt-decl.cc (tree_decl_elt::eval): New function.
* pt-decl.h (tree_decl_elt::eval): Provide decl.
(tree_decl_elt::is_defined, tree_decl_elt::lvalue_ok,
tree_decl_elt::mark_as_formal_parameter, tree_decl_elt::rvalue,
tree_decl_elt::lvalue): New functions.
* pt-misc.h (class tree_parameter_list): Derive from
octave_base_list<tree_decl_elt *> instead of
octave_base_list<tree_identifier *>.
(tree_parameter_list::tree_parameter_list (tree_identifier *)): Delete.
(tree_parameter_list::tree_parameter_list (tree_decl_elt *)):
New function.
* pt-misc.cc (tree_parameter_list::mark_as_formal_parameters,
tree_parameter_list::initialize_undefined_elements,
tree_parameter_list::undefine, tree_parameter_list::dup,
tree_parameter_list::convert_to_const_vector,
tree_parameter_list::is_defined): Handle argument list elements
as tree_decl_list objects instead of tree_identifier objects.
(tree_parameter_list::define_from_arg_vector): Likewise.
Always process entire list, setting default values if possible.
Accept ":" to mean "use default argument".
* parse.y (param_list2): Use decl2 to recognize
"identifier '=' expression" in addition to "identifier".
* parse.y (return_list1, make_anon_fcn_handle, finish_function):
Adapt to new definition of tree_parameter_list.
* pt-bp.cc (tree_breakpoint::visit_parameter_list): Likewise.
* pt-check.cc (tree_checker::visit_parameter_list): Likewise.
* pt-pr-code.cc (tree_print_code::visit_parameter_list): Likewise.
Index: src/parse.y
===================================================================
RCS file: /cvs/octave/src/parse.y,v
retrieving revision 1.268
diff -u -u -r1.268 parse.y
--- src/parse.y 29 Aug 2006 16:37:40 -0000 1.268
+++ src/parse.y 15 Dec 2006 03:22:23 -0000
@@ -1165,9 +1165,9 @@
}
;
-param_list2 : identifier
+param_list2 : decl2
{ $$ = new tree_parameter_list ($1); }
- | param_list2 ',' identifier
+ | param_list2 ',' decl2
{
$1->append ($3);
$$ = $1;
@@ -1214,10 +1214,10 @@
;
return_list1 : identifier
- { $$ = new tree_parameter_list ($1); }
+ { $$ = new tree_parameter_list (new tree_decl_elt ($1)); }
| return_list1 ',' identifier
{
- $1->append ($3);
+ $1->append (new tree_decl_elt ($3));
$$ = $1;
}
;
@@ -1796,8 +1796,9 @@
// created so we don't have to create a new statement at all.
id = new tree_identifier (sr);
+ tree_decl_elt *elt = new tree_decl_elt (id);
- ret_list = new tree_parameter_list (id);
+ ret_list = new tree_parameter_list (elt);
}
tree_statement_list *body = new tree_statement_list (stmt);
@@ -2585,7 +2586,9 @@
finish_function (tree_identifier *id, octave_user_function *fcn,
octave_comment_list *lc)
{
- tree_parameter_list *tpl = new tree_parameter_list (id);
+ tree_decl_elt *tmp = new tree_decl_elt (id);
+
+ tree_parameter_list *tpl = new tree_parameter_list (tmp);
tpl->mark_as_formal_parameters ();
Index: src/pt-bp.cc
===================================================================
RCS file: /cvs/octave/src/pt-bp.cc,v
retrieving revision 1.22
diff -u -u -r1.22 pt-bp.cc
--- src/pt-bp.cc 16 Jun 2006 05:09:41 -0000 1.22
+++ src/pt-bp.cc 15 Dec 2006 03:22:23 -0000
@@ -470,7 +470,7 @@
while (p != lst.end ())
{
- tree_identifier *elt = *p++;
+ tree_decl_elt *elt = *p++;
if (elt)
elt->accept (*this);
Index: src/pt-check.cc
===================================================================
RCS file: /cvs/octave/src/pt-check.cc,v
retrieving revision 1.18
diff -u -u -r1.18 pt-check.cc
--- src/pt-check.cc 16 Jun 2006 05:09:41 -0000 1.18
+++ src/pt-check.cc 15 Dec 2006 03:22:23 -0000
@@ -327,7 +327,7 @@
while (p != lst.end ())
{
- tree_identifier *elt = *p++;
+ tree_decl_elt *elt = *p++;
if (elt)
elt->accept (*this);
Index: src/pt-decl.cc
===================================================================
RCS file: /cvs/octave/src/pt-decl.cc,v
retrieving revision 1.23
diff -u -u -r1.23 pt-decl.cc
--- src/pt-decl.cc 16 Jun 2006 05:09:42 -0000 1.23
+++ src/pt-decl.cc 15 Dec 2006 03:22:23 -0000
@@ -45,6 +45,28 @@
delete expr;
}
+bool
+tree_decl_elt::eval (void)
+{
+ bool retval = false;
+
+ if (id && expr)
+ {
+ octave_lvalue ult = id->lvalue ();
+
+ octave_value init_val = expr->rvalue ();
+
+ if (! error_state)
+ {
+ ult.assign (octave_value::op_asn_eq, init_val);
+
+ retval = true;
+ }
+ }
+
+ return retval;
+}
+
tree_decl_elt *
tree_decl_elt::dup (symbol_table *sym_tab)
{
Index: src/pt-decl.h
===================================================================
RCS file: /cvs/octave/src/pt-decl.h,v
retrieving revision 1.12
diff -u -u -r1.12 pt-decl.h
--- src/pt-decl.h 16 Jun 2006 05:09:42 -0000 1.12
+++ src/pt-decl.h 15 Dec 2006 03:22:23 -0000
@@ -32,7 +32,9 @@
#include <string>
#include "base-list.h"
+#include "oct-lvalue.h"
#include "pt-cmd.h"
+#include "pt-id.h"
// List of expressions that make up a declaration statement.
@@ -48,7 +50,26 @@
~tree_decl_elt (void);
- void eval (void);
+ bool eval (void);
+
+ bool is_defined (void) { return id ? id->is_defined () : false; }
+
+ void mark_as_formal_parameter (void)
+ {
+ if (id)
+ id->mark_as_formal_parameter ();
+ }
+
+ bool lvalue_ok (void) { return id ? id->lvalue_ok () : false; }
+
+ octave_value rvalue (void) { return id ? id->rvalue () : octave_value (); }
+
+ octave_value_list rvalue (int nargout)
+ {
+ return id ? id->rvalue (nargout) : octave_value_list ();
+ }
+
+ octave_lvalue lvalue (void) { return id ? id->lvalue () : octave_lvalue (); }
tree_identifier *ident (void) { return id; }
Index: src/pt-misc.cc
===================================================================
RCS file: /cvs/octave/src/pt-misc.cc,v
retrieving revision 1.68
diff -u -u -r1.68 pt-misc.cc
--- src/pt-misc.cc 16 Jun 2006 05:09:42 -0000 1.68
+++ src/pt-misc.cc 15 Dec 2006 03:22:23 -0000
@@ -54,7 +54,7 @@
{
for (iterator p = begin (); p != end (); p++)
{
- tree_identifier *elt = *p;
+ tree_decl_elt *elt = *p;
elt->mark_as_formal_parameter ();
}
}
@@ -73,7 +73,7 @@
if (++count > nargout)
break;
- tree_identifier *elt = *p;
+ tree_decl_elt *elt = *p;
if (! elt->is_defined ())
{
@@ -99,16 +99,13 @@
{
int nargin = args.length ();
- if (nargin <= 0)
- return;
-
int expected_nargin = length ();
iterator p = begin ();
for (int i = 0; i < expected_nargin; i++)
{
- tree_identifier *elt = *p++;
+ tree_decl_elt *elt = *p++;
octave_lvalue ref = elt->lvalue ();
@@ -116,14 +113,17 @@
{
if (args(i).is_defined () && args(i).is_magic_colon ())
{
- ::error ("invalid use of colon in function argument list");
- return;
+ if (! elt->eval ())
+ {
+ ::error ("no default value for argument %d\n", i+1);
+ return;
+ }
}
-
- ref.assign (octave_value::op_asn_eq, args(i));
+ else
+ ref.assign (octave_value::op_asn_eq, args(i));
}
else
- ref.assign (octave_value::op_asn_eq, octave_value ());
+ elt->eval ();
}
}
@@ -136,7 +136,7 @@
for (int i = 0; i < len; i++)
{
- tree_identifier *elt = *p++;
+ tree_decl_elt *elt = *p++;
octave_lvalue ref = elt->lvalue ();
@@ -157,7 +157,7 @@
for (iterator p = begin (); p != end (); p++)
{
- tree_identifier *elt = *p;
+ tree_decl_elt *elt = *p;
retval(i++) = elt->is_defined () ? elt->rvalue () : octave_value ();
}
@@ -175,7 +175,7 @@
for (iterator p = begin (); p != end (); p++)
{
- tree_identifier *elt = *p;
+ tree_decl_elt *elt = *p;
if (! elt->is_defined ())
{
@@ -197,7 +197,7 @@
for (iterator p = begin (); p != end (); p++)
{
- tree_identifier *elt = *p;
+ tree_decl_elt *elt = *p;
new_list->append (elt->dup (sym_tab));
}
Index: src/pt-misc.h
===================================================================
RCS file: /cvs/octave/src/pt-misc.h,v
retrieving revision 1.47
diff -u -u -r1.47 pt-misc.h
--- src/pt-misc.h 16 Jun 2006 05:09:42 -0000 1.47
+++ src/pt-misc.h 15 Dec 2006 03:22:23 -0000
@@ -36,20 +36,21 @@
class tree_walker;
#include "base-list.h"
+#include "pt-decl.h"
// Parameter lists. Used to hold the list of input and output
// parameters in a function definition. Elements are identifiers
// only.
class
-tree_parameter_list : public octave_base_list<tree_identifier *>
+tree_parameter_list : public octave_base_list<tree_decl_elt *>
{
public:
tree_parameter_list (void)
: marked_for_varargs (0) { }
- tree_parameter_list (tree_identifier *t)
+ tree_parameter_list (tree_decl_elt *t)
: marked_for_varargs (0) { append (t); }
~tree_parameter_list (void);
Index: src/pt-pr-code.cc
===================================================================
RCS file: /cvs/octave/src/pt-pr-code.cc,v
retrieving revision 1.51
diff -u -u -r1.51 pt-pr-code.cc
--- src/pt-pr-code.cc 16 Jun 2006 05:09:42 -0000 1.51
+++ src/pt-pr-code.cc 15 Dec 2006 03:22:23 -0000
@@ -721,7 +721,7 @@
while (p != lst.end ())
{
- tree_identifier *elt = *p++;
+ tree_decl_elt *elt = *p++;
if (elt)
{
- Re: Default arguments, (continued)
- Re: Default arguments, John W. Eaton, 2006/12/15
- Re: Default arguments, Sean O'Rourke, 2006/12/15
- Re: Default arguments, Tom Holroyd (NIH/NIMH) [E], 2006/12/15
- Re: Default arguments, Sean O'Rourke, 2006/12/15
- Re: Default arguments, John W. Eaton, 2006/12/15
- Re: Default arguments, Sean O'Rourke, 2006/12/15
- Re: Default arguments, Dmitri A. Sergatskov, 2006/12/15
- Re: Default arguments, John W. Eaton, 2006/12/15
- Re: Default arguments, Paul Kienzle, 2006/12/16
- Re: Default arguments, Tom Holroyd (NIH/NIMH) [E], 2006/12/15
Default arguments,
John W. Eaton <=