# HG changeset patch # User John W. Eaton # Date 1705777202 18000 # Node ID 5252c3621c7a3c4d511830d47a62beb4eacdea56 # Parent 0c193dadf110c3b51d44780ce36f06738ae62ddd# Parent aee5333aebb17e6ff7eb4073ab4e3862eccdc60e merge stable to default diff -r 0c193dadf110 -r 5252c3621c7a doc/interpreter/func.txi --- a/doc/interpreter/func.txi Fri Jan 19 16:34:31 2024 -0800 +++ b/doc/interpreter/func.txi Sat Jan 20 14:00:02 2024 -0500 @@ -628,6 +628,25 @@ @end group @end example +Functions may take advantage of ignored outputs to reduce the number of +calculations performed. To do so, use the @code{isargout} function to query +whether the output argument is wanted. For example: + +@example +@group +function [out1, out2] = long_function (x, y, z) + if (isargout (1)) + ## Long calculation + @dots{} + out1 = result; + endif + @dots{} +endfunction +@end group +@end example + +@DOCSTRING(isargout) + @node Default Arguments @section Default Arguments @cindex default arguments diff -r 0c193dadf110 -r 5252c3621c7a etc/NEWS.9.md --- a/etc/NEWS.9.md Fri Jan 19 16:34:31 2024 -0800 +++ b/etc/NEWS.9.md Sat Jan 20 14:00:02 2024 -0500 @@ -173,12 +173,6 @@ Function | Replacement -----------------------|------------------ - isargout | none (see below) - - * The implementation of `isargout` has been the source of a number of bugs - and was inefficient, even for functions that did not use it. It may be - removed in a future version of Octave. Until then, `isargout(k)` will - return true for k in the range `1:min (1, nargout)`. - Properties diff -r 0c193dadf110 -r 5252c3621c7a libinterp/corefcn/stack-frame.h --- a/libinterp/corefcn/stack-frame.h Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/corefcn/stack-frame.h Sat Jan 20 14:00:02 2024 -0500 @@ -130,6 +130,7 @@ enum auto_var_type { ARG_NAMES, + IGNORED, NARGIN, NARGOUT, SAVED_WARNING_STATES, diff -r 0c193dadf110 -r 5252c3621c7a libinterp/octave-value/ov-usr-fcn.cc --- a/libinterp/octave-value/ov-usr-fcn.cc Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/octave-value/ov-usr-fcn.cc Sat Jan 20 14:00:02 2024 -0500 @@ -790,7 +790,7 @@ Programming Note. @code{nargout} does not work for built-in functions and returns -1 for all anonymous functions. -@seealso{nargin, varargout, nthargout} +@seealso{nargin, varargout, isargout, nthargout} @end deftypefn */) { int nargin = args.length (); @@ -886,27 +886,39 @@ } static bool -isargout1 (int nargout, double k) +val_in_table (const Matrix& table, double val) +{ + if (table.isempty ()) + return false; + + octave_idx_type i = table.lookup (val, ASCENDING); + return (i > 0 && table(i-1) == val); +} + +static bool +isargout1 (int nargout, const Matrix& ignored, double k) { if (k != math::fix (k) || k <= 0) error ("isargout: K must be a positive integer"); - return k == 1 || k <= nargout; + return (k == 1 || k <= nargout) && ! val_in_table (ignored, k); } -// LEGACY FUNCTION as of Octave 9. - DEFMETHOD (isargout, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{tf} =} isargout (@var{k}) -This function is obsolete and may be removed from a future version of -Octave. Inside a function and now always returns true for @var{k} in -the range @code{1:min (1, nargout)}. +Within a function, return a logical value indicating whether the argument +@var{k} will be assigned to a variable on output. -@var{k} can also be an array, in which case the function works -element-by-element and a logical array is returned. +If the result is false, the argument has been ignored during the function +call through the use of the tilde (~) special output argument. Functions +can use @code{isargout} to avoid performing unnecessary calculations for +outputs which are unwanted. -At the top level, @code{isargout} returns an error. +If @var{k} is outside the range @code{1:max (nargout)}, the function returns +false. @var{k} can also be an array, in which case the function works +element-by-element and a logical array is returned. At the top level, +@code{isargout} returns an error. @seealso{nargout, varargout, nthargout} @end deftypefn */) { @@ -925,11 +937,16 @@ if (tmp.is_defined ()) nargout1 = tmp.int_value (); + Matrix ignored; + tmp = tw.get_auto_fcn_var (stack_frame::IGNORED); + if (tmp.is_defined ()) + ignored = tmp.matrix_value (); + if (args(0).is_scalar_type ()) { double k = args(0).double_value (); - return ovl (isargout1 (nargout1, k)); + return ovl (isargout1 (nargout1, ignored, k)); } else if (args(0).isnumeric ()) { @@ -937,7 +954,7 @@ boolNDArray r (ka.dims ()); for (octave_idx_type i = 0; i < ka.numel (); i++) - r(i) = isargout1 (nargout1, ka(i)); + r(i) = isargout1 (nargout1, ignored, ka(i)); return ovl (r); } @@ -949,50 +966,83 @@ /* %!function [x, y] = try_isargout () -%! global isargout_global -%! isargout_global = [isargout(1), isargout(2)]; -%! x = 1; -%! y = 2; +%! if (isargout (1)) +%! if (isargout (2)) +%! x = 1; y = 2; +%! else +%! x = -1; +%! endif +%! else +%! if (isargout (2)) +%! y = -2; +%! else +%! error ("no outputs requested"); +%! endif +%! endif +%!endfunction +%! +%!function [a, b] = try_isargout2 (x, y) +%! a = y; +%! b = {isargout(1), isargout(2), x}; %!endfunction %! %!test -%! global isargout_global -%! unwind_protect -%! [x, y] = try_isargout (); -%! assert ([x, y], [1, 2]); -%! assert (isargout_global, [true, true]); -%! unwind_protect_cleanup -%! clear -global isargout_global -%! end_unwind_protect +%! [x, y] = try_isargout (); +%! assert ([x, y], [1, 2]); +%! +%!test +%! [x, ~] = try_isargout (); +%! assert (x, -1); %! %!test -%! global isargout_global -%! unwind_protect -%! [x, ~] = try_isargout (); -%! assert (x, 1); -%! assert (isargout_global, [true, true]); -%! unwind_protect_cleanup -%! clear -global isargout_global -%! end_unwind_protect +%! [~, y] = try_isargout (); +%! assert (y, -2); +%! +%!error [~, ~] = try_isargout () +%! +## Check to see that isargout isn't sticky: +%!test +%! [x, y] = try_isargout (); +%! assert ([x, y], [1, 2]); %! +## It should work without (): +%!test +%! [~, y] = try_isargout; +%! assert (y, -2); +%! +## It should work in function handles, +%!test +%! fh = @try_isargout; +%! [~, y] = fh (); +%! assert (y, -2); +%! +## anonymous functions, %!test -%! global isargout_global -%! unwind_protect -%! [~, y] = try_isargout (); -%! assert (y, 2); -%! assert (isargout_global, [true, true]); -%! unwind_protect_cleanup -%! clear -global isargout_global -%! end_unwind_protect +%! af = @() try_isargout; +%! [~, y] = af (); +%! assert (y, -2); %! +## and cell arrays of handles or anonymous functions. %!test -%! global isargout_global -%! unwind_protect -%! [~, ~] = try_isargout () -%! assert (isargout_global, [true, true]); -%! unwind_protect_cleanup -%! clear -global isargout_global -%! end_unwind_protect +%! fh = @try_isargout; +%! af = @() try_isargout; +%! c = {fh, af}; +%! [~, y] = c{1}(); +%! assert (y, -2); +%!test +%! fh = @try_isargout; +%! af = @() try_isargout; +%! c = {fh, af}; +%! [~, y] = c{2}(); +%! assert (y, -2); +%! +## Nesting, anyone? +%!test +%! [~, b] = try_isargout2 (try_isargout, rand); +%! assert (b, {0, 1, -1}); +%!test +%! [~, b] = try_isargout2 ({try_isargout, try_isargout}, rand); +%! assert (b, {0, 1, {-1, -1}}); */ OCTAVE_END_NAMESPACE(octave) diff -r 0c193dadf110 -r 5252c3621c7a libinterp/op-kw-docs --- a/libinterp/op-kw-docs Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/op-kw-docs Sat Jan 20 14:00:02 2024 -0500 @@ -873,14 +873,14 @@ -*- texinfo -*- @deftypefn {} {} varargin Pass an arbitrary number of arguments into a function. -@seealso{varargout, nargin, nargout, nthargout} +@seealso{varargout, nargin, isargout, nargout, nthargout} @end deftypefn varargout @c libinterp/parse-tree/oct-parse.yy -*- texinfo -*- @deftypefn {} {} varargout Pass an arbitrary number of arguments out of a function. -@seealso{varargin, nargin, nargout, nthargout} +@seealso{varargin, nargin, isargout, nargout, nthargout} @end deftypefn while @c libinterp/parse-tree/oct-parse.yy diff -r 0c193dadf110 -r 5252c3621c7a libinterp/parse-tree/pt-assign.cc --- a/libinterp/parse-tree/pt-assign.cc Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/parse-tree/pt-assign.cc Sat Jan 20 14:00:02 2024 -0500 @@ -207,8 +207,6 @@ ? rhs_val1(0).list_value () : rhs_val1); - tw.set_lvalue_list (nullptr); - octave_idx_type k = 0; octave_idx_type n = rhs_val.length (); diff -r 0c193dadf110 -r 5252c3621c7a libinterp/parse-tree/pt-eval.cc --- a/libinterp/parse-tree/pt-eval.cc Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/parse-tree/pt-eval.cc Sat Jan 20 14:00:02 2024 -0500 @@ -1458,6 +1458,40 @@ return frame->inputname (n, ids_only); } +Matrix +tree_evaluator::ignored_fcn_outputs () const +{ + Matrix retval; + + const std::list *lvalues = m_lvalue_list; + + if (! lvalues) + return retval; + + octave_idx_type nbh = 0; + + for (const auto& lval : *lvalues) + nbh += lval.is_black_hole (); + + if (nbh > 0) + { + retval.resize (1, nbh); + + octave_idx_type k = 0; + octave_idx_type l = 0; + + for (const auto& lval : *lvalues) + { + if (lval.is_black_hole ()) + retval(l++) = k+1; + + k += lval.numel (); + } + } + + return retval; +} + // If NAME is an operator (like "+", "-", ...), convert it to the // corresponding function name ("plus", "minus", ...). @@ -3529,6 +3563,17 @@ octave_value_list args (xargs); + // FIXME: this probably shouldn't be a double-precision matrix. + Matrix ignored_outputs = ignored_fcn_outputs (); + + unwind_protect frame; + + if (! user_function.is_anonymous_function ()) + { + frame.protect_var (m_lvalue_list); + m_lvalue_list = nullptr; + } + octave_value_list ret_args; int nargin = args.length (); @@ -3591,8 +3636,8 @@ } } - bind_auto_fcn_vars (xargs.name_tags (), nargin, nargout, - user_function.takes_varargs (), + bind_auto_fcn_vars (xargs.name_tags (), ignored_outputs, nargin, + nargout, user_function.takes_varargs (), user_function.all_va_args (args)); // For classdef constructor, pre-populate the output arguments @@ -5192,11 +5237,13 @@ void tree_evaluator::bind_auto_fcn_vars (const string_vector& arg_names, + const Matrix& ignored_outputs, int nargin, int nargout, bool takes_varargs, const octave_value_list& va_args) { set_auto_fcn_var (stack_frame::ARG_NAMES, Cell (arg_names)); + set_auto_fcn_var (stack_frame::IGNORED, ignored_outputs); set_auto_fcn_var (stack_frame::NARGIN, nargin); set_auto_fcn_var (stack_frame::NARGOUT, nargout); set_auto_fcn_var (stack_frame::SAVED_WARNING_STATES, octave_value ()); @@ -5631,12 +5678,4 @@ %!error inputname (-1) */ -// DEPRECATED in Octave 9. - -Matrix -tree_evaluator::ignored_fcn_outputs () const -{ - return Matrix (); -} - OCTAVE_END_NAMESPACE(octave) diff -r 0c193dadf110 -r 5252c3621c7a libinterp/parse-tree/pt-eval.h --- a/libinterp/parse-tree/pt-eval.h Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/parse-tree/pt-eval.h Sat Jan 20 14:00:02 2024 -0500 @@ -344,7 +344,6 @@ SC_OTHER // command-line input or eval string }; - OCTAVE_DEPRECATED (9, "tree_evaluator::ignored_fcn_outputs is obsolete and now always returns an empty Matrix object"); Matrix ignored_fcn_outputs () const; std::string inputname (int n, bool ids_only = true) const; @@ -889,7 +888,8 @@ bool quit_loop_now (); void bind_auto_fcn_vars (const string_vector& arg_names, - int nargin, int nargout, bool takes_varargs, + const Matrix& ignored_outputs, int nargin, + int nargout, bool takes_varargs, const octave_value_list& va_args); std::string check_autoload_file (const std::string& nm) const; diff -r 0c193dadf110 -r 5252c3621c7a libinterp/parse-tree/pt-mat.cc --- a/libinterp/parse-tree/pt-mat.cc Fri Jan 19 16:34:31 2024 -0800 +++ b/libinterp/parse-tree/pt-mat.cc Sat Jan 20 14:00:02 2024 -0500 @@ -47,6 +47,13 @@ octave_value tree_matrix::evaluate (tree_evaluator& tw, int) { + unwind_action act ([&tw] (const std::list *lvl) + { + tw.set_lvalue_list (lvl); + }, tw.lvalue_list ()); + + tw.set_lvalue_list (nullptr); + tm_const tmp (*this, tw); return tmp.concat (tw.string_fill_char ()); diff -r 0c193dadf110 -r 5252c3621c7a scripts/miscellaneous/nthargout.m --- a/scripts/miscellaneous/nthargout.m Fri Jan 19 16:34:31 2024 -0800 +++ b/scripts/miscellaneous/nthargout.m Sat Jan 20 14:00:02 2024 -0500 @@ -68,7 +68,7 @@ ## @var{USV} = nthargout ([1:3], @@svd, hilb (5)); ## @end example ## -## @seealso{nargin, nargout, varargin, varargout} +## @seealso{nargin, nargout, varargin, varargout, isargout} ## @end deftypefn function arg = nthargout (n, varargin)