octave-maintainers
[Top][All Lists]
Advanced

[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)
        {

reply via email to

[Prev in Thread] Current Thread [Next in Thread]