diff src/DLD-FUNCTIONS/cellfun.cc @ 13234:e066d173aae6

improve compatibility of arrayfun for nargout = 0 case * cellfun.cc (Farrayfun): New function, adapted from Fcellfun. Copy tests and doc string from arrayfun.m. New tests. (get_mapper_fun_options): Rename from get_cellfun_options. Change all uses. * arrayfun.m: Delete. * scripts/general/module.mk (general_FCN_FILES): Remove general/arrayfun.m from the list.
author John W. Eaton <jwe@octave.org>
date Mon, 26 Sep 2011 15:25:17 -0400
parents 20ed9548070f
children 80f7564a3849
line wrap: on
line diff
--- a/src/DLD-FUNCTIONS/cellfun.cc	Mon Sep 26 13:09:02 2011 -0400
+++ b/src/DLD-FUNCTIONS/cellfun.cc	Mon Sep 26 15:25:17 2011 -0400
@@ -1,6 +1,7 @@
 /*
 
 Copyright (C) 2005-2011 Mohamed Kamoun
+Copyright (C) 2006-2011 Bill Denney
 Copyright (C) 2009 Jaroslav Hajek
 Copyright (C) 2010 VZLU Prague
 
@@ -196,7 +197,7 @@
 }
 
 static void
-get_cellfun_options (const octave_value_list& args, int& nargin,
+get_mapper_fun_options (const octave_value_list& args, int& nargin,
                      bool& uniform_output, octave_value& error_handler)
 {
   while (nargin > 3 && args(nargin-2).is_string ())
@@ -456,7 +457,7 @@
       bool uniform_output = true;
       octave_value error_handler;
       
-      get_cellfun_options (args, nargin, uniform_output, error_handler);
+      get_mapper_fun_options (args, nargin, uniform_output, error_handler);
 
       if (error_state)
         return octave_value_list ();
@@ -475,6 +476,9 @@
 
       dim_vector fdims (1, 1);
 
+      // Collect arguments.  Pre-fill scalar elements of inputlist
+      // array.
+
       for (int j = 0; j < nargin; j++)
         {
           if (! args(j+1).is_cell ())
@@ -977,6 +981,643 @@
 
 */
 
+// Arrayfun was originally a .m file written by Bill Denney and Jaroslav
+// Hajek.  It was converted to C++ by jwe so that it could properly
+// handle the nargout = 0 case.
+
+DEFUN_DLD (arrayfun, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn  {Function File} {} arrayfun (@var{func}, @var{A})\n\
+@deftypefnx {Function File} {@var{x} =} arrayfun (@var{func}, @var{A})\n\
+@deftypefnx {Function File} {@var{x} =} arrayfun (@var{func}, @var{A}, @var{b}, @dots{})\n\
+@deftypefnx {Function File} {[@var{x}, @var{y}, @dots{}] =} arrayfun (@var{func}, @var{A}, @dots{})\n\
+@deftypefnx {Function File} {} arrayfun (@dots{}, \"UniformOutput\", @var{val})\n\
+@deftypefnx {Function File} {} arrayfun (@dots{}, \"ErrorHandler\", @var{errfunc})\n\
+\n\
+Execute a function on each element of an array.  This is useful for\n\
+functions that do not accept array arguments.  If the function does\n\
+accept array arguments it is better to call the function directly.\n\
+\n\
+The first input argument @var{func} can be a string, a function\n\
+handle, an inline function, or an anonymous function.  The input\n\
+argument @var{A} can be a logic array, a numeric array, a string\n\
+array, a structure array, or a cell array.  By a call of the function\n\
+@command{arrayfun} all elements of @var{A} are passed on to the named\n\
+function @var{func} individually.\n\
+\n\
+The named function can also take more than two input arguments, with\n\
+the input arguments given as third input argument @var{b}, fourth\n\
+input argument @var{c}, @dots{}  If given more than one array input\n\
+argument then all input arguments must have the same sizes, for\n\
+example:\n\
+\n\
+@example\n\
+@group\n\
+arrayfun (@@atan2, [1, 0], [0, 1])\n\
+@result{} ans = [1.5708   0.0000]\n\
+@end group\n\
+@end example\n\
+\n\
+If the parameter @var{val} after a further string input argument\n\
+\"UniformOutput\" is set @code{true} (the default), then the named\n\
+function @var{func} must return a single element which then will be\n\
+concatenated into the return value and is of type matrix.  Otherwise,\n\
+if that parameter is set to @code{false}, then the outputs are\n\
+concatenated in a cell array.  For example:\n\
+\n\
+@example\n\
+@group\n\
+arrayfun (@@(x,y) x:y, \"abc\", \"def\", \"UniformOutput\", false)\n\
+@result{} ans =\n\
+@{\n\
+  [1,1] = abcd\n\
+  [1,2] = bcde\n\
+  [1,3] = cdef\n\
+@}\n\
+@end group\n\
+@end example\n\
+\n\
+If more than one output arguments are given then the named function\n\
+must return the number of return values that also are expected, for\n\
+example:\n\
+\n\
+@example\n\
+@group\n\
+[A, B, C] = arrayfun (@@find, [10; 0], \"UniformOutput\", false)\n\
+@result{}\n\
+A =\n\
+@{\n\
+  [1,1] =  1\n\
+  [2,1] = [](0x0)\n\
+@}\n\
+B =\n\
+@{\n\
+  [1,1] =  1\n\
+  [2,1] = [](0x0)\n\
+@}\n\
+C =\n\
+@{\n\
+  [1,1] =  10\n\
+  [2,1] = [](0x0)\n\
+@}\n\
+@end group\n\
+@end example\n\
+\n\
+If the parameter @var{errfunc} after a further string input argument\n\
+\"ErrorHandler\" is another string, a function handle, an inline\n\
+function, or an anonymous function, then @var{errfunc} defines a\n\
+function to call in the case that @var{func} generates an error.\n\
+The definition of the function must be of the form\n\
+\n\
+@example\n\
+function [@dots{}] = errfunc (@var{s}, @dots{})\n\
+@end example\n\
+\n\
+@noindent\n\
+where there is an additional input argument to @var{errfunc}\n\
+relative to @var{func}, given by @var{s}.  This is a structure with\n\
+the elements \"identifier\", \"message\", and \"index\" giving,\n\
+respectively, the error identifier, the error message, and the index of\n\
+the array elements that caused the error.  The size of the output\n\
+argument of @var{errfunc} must have the same size as the output\n\
+argument of @var{func}, otherwise a real error is thrown.  For\n\
+example:\n\
+\n\
+@example\n\
+@group\n\
+function y = ferr (s, x), y = \"MyString\"; endfunction\n\
+arrayfun (@@str2num, [1234], \\n\
+          \"UniformOutput\", false, \"ErrorHandler\", @@ferr)\n\
+@result{} ans =\n\
+@{\n\
+ [1,1] = MyString\n\
+@}\n\
+@end group\n\
+@end example\n\
+\n\
+@seealso{spfun, cellfun, structfun}\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+  int nargin = args.length ();
+  int nargout1 = (nargout < 1 ? 1 : nargout);
+
+  if (nargin < 2)
+    {
+      error ("arrayfun: function requires at least 2 arguments");
+      print_usage ();
+      return retval;
+    }
+
+  octave_value func = args(0);
+  bool symbol_table_lookup = false;
+
+  if (func.is_string ())
+    {
+      // See if we can convert the string into a function.
+
+      std::string name = args(0).string_value ();
+
+      if (! valid_identifier (name))
+        {
+          std::string fcn_name = unique_symbol_name ("__arrayfun_fcn_");
+          std::string fname = "function y = " + fcn_name + "(x) y = ";
+
+          octave_function *ptr_func
+            = extract_function (args(0), "arrayfun", fcn_name,
+                                fname, "; endfunction");
+
+          if (ptr_func && ! error_state)
+            func = octave_value (ptr_func, true);
+        }
+      else
+        {
+          func = symbol_table::find_function (name);
+
+          if (func.is_undefined ())
+            error ("arrayfun: invalid function NAME: %s", name.c_str ());
+
+          symbol_table_lookup = true;
+        }
+
+      if (error_state)
+        return retval;
+    }
+
+  if (func.is_function_handle () || func.is_inline_function ()
+      || func.is_function ())
+    {
+      // The following is an optimisation because the symbol table can
+      // give a more specific function class, so this can result in
+      // fewer polymorphic function calls as the function gets called
+      // for each value of the array.
+
+      if (! symbol_table_lookup )
+        {
+          if (func.is_function_handle ())
+            {
+              octave_fcn_handle* f = func.fcn_handle_value ();
+
+              // Overloaded function handles need to check the type of
+              // the arguments for each element of the array, so they
+              // cannot be optimised this way.
+
+              if (f -> is_overloaded ())
+                goto nevermind;
+            }
+          octave_value f = symbol_table::find_function (func.function_value ()
+                                                         -> name ());
+          if (f.is_defined ())
+            func = f;
+        }
+
+    nevermind:
+
+      bool uniform_output = true;
+      octave_value error_handler;
+      
+      get_mapper_fun_options (args, nargin, uniform_output, error_handler);
+
+      if (error_state)
+        return octave_value_list ();
+
+      octave_value_list inputlist (nargin, octave_value ());
+
+      OCTAVE_LOCAL_BUFFER (octave_value, inputs, nargin);
+      OCTAVE_LOCAL_BUFFER (bool, mask, nargin);
+
+      octave_idx_type k = 1;
+
+      dim_vector fdims (1, 1);
+
+      // Collect arguments.  Pre-fill scalar elements of inputlist
+      // array.
+
+      for (int j = 0; j < nargin; j++)
+        {
+          inputs[j] = args(j+1);
+          mask[j] = inputs[j].numel () != 1;
+
+          if (! mask[j])
+            inputlist(j) = inputs[j];
+        }
+
+      for (int j = 0; j < nargin; j++)
+        {
+          if (mask[j])
+            {
+              fdims = inputs[j].dims ();
+              k = inputs[j].numel ();
+
+              for (int i = j+1; i < nargin; i++)
+                {
+                  if (mask[i] && inputs[i].dims () != fdims)
+                    {
+                      error ("arrayfun: dimensions mismatch");
+                      return retval;
+                    }
+                }
+              break;
+            }
+        }
+
+
+      unwind_protect frame;
+      frame.protect_var (buffer_error_messages);
+
+      if (error_handler.is_defined ())
+        buffer_error_messages++;
+
+      // Apply functions.
+
+      if (uniform_output)
+        {
+          std::list<octave_value_list> idx_list (1);
+          idx_list.front ().resize (1);
+          std::string idx_type = "(";
+
+          OCTAVE_LOCAL_BUFFER (octave_value, retv, nargout1);
+
+          for (octave_idx_type count = 0; count < k; count++)
+            {
+              idx_list.front ()(0) = count + 1.0;
+
+              for (int j = 0; j < nargin; j++)
+                {
+                  if (mask[j])
+                    inputlist.xelem (j) = inputs[j].do_index_op (idx_list);
+
+                  if (error_state)
+                    return retval;
+                }
+
+              const octave_value_list tmp
+                = get_output_list (count, nargout, inputlist, func,
+                                   error_handler);
+
+              if (error_state)
+                return retval;
+
+              if (nargout > 0 && tmp.length () < nargout)
+                {
+                  error ("arrayfun: function returned fewer than nargout values");
+                  return retval;
+                }
+
+              if  (nargout > 0
+                   || (nargout == 0
+                       && tmp.length () > 0 && tmp(0).is_defined ()))
+                {
+                  int num_to_copy = tmp.length ();
+
+                  if (num_to_copy > nargout1)
+                    num_to_copy = nargout1;
+
+                  if (count == 0)
+                    {
+                      for (int j = 0; j < num_to_copy; j++)
+                        {
+                          if (tmp(j).is_defined ())
+                            {
+                              octave_value val = tmp(j);
+
+                              if (val.numel () == 1)
+                                retv[j] = val.resize (fdims);
+                              else
+                                {
+                                  error ("arrayfun: all values must be scalars when UniformOutput = true");
+                                  break;
+                                }
+                            }
+                        }
+                    }
+                  else
+                    {
+                      for (int j = 0; j < num_to_copy; j++)
+                        {
+                          if (tmp(j).is_defined ())
+                            {
+                              octave_value val = tmp(j);
+
+                              if (! retv[j].fast_elem_insert (count, val))
+                                {
+                                  if (val.numel () == 1)
+                                    {
+                                      idx_list.front ()(0) = count + 1.0;
+                                      retv[j].assign (octave_value::op_asn_eq,
+                                                      idx_type, idx_list, val);
+
+                                      if (error_state)
+                                        break;
+                                    }
+                                  else
+                                    {
+                                      error ("arrayfun: all values must be scalars when UniformOutput = true");
+                                      break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+              if (error_state)
+                break;
+            }
+
+          retval.resize (nargout1);
+
+          for (int j = 0; j < nargout1; j++)
+            {
+              if (nargout > 0 && retv[j].is_undefined ())
+                retval(j) = NDArray (fdims);
+              else
+                retval(j) = retv[j];
+            }
+        }
+      else
+        {
+          std::list<octave_value_list> idx_list (1);
+          idx_list.front ().resize (1);
+          std::string idx_type = "(";
+
+          OCTAVE_LOCAL_BUFFER (Cell, results, nargout1);
+
+          for (int j = 0; j < nargout1; j++)
+            results[j].resize (fdims, Matrix ());
+
+          bool have_some_output = false;
+
+          for (octave_idx_type count = 0; count < k; count++)
+            {
+              idx_list.front ()(0) = count + 1.0;
+
+              for (int j = 0; j < nargin; j++)
+                {
+                  if (mask[j])
+                    inputlist.xelem (j) = inputs[j].do_index_op (idx_list);
+
+                  if (error_state)
+                    return retval;
+                }
+
+              const octave_value_list tmp
+                = get_output_list (count, nargout, inputlist, func,
+                                   error_handler);
+
+              if (error_state)
+                return retval;
+
+              if (nargout > 0 && tmp.length () < nargout)
+                {
+                  error ("arrayfun: function returned fewer than nargout values");
+                  return retval;
+                }
+
+              if  (nargout > 0
+                   || (nargout == 0
+                       && tmp.length () > 0 && tmp(0).is_defined ()))
+                {
+                  int num_to_copy = tmp.length ();
+
+                  if (num_to_copy > nargout1)
+                    num_to_copy = nargout1;
+
+                  if (num_to_copy > 0)
+                    have_some_output = true;
+
+                  for (int j = 0; j < num_to_copy; j++)
+                    results[j](count) = tmp(j);
+                }
+            }
+
+          if (have_some_output || fdims.any_zero ())
+            {
+              retval.resize (nargout1);
+
+              for (int j = 0; j < nargout1; j++)
+                retval(j) = results[j];
+            }
+        }
+    }
+  else
+    error ("arrayfun: argument NAME must be a string or function handle");
+
+  return retval;
+}
+
+/*
+%!function r = f11 (x)
+%!  global __arrayfun_test_num_outputs__
+%!  __arrayfun_test_num_outputs__ = nargout;
+%!  r = x;
+%! endfunction
+
+%!function f01 (x)
+%!  global __arrayfun_test_num_outputs__
+%!  __arrayfun_test_num_outputs__ = nargout;
+%! endfunction
+
+%!test
+%! global __arrayfun_test_num_outputs__
+%! arrayfun (@f11, {1});
+%! assert (__arrayfun_test_num_outputs__, 0)
+%! x = arrayfun (@f11, {1});
+%! assert (__arrayfun_test_num_outputs__, 1)
+
+%!test
+%! global __arrayfun_test_num_outputs__
+%! arrayfun (@f01, {1});
+%! assert (__arrayfun_test_num_outputs__, 0)
+
+%!error x = arrayfun (@f01, [1, 2]);
+
+%!test
+%! assert (arrayfun (@f11, [1, 2]), [1, 2])
+%! assert (arrayfun (@f11, [1, 2], 'uniformoutput', false), {1, 2});
+%! assert (arrayfun (@f11, {1, 2}), {1, 2})
+%! assert (arrayfun (@f11, {1, 2}, 'uniformoutput', false), {{1}, {2}});
+
+%!assert (arrayfun (@ones, 1, [2,3], 'uniformoutput', false), {[1,1], [1,1,1]});
+
+%% Test function to check the "Errorhandler" option
+%!function [z] = arrayfunerror (S, varargin)
+%!      z = S;
+%!    endfunction
+%% First input argument can be a string, an inline function, a
+%% function_handle or an anonymous function
+%!test
+%!  arrayfun (@isequal, [false, true], [true, true]); %% No output argument
+%!error
+%!  arrayfun (@isequal); %% One or less input arguments
+%!test
+%!  A = arrayfun ("isequal", [false, true], [true, true]);
+%!  assert (A, [false, true]);
+%!test
+%!  A = arrayfun (inline ("(x == y)", "x", "y"), [false, true], [true, true]);
+%!  assert (A, [false, true]);
+%!test
+%!  A = arrayfun (@isequal, [false, true], [true, true]);
+%!  assert (A, [false, true]);
+%!test
+%!  A = arrayfun (@(x,y) isequal(x,y), [false, true], [true, true]);
+%!  assert (A, [false, true]);
+
+%% Number of input and output arguments may be greater than one
+%#!test
+%!  A = arrayfun (@(x) islogical (x), false);
+%!  assert (A, true);
+%!test
+%!  A = arrayfun (@(x,y,z) x + y + z, [1, 1, 1], [2, 2, 2], [3, 4, 5]);
+%!  assert (A, [6, 7, 8], 1e-16);
+%!test %% Two input arguments of different types
+%!  A = arrayfun (@(x,y) islogical (x) && ischar (y), false, "a");
+%!  assert (A, true);
+%!test %% Pass another variable to the anonymous function
+%!  y = true; A = arrayfun (@(x) islogical (x && y), false);
+%!  assert (A, true);
+%!test %% Three ouptut arguments of different type
+%!  [A, B, C] = arrayfun (@find, [10, 11; 0, 12], "UniformOutput", false);
+%!  assert (isequal (A, {true, true; [], true}));
+%!  assert (isequal (B, {true, true; [], true}));
+%!  assert (isequal (C, {10, 11; [], 12}));
+
+%% Input arguments can be of type logical
+%!test
+%!  A = arrayfun (@(x,y) x == y, [false, true], [true, true]);
+%!  assert (A, [false, true]);
+%!test
+%!  A = arrayfun (@(x,y) x == y, [false; true], [true; true], "UniformOutput", true);
+%!  assert (A, [false; true]);
+%!test
+%!  A = arrayfun (@(x) x, [false, true, false, true], "UniformOutput", false);
+%!  assert (A, {false, true, false, true});
+%!test %% Three ouptut arguments of same type
+%!  [A, B, C] = arrayfun (@find, [true, false; false, true], "UniformOutput", false);
+%!  assert (isequal (A, {true, []; [], true}));
+%!  assert (isequal (B, {true, []; [], true}));
+%!  assert (isequal (C, {true, []; [], true}));
+%!test
+%!  A = arrayfun (@(x,y) array2str (x,y), true, true, "ErrorHandler", @arrayfunerror);
+%!  assert (isfield (A, "identifier"), true);
+%!  assert (isfield (A, "message"), true);
+%!  assert (isfield (A, "index"), true);
+%!  assert (isempty (A.message), false);
+%!  assert (A.index, 1);
+%!test %% Overwriting setting of "UniformOutput" true
+%!  A = arrayfun (@(x,y) array2str (x,y), true, true, \
+%!                "UniformOutput", true, "ErrorHandler", @arrayfunerror);
+%!  assert (isfield (A, "identifier"), true);
+%!  assert (isfield (A, "message"), true);
+%!  assert (isfield (A, "index"), true);
+%!  assert (isempty (A.message), false);
+%!  assert (A.index, 1);
+
+%% Input arguments can be of type numeric
+%!test
+%!  A = arrayfun (@(x,y) x>y, [1.1, 4.2], [3.1, 2+3*i]);
+%!  assert (A, [false, true]);
+%!test
+%!  A = arrayfun (@(x,y) x>y, [1.1, 4.2; 2, 4], [3.1, 2; 2, 4+2*i], "UniformOutput", true);
+%!  assert (A, [false, true; false, false]);
+%!test
+%!  A = arrayfun (@(x,y) x:y, [1.1, 4], [3.1, 6], "UniformOutput", false);
+%!  assert (isequal (A{1}, [1.1, 2.1, 3.1]));
+%!  assert (isequal (A{2}, [4, 5, 6]));
+%!test %% Three ouptut arguments of different type
+%!  [A, B, C] = arrayfun (@find, [10, 11; 0, 12], "UniformOutput", false);
+%!  assert (isequal (A, {true, true; [], true}));
+%!  assert (isequal (B, {true, true; [], true}));
+%!  assert (isequal (C, {10, 11; [], 12}));
+%!test
+%!  A = arrayfun (@(x,y) array2str(x,y), {1.1, 4}, {3.1, 6}, "ErrorHandler", @arrayfunerror);
+%!  B = isfield (A(1), "message") && isfield (A(1), "index");
+%!  assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
+%!  assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
+%!  assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
+%!  assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
+%!  assert ([A(1).index, A(2).index], [1, 2]);
+%!test %% Overwriting setting of "UniformOutput" true
+%!  A = arrayfun (@(x,y) array2str(x,y), {1.1, 4}, {3.1, 6}, \
+%!                "UniformOutput", true, "ErrorHandler", @arrayfunerror);
+%!  B = isfield (A(1), "message") && isfield (A(1), "index");
+%!  assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
+%!  assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
+%!  assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
+%!  assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
+%!  assert ([A(1).index, A(2).index], [1, 2]);
+
+%% Input arguments can be of type character or strings
+%!test
+%!  A = arrayfun (@(x,y) x>y, ["ad", "c", "ghi"], ["cc", "d", "fgh"]);
+%!  assert (A, [false, true, false, true, true, true]);
+%!test
+%!  A = arrayfun (@(x,y) x>y, ["a"; "f"], ["c"; "d"], "UniformOutput", true);
+%!  assert (A, [false; true]);
+%!test
+%!  A = arrayfun (@(x,y) x:y, ["a", "d"], ["c", "f"], "UniformOutput", false);
+%!  assert (A, {"abc", "def"});
+%! %#!test
+%!   A = arrayfun (@(x,y) cell2str(x,y), ["a", "d"], ["c", "f"], "ErrorHandler", @arrayfunerror);
+%!   B = isfield (A(1), "identifier") && isfield (A(1), "message") && isfield (A(1), "index");
+%!   assert (B, true);
+
+%% Input arguments can be of type structure
+%!test
+%!  a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2);
+%!  A = arrayfun (@(x,y) (x.a < y.a) && (x.b > y.b), a, b);
+%!  assert (A, true);
+%!test
+%!  a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2);
+%!  A = arrayfun (@(x,y) (x.a < y.a) && (x.b > y.b), a, b, "UniformOutput", true);
+%!  assert (A, true);
+%!test
+%!  a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2);
+%!  A = arrayfun (@(x,y) x.a:y.a, a, b, "UniformOutput", false);
+%!  assert (isequal (A, {[1.1, 2.1, 3.1]}));
+%!test
+%!  A = arrayfun (@(x) mat2str(x), "a", "ErrorHandler", @arrayfunerror);
+%!  assert (isfield (A, "identifier"), true);
+%!  assert (isfield (A, "message"), true);
+%!  assert (isfield (A, "index"), true);
+%!  assert (isempty (A.message), false);
+%!  assert (A.index, 1);
+%!test %% Overwriting setting of "UniformOutput" true
+%!  A = arrayfun (@(x) mat2str(x), "a", "UniformOutput", true, \
+%!                "ErrorHandler", @arrayfunerror);
+%!  assert (isfield (A, "identifier"), true);
+%!  assert (isfield (A, "message"), true);
+%!  assert (isfield (A, "index"), true);
+%!  assert (isempty (A.message), false);
+%!  assert (A.index, 1);
+
+%% Input arguments can be of type cell array
+%!test
+%!  A = arrayfun (@(x,y) x{1} < y{1}, {1.1, 4.2}, {3.1, 2});
+%!  assert (A, [true, false]);
+%!test
+%!  A = arrayfun (@(x,y) x{1} < y{1}, {1.1; 4.2}, {3.1; 2}, "UniformOutput", true);
+%!  assert (A, [true; false]);
+%!test
+%!  A = arrayfun (@(x,y) x{1} < y{1}, {1.1, 4.2}, {3.1, 2}, "UniformOutput", false);
+%!  assert (A, {true, false});
+%!test
+%!  A = arrayfun (@(x,y) num2str(x,y), {1.1, 4.2}, {3.1, 2}, "ErrorHandler", @arrayfunerror);
+%!  assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
+%!  assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
+%!  assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
+%!  assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
+%!  assert ([A(1).index, A(2).index], [1, 2]);
+%!test
+%!  A = arrayfun (@(x,y) num2str(x,y), {1.1, 4.2}, {3.1, 2}, \
+%!                "UniformOutput", true, "ErrorHandler", @arrayfunerror);
+%!  assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))], [true, true]);
+%!  assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))], [true, true]);
+%!  assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))], [true, true]);
+%!  assert ([(isempty (A(1).message)), (isempty (A(2).message))], [false, false]);
+%!  assert ([A(1).index, A(2).index], [1, 2]);
+*/
+
 static void
 do_num2cell_helper (const dim_vector& dv,
                     const Array<int>& dimv,