octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: mapper functions vs. cell arrays


From: David Bateman
Subject: Re: mapper functions vs. cell arrays
Date: Sat, 02 Sep 2006 01:09:50 +0200
User-agent: Thunderbird 1.5.0.5 (X11/20060817)

John W. Eaton wrote:
> On  1-Sep-2006, David Bateman wrote:
> 
> | Thinking about this a bit more, the octave cellfun effectively assumes
> | that "UniformOutput" is false and so all results are given in a cell
> | array. "UniformOutput" being "true" equivalent in octave would be
> | 
> | cell2mat(cellfun (@upper, {'f', 'o', 'o'}))
> | 
> | So matlab just made the default output of cellfun different from octave
> | so the output is incompatible. If we want to make cellfun compatible
> | then the want we might do is make cell2mat and oct-file in cellfun.cc
> | and call it on the outputs if "UniformOutput" is "true".
> | 
> | I'll make a cellfun that is matlab compatible if you want it... I'll do
> | it after work today...
> 
> Yes, if you have time and would like to do it that would be great.
> 
> Thanks,
> 
> jwe
> 

Ok, then what about something like the attached patch. The only thing I
don't like about this is the use of feval. This shouldn't be needed as
we can assume that all elements are 1x1. However, we need to treat
arbitrary array return types and so its not evident how exact to replace it.

Note I also had to export the Vlast_error_message and Vlast_error_id
from error.cc, to allow the erorr handler to be implemented correctly.
It appears to be matlab compatible with the tests I added.

Regards
David
? Root
? Root.public
? Root.ssh
? autom4te.cache
? config.h.in
? configure
? patch
? run-octave
? examples/mystruct.mex
? scripts/ChangeLog.flc
? scripts/autom4te.cache
? scripts/configure
? scripts/pkg/patch
? src/DLD-FUNCTIONS/sparse.cc.flc
Index: src/error.cc
===================================================================
RCS file: /cvs/octave/src/error.cc,v
retrieving revision 1.112
diff -c -r1.112 error.cc
*** src/error.cc        26 Jul 2006 17:19:10 -0000      1.112
--- src/error.cc        1 Sep 2006 22:51:31 -0000
***************
*** 74,80 ****
  static Octave_map warning_options;
  
  // The text of the last error message.
! static std::string Vlast_error_message;
  
  // The text of the last warning message.
  static std::string Vlast_warning_message;
--- 74,80 ----
  static Octave_map warning_options;
  
  // The text of the last error message.
! std::string Vlast_error_message;
  
  // The text of the last warning message.
  static std::string Vlast_warning_message;
***************
*** 83,89 ****
  static std::string Vlast_warning_id;
  
  // The last error message id.
! static std::string Vlast_error_id;
  
  // Current error state.
  //
--- 83,89 ----
  static std::string Vlast_warning_id;
  
  // The last error message id.
! std::string Vlast_error_id;
  
  // Current error state.
  //
Index: src/error.h
===================================================================
RCS file: /cvs/octave/src/error.h,v
retrieving revision 1.39
diff -c -r1.39 error.h
*** src/error.h 26 Jul 2006 17:19:10 -0000      1.39
--- src/error.h 1 Sep 2006 22:51:31 -0000
***************
*** 63,68 ****
--- 63,74 ----
  extern void disable_warning (const std::string& id);
  extern void initialize_default_warning_state (void);
  
+ // The text of the last error message.
+ extern std::string Vlast_error_message;
+ 
+ // The last error message id.
+ extern std::string Vlast_error_id;
+ 
  // Current error state.
  extern int error_state;
  
Index: src/DLD-FUNCTIONS/cellfun.cc
===================================================================
RCS file: /cvs/octave/src/DLD-FUNCTIONS/cellfun.cc,v
retrieving revision 1.9
diff -c -r1.9 cellfun.cc
*** src/DLD-FUNCTIONS/cellfun.cc        23 May 2006 18:34:10 -0000      1.9
--- src/DLD-FUNCTIONS/cellfun.cc        1 Sep 2006 22:51:32 -0000
***************
*** 26,46 ****
  #endif
  
  #include <string>
  
  #include "lo-mappers.h"
  
  #include "Cell.h"
  #include "defun-dld.h"
  #include "parse.h"
  #include "variables.h"
  #include "ov-colon.h"
  
! DEFUN_DLD (cellfun, args, ,
    " -*- texinfo -*-\n\
  @deftypefn {Lodable Function} {} cellfun (@var{name}, @var{c})\n\
  @deftypefnx {Lodable Function} {} cellfun (\"size\", @var{c}, @var{k})\n\
  @deftypefnx {Lodable Function} {} cellfun (\"isclass\", @var{c}, 
@var{class})\n\
  @deftypefnx {Lodable Function} {} cellfun (@var{func}, @var{c})\n\
  \n\
  Evaluate the function named @var{name} on the elements of the cell array\n\
  @var{c}.  Elements in @var{c} are passed on to the named function\n\
--- 26,53 ----
  #endif
  
  #include <string>
+ #include <vector>
  
  #include "lo-mappers.h"
  
  #include "Cell.h"
+ #include "oct-map.h"
  #include "defun-dld.h"
  #include "parse.h"
  #include "variables.h"
  #include "ov-colon.h"
+ #include "unwind-prot.h"
  
! DEFUN_DLD (cellfun, args, nargout,
    " -*- texinfo -*-\n\
  @deftypefn {Lodable Function} {} cellfun (@var{name}, @var{c})\n\
  @deftypefnx {Lodable Function} {} cellfun (\"size\", @var{c}, @var{k})\n\
  @deftypefnx {Lodable Function} {} cellfun (\"isclass\", @var{c}, 
@var{class})\n\
  @deftypefnx {Lodable Function} {} cellfun (@var{func}, @var{c})\n\
+ @deftypefnx {Lodable Function} {} cellfun (@var{func}, @var{c}, @var{d})\n\
+ @deftypefnx {Lodable Function} address@hidden, @var{b}]} = cellfun 
(@dots{})\n\
+ @deftypefnx {Lodable Function} {} cellfun (@dots{}, 
'ErrorHandler',@var{errfunc})\n\
+ @deftypefnx {Lodable Function} {} cellfun (@dots{}, 
'UniformOutput',@var{val})\n\
  \n\
  Evaluate the function named @var{name} on the elements of the cell array\n\
  @var{c}.  Elements in @var{c} are passed on to the named function\n\
***************
*** 67,92 ****
  \n\
  Additionally, @code{cellfun} accepts an arbitrary function @var{func}\n\
  in the form of an inline function, function handle, or the name of a\n\
! function (in a character string).  The function should take a single\n\
! argument and return a single value, and in the case of a character string\n\
! argument, the argument must be named @var{x}.  For example\n\
  \n\
  @example\n\
  @group\n\
! cellfun (\"tolower(x)\", @{\"Foo\", \"Bar\", \"FooBar\"@})\n\
  @result{} ans = @{\"foo\", \"bar\", \"foobar\"@}\n\
  @end group\n\
  @end example\n\
  @seealso{isempty, islogical, isreal, length, ndims, numel, size, isclass}\n\
  @end deftypefn")
  {
!   octave_value retval;
  
    std::string name = "function";
  
    octave_function *func = 0;
  
    int nargin = args.length ();
  
    if (nargin < 2)
      {
--- 74,138 ----
  \n\
  Additionally, @code{cellfun} accepts an arbitrary function @var{func}\n\
  in the form of an inline function, function handle, or the name of a\n\
! function (in a character string). In the case of a character string\n\
! argument, the argument must be named @var{x}. The function can take one\n\
! or more arguments, with the inputs args given by @var{c}, @var{d}, etc.\n\
! Equally the function can return one or more output arguments. For example\n\
  \n\
  @example\n\
  @group\n\
! cellfun (@@atan2, @{1, address@hidden, @{0, address@hidden)\n\
! @result{}ans = [1.57080   0.00000]\n\
! @end group\n\
! @end example\n\
! \n\
! Note that the default output argument is an array of the same size as the\n\
! input arguments.\n\
! \n\
! If the param 'UniformOutput' is set to true (the default), then the 
function\n\
! must return either a single element which will be concatenated into the\n\
! return value. If 'UniformOutput is false, the outputs are concatenated in\n\
! a cell array. For example\n\
! \n\
! @example\n\
! @group\n\
! cellfun (\"tolower(x)\", @{\"Foo\", \"Bar\", 
\"FooBar\"@},'UniformOutput',false)\n\
  @result{} ans = @{\"foo\", \"bar\", \"foobar\"@}\n\
  @end group\n\
  @end example\n\
+ \n\
+ Given the parameter 'ErrorHandler', then @var{errfunc} defines a function 
to\n\
+ call in case @var{func} generates an error. The form of the function is\n\
+ \n\
+ @example\n\
+ function address@hidden = errfunc (@var{s}, @dots{})\n\
+ @end example\n\
+ \n\
+ where there is an additional input argument to @var{errfunc} relative to\n\
+ @var{func}, given by @var{s}. This is a structcure with the elements\n\
+ 'identifier', 'message' and 'index', giving respectively the error\n\
+ identifier, the error message, and the index into the input arguments\n\
+ of the element that caused the error. For example\n\
+ \n\
+ @example\n\
+ @group\n\
+ function y = foo (s, x), y = NaN; endfunction\n\
+ cellfun (@@factorial, @{-1,address@hidden,'ErrorHandler',@@foo)\n\
+ @result{} ans = [NaN 2]\n\
+ @end group\n\
+ @end example\n\
+ \n\
  @seealso{isempty, islogical, isreal, length, ndims, numel, size, isclass}\n\
  @end deftypefn")
  {
!   octave_value_list retval;
  
    std::string name = "function";
  
    octave_function *func = 0;
  
    int nargin = args.length ();
+   nargout = (nargout < 1 ? 1 : nargout);
  
    if (nargin < 2)
      {
***************
*** 119,167 ****
    
    Cell f_args = args(1).cell_value ();
    
!   int k = f_args.numel ();
  
    if (name == "isempty")
      {      
        boolNDArray result (f_args.dims ());
!       for (int count = 0; count < k ; count++)
          result(count) = f_args.elem(count).is_empty ();
!       retval = result;
      }
    else if (name == "islogical")
      {
        boolNDArray result (f_args.dims ());
!       for (int  count= 0; count < k ; count++)
          result(count) = f_args.elem(count).is_bool_type ();
!       retval = result;
      }
    else if (name == "isreal")
      {
        boolNDArray result (f_args.dims ());
!       for (int  count= 0; count < k ; count++)
          result(count) = f_args.elem(count).is_real_type ();
!       retval = result;
      }
    else if (name == "length")
      {
        NDArray result (f_args.dims ());
!       for (int  count= 0; count < k ; count++)
          result(count) = static_cast<double> (f_args.elem(count).length ());
!       retval = result;
      }
    else if (name == "ndims")
      {
        NDArray result (f_args.dims ());
!       for (int count = 0; count < k ; count++)
          result(count) = static_cast<double> (f_args.elem(count).ndims ());
!       retval = result;
      }
    else if (name == "prodofsize")
      {
        NDArray result (f_args.dims ());
!       for (int count = 0; count < k ; count++)
          result(count) = static_cast<double> (f_args.elem(count).numel ());
!       retval = result;
      }
    else if (name == "size")
      {
--- 165,213 ----
    
    Cell f_args = args(1).cell_value ();
    
!   octave_idx_type k = f_args.numel ();
  
    if (name == "isempty")
      {      
        boolNDArray result (f_args.dims ());
!       for (octave_idx_type count = 0; count < k ; count++)
          result(count) = f_args.elem(count).is_empty ();
!       retval(0) = result;
      }
    else if (name == "islogical")
      {
        boolNDArray result (f_args.dims ());
!       for (octave_idx_type  count= 0; count < k ; count++)
          result(count) = f_args.elem(count).is_bool_type ();
!       retval(0) = result;
      }
    else if (name == "isreal")
      {
        boolNDArray result (f_args.dims ());
!       for (octave_idx_type  count= 0; count < k ; count++)
          result(count) = f_args.elem(count).is_real_type ();
!       retval(0) = result;
      }
    else if (name == "length")
      {
        NDArray result (f_args.dims ());
!       for (octave_idx_type  count= 0; count < k ; count++)
          result(count) = static_cast<double> (f_args.elem(count).length ());
!       retval(0) = result;
      }
    else if (name == "ndims")
      {
        NDArray result (f_args.dims ());
!       for (octave_idx_type count = 0; count < k ; count++)
          result(count) = static_cast<double> (f_args.elem(count).ndims ());
!       retval(0) = result;
      }
    else if (name == "prodofsize")
      {
        NDArray result (f_args.dims ());
!       for (octave_idx_type count = 0; count < k ; count++)
          result(count) = static_cast<double> (f_args.elem(count).numel ());
!       retval(0) = result;
      }
    else if (name == "size")
      {
***************
*** 175,181 ****
          if (!error_state)
              {
                NDArray result (f_args.dims ());
!               for (int count = 0; count < k ; count++)
                  {
                    dim_vector dv = f_args.elem(count).dims ();
                    if (d < dv.length ())
--- 221,227 ----
          if (!error_state)
              {
                NDArray result (f_args.dims ());
!               for (octave_idx_type count = 0; count < k ; count++)
                  {
                    dim_vector dv = f_args.elem(count).dims ();
                    if (d < dv.length ())
***************
*** 183,189 ****
                    else
                    result(count) = 1.0;
                  }
!               retval = result;
              }
          }
        else
--- 229,235 ----
                    else
                    result(count) = 1.0;
                  }
!               retval(0) = result;
              }
          }
        else
***************
*** 195,210 ****
          {
            std::string class_name = args(2).string_value();
            boolNDArray result (f_args.dims ());
!           for (int count = 0; count < k ; count++)
              result(count) = (f_args.elem(count).class_name() == class_name);
            
!           retval = result;
          }
        else
          error ("not enough arguments for `isclass'");
      }
    else 
      {
        std::string fcn_name;
        
        if (! func)
--- 241,257 ----
          {
            std::string class_name = args(2).string_value();
            boolNDArray result (f_args.dims ());
!           for (octave_idx_type count = 0; count < k ; count++)
              result(count) = (f_args.elem(count).class_name() == class_name);
            
!           retval(0) = result;
          }
        else
          error ("not enough arguments for `isclass'");
      }
    else 
      {
+       int orig_buffer_error_messages = buffer_error_messages;
        std::string fcn_name;
        
        if (! func)
***************
*** 221,249 ****
        error ("unknown function");
        else
        {
!         Cell result (f_args.dims ());
  
!           for (int count = 0; count < k ; count++)
            {
              octave_value_list tmp
!               = func->do_multi_index_op (1, f_args.elem (count));
!             result(count) = tmp(0);
  
              if (error_state)
                break;
            }
  
          if (! error_state)
!           retval = result;
  
          if (! fcn_name.empty ())
            clear_function (fcn_name);
        }
      }
  
    return retval;
  }
  
  DEFUN_DLD (num2cell, args, ,
    "-*- texinfo -*-\n\
  @deftypefn {Loadable Function} address@hidden =} num2cell (@var{m})\n\
--- 268,465 ----
        error ("unknown function");
        else
        {
!         octave_value_list inputlist;
!         octave_value_list errlist;
!         bool UniformOutput = true;
!         bool haveErrorHandler = false;
!         std::string err_name;
!         octave_function *ErrorHandler;
!         int offset = 1;
!         int i = 1;
!         OCTAVE_LOCAL_BUFFER (Cell, result, nargout);
!         OCTAVE_LOCAL_BUFFER (Cell, inputs, nargin);
!         while (i < nargin)
!           {
!             if (args(i).is_string())
!               {
!                 std::string arg = args(i++).string_value();
!                 if (i == nargin)
!                   {
!                     error ("cellfun: parameter value is missing");
!                     goto cellfun_err;
!                   }
! 
!                 std::transform (arg.begin (), arg.end (), 
!                                 arg.begin (), tolower);
! 
!                 if (arg == "uniformoutput")
!                   UniformOutput = args(i++).bool_value();
!                 else if (arg == "errorhandler")
!                   {
!                     if (args(i).is_function_handle () || 
!                         args(i).is_inline_function ())
!                       {
!                         ErrorHandler = args(i).function_value ();
! 
!                         if (error_state)
!                           goto cellfun_err;
!                       }
!                     else if (args(i).is_string ())
!                       {
!                         err_name = unique_symbol_name ("__cellfun_fcn_");
!                         std::string fname = "function y = ";
!                         fname.append (fcn_name);
!                         fname.append ("(x) y = ");
!                         ErrorHandler = extract_function (args(i), "cellfun", 
!                                                          err_name, fname,
!                                                          "; endfunction");
!                       }
! 
!                     if (!ErrorHandler)
!                       goto cellfun_err;
! 
!                     haveErrorHandler = true;
!                     i++;
!                   }
!                 else
!                   {
!                     error ("cellfun: unrecognized parameter %s", 
!                            arg.c_str());
!                     goto cellfun_err;
!                   }
!                 offset += 2;
!               }
!             else
!               {
!                 inputs[i-offset] = args(i).cell_value ();
!                 if (f_args.dims() != inputs[i-offset].dims())
!                   {
!                     error ("cellfun: Dimension mismatch");
!                     goto cellfun_err;
  
!                   }
!                 i++;
!               }
!           }
! 
!         for (int i = 0; i < nargout; i++)
!           result[i] = Cell (f_args.dims ());
! 
!         inputlist.resize(nargin-offset);
! 
!         if (haveErrorHandler)
!           buffer_error_messages = 1;
! 
!           for (octave_idx_type count = 0; count < k ; count++)
            {
+             for (int i = 0; i < nargin-offset; i++)
+               inputlist(i) = inputs[i](count);
+ 
              octave_value_list tmp
!               = func->do_multi_index_op (nargout, inputlist);
! 
!             if (error_state && haveErrorHandler)
!               {
!                 Octave_map msg;
!                 msg.assign ("identifier", Vlast_error_id);
!                 msg.assign ("message", Vlast_error_message);
!                 msg.assign ("index", octave_value(double (count)));
!                 octave_value_list errlist = inputlist;
!                 errlist.prepend (msg);
!                 buffer_error_messages = orig_buffer_error_messages;
!                 error_state = 0;
!                 tmp = ErrorHandler->do_multi_index_op (nargout, errlist);
!                 buffer_error_messages = 1;
! 
!                 if (error_state)
!                   goto cellfun_err;
!               }
! 
!             if (tmp.length() < nargout)
!               {
!                 error ("cellfun: too many output arguments");
!                 goto cellfun_err;
!               }
! 
!             for (int i = 0; i < nargout; i++)
!               result[i](count) = tmp(i);
  
              if (error_state)
                break;
            }
  
          if (! error_state)
!           {
!             retval.resize(nargout);
!             if (UniformOutput)
!               {
!                 // FIXME do this without an feval. This is simplified,
!                 // but the fact that all elements are supposed to be scalar
!                 for (int i = 0; i < nargout; i++)
!                   {
!                     octave_value_list tmp = 
!                       feval ("cell2mat", octave_value(result[i]), 1);
!                     retval(i) = tmp(0);
!                   }
!               }
!             else
!               {
!                 for (int i = 0; i < nargout; i++)
!                   retval(i) = result[i];
!               }
! 
!             if (error_state)
!               retval = octave_value_list();
!           }
! 
!       cellfun_err:
!         if (haveErrorHandler)
!           buffer_error_messages = orig_buffer_error_messages;
  
          if (! fcn_name.empty ())
            clear_function (fcn_name);
+ 
+         if (! err_name.empty ())
+           clear_function (err_name);
        }
      }
  
    return retval;
  }
  
+ /*
+ 
+ %!error(cellfun(1))
+ %!error(cellfun('isclass',1))
+ %!error(cellfun('size',1))
+ %!error(cellfun(@sin,{[]},'BadParam',false))
+ %!error(cellfun(@sin,{[]},'UniformOuput'))
+ %!error(cellfun(@sin,{[]},'ErrorHandler'))
+ %!assert(cellfun(@sin,{0,1}),sin([0,1]))
+ %!assert(cellfun(inline('sin(x)'),{0,1}),sin([0,1]))
+ %!assert(cellfun('sin',{0,1}),sin([0,1]))
+ %!assert(cellfun('isempty',{1,[]}),[false,true])
+ %!assert(cellfun('islogical',{false,pi}),[true,false])
+ %!assert(cellfun('isreal',{1i,1}),[false,true])
+ %!assert(cellfun('length',{zeros(2,2),1}),[2,1])
+ %!assert(cellfun('prodofsize',{zeros(2,2),1}),[4,1])
+ %!assert(cellfun('ndims',{zeros([2,2,2]),1}),[3,2])
+ %!assert(cellfun('isclass',{zeros([2,2,2]),'test'},'double'),[true,false])
+ %!assert(cellfun('size',{zeros([1,2,3]),1},1),[1,1])
+ %!assert(cellfun('size',{zeros([1,2,3]),1},2),[2,1])
+ %!assert(cellfun('size',{zeros([1,2,3]),1},3),[3,1])
+ %!assert(cellfun(@atan2,{1,1},{1,2}),[atan2(1,1),atan2(1,2)])
+ 
%!assert(cellfun(@atan2,{1,1},{1,2},'UniformOutput',false),{atan2(1,1),atan2(1,2)})
+ %!error(cellfun(@factorial,{-1,3}))
+ %!assert(cellfun(@factorial,{-1,3},'ErrorHandler',@(x,y) NaN),[NaN,6])
+ %!test
+ %! [a,b,c]=cellfun(@fileparts,{'/a/b/c.d','/e/f/g.h'},'UniformOutput',false);
+ %! assert(a,{'/a/b','/e/f'})
+ %! assert(b,{'c','g'})
+ %! assert(c,{'.d','.h'})
+ 
+ */
+ 
  DEFUN_DLD (num2cell, args, ,
    "-*- texinfo -*-\n\
  @deftypefn {Loadable Function} address@hidden =} num2cell (@var{m})\n\
***************
*** 271,277 ****
          sings.resize (dsings.length());
  
          if (!error_state)
!           for (int i = 0; i < dsings.length(); i++)
              if (dsings(i) > dv.length() || dsings(i) < 1 ||
                  D_NINT(dsings(i)) != dsings(i))
                {
--- 487,493 ----
          sings.resize (dsings.length());
  
          if (!error_state)
!           for (octave_idx_type i = 0; i < dsings.length(); i++)
              if (dsings(i) > dv.length() || dsings(i) < 1 ||
                  D_NINT(dsings(i)) != dsings(i))
                {
***************
*** 332,337 ****
--- 548,561 ----
    return retval;
  }
  
+ /*
+ 
+ %!assert(num2cell([1,2;3,4]),{1,2;3,4})
+ %!assert(num2cell([1,2;3,4],1),{[1;3],[2;4]})
+ %!assert(num2cell([1,2;3,4],2),{[1,2];[3,4]})
+ 
+ */
+ 
  DEFUN_DLD (mat2cell, args, ,
    "-*- texinfo -*-\n\
  @deftypefn {Loadable Function} address@hidden =} mat2cell (@var{a}, @var{m}, 
@var{n})\n\

reply via email to

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