octave-maintainers
[Top][All Lists]
Advanced

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

arrayfun.m reworked


From: Thomas Treichl
Subject: arrayfun.m reworked
Date: Thu, 31 Jul 2008 23:02:45 +0200
User-agent: Thunderbird 2.0.0.16 (Macintosh/20080707)

Hi,

I have finished reworking arrayfun - finally I decided to find the bug in arrayfun.m and not to send my arrayun.cc here. And thanks to David for having a look at the cellfun problems I have had ;)

You have been right John, my implementation arrayfun.cc is still slower then the current implementation because I used the C++ subsref and subsasgn methods for all types of input arguments.

The attached changeset includes the following changes to arrayfun.m:
 - created new help text
 - fixed bug in arrayfun function
 - added a lot of test to check my compatibility needs

I hope it is good enough to be included into the default development branch.

Regards,

  Thomas

PS. I have created a similiar large testset for cellfun, should I prepare a patch for this or is this some kind of overshooting?
# HG changeset patch
# User Thomas Treichl <address@hidden>
# Date 1217537669 -7200
# Node ID a3157728def1b0f490eb67f7f08c8e6be79f0a83
# Parent  ff9e7873f8ea2008610dfcb93b820757e320ccc3
Minor bug fixes, update help text and tests

diff --git a/scripts/ChangeLog b/scripts/ChangeLog
--- a/scripts/ChangeLog
+++ b/scripts/ChangeLog
@@ -1,3 +1,7 @@ 2008-07-29  John W. Eaton  <address@hidden
+2008-07-29  Thomas Treichl  <address@hidden>
+
+       * general/arrayfun.m: Minor bug fixes, update help text and tests
+
 2008-07-29  John W. Eaton  <address@hidden>
 
        * plot/axis.m (__get_tight_lims__): Use strcat instead of [].
diff --git a/scripts/general/arrayfun.m b/scripts/general/arrayfun.m
--- a/scripts/general/arrayfun.m
+++ b/scripts/general/arrayfun.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2006, 2007 Bill Denney
+## Copyright (C) 2006, 2007, 2008 Bill Denney
 ##
 ## This file is part of Octave.
 ##
@@ -17,17 +17,114 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} address@hidden =} arrayfun (@var{name}, @var{c})
-## @deftypefnx {Function File} address@hidden =} arrayfun (@var{func}, @var{c})
-## @deftypefnx {Function File} address@hidden =} arrayfun (@var{func}, 
@var{c}, @var{d})
-## @deftypefnx {Function File} address@hidden =} arrayfun (@var{func}, 
@var{c}, @var{options})
-## @deftypefnx {Function File} address@hidden, @var{b}, @dots{}] =} arrayfun 
(@var{func}, @var{c}, @dots{})
+## @deftypefn  {Function File} {} arrayfun (@var{func}, @var{a})
+## @deftypefnx {Function File} address@hidden =} arrayfun (@var{func}, @var{a})
+## @deftypefnx {Function File} address@hidden =} arrayfun (@var{func}, 
@var{a}, @var{b}, @dots{})
+## @deftypefnx {Function File} address@hidden, @var{y}, @dots{}] =} arrayfun 
(@var{func}, @var{a}, @dots{})
+## @deftypefnx {Function File} {} arrayfun (@dots{}, "UniformOutput", 
@var{val})
+## @deftypefnx {Function File} {} arrayfun (@dots{}, "ErrorHandler", 
@var{errfunc})
+##
 ## Execute a function on each element of an array.  This is useful for
 ## functions that do not accept array arguments.  If the function does
 ## accept array arguments it is better to call the function directly.
 ##
-## See @code{cellfun} for complete usage instructions.
-## @seealso{cellfun}
+## The first input argument @var{func} can be a string, a function
+## handle, an inline function or an anonymous function.  The input
+## argument @var{a} can be a logic array, a numeric array, a string
+## array, a structure array or a cell array.  By a call of the function
+## @command{arrayfun} all elements of @var{a} are passed on to the named
+## function @var{func} individually.
+## 
+## The named function can also take more than two input arguments, with
+## the input arguments given as third input argument @var{b}, fourth
+## input argument @var{c}, @dots{}  If given more than one array input
+## argument then all input arguments must have the same sizes, for
+## example
+##
+## @example
+## @group
+## arrayfun (@@atan2, [1, 0], [0, 1])
+## @result{} ans = [1.5708   0.0000]
+## @end group
+## @end example
+##
+## If the parameter @var{val} after a further string input argument
+## "UniformOutput" is set @code{true} (the default), then the named
+## function @var{func} must return a single element which then will be
+## concatenated into the return value and is of type matrix.  Otherwise,
+## if that parameter is set to @code{false}, then the outputs are
+## concatenated in a cell array.  For example
+##
+## @example
+## @group
+## arrayfun (@@(x,y) x:y, "abc", "def", "UniformOutput", false)
+## @result{} ans =
+## @{
+##   [1,1] = abcd
+##   [1,2] = bcde
+##   [1,3] = cdef
+## @}
+## @end group
+## @end example
+##
+## If more than one output arguments are given then the named function
+## must return the number of return values that also are expected, for
+## example
+##
+## @example
+## @group
+## [A, B, C] = arrayfun (@@find, [10; 0], "UniformOutput", false)
+## @result{}
+## A =
+## @{
+##   [1,1] =  1
+##   [2,1] = [](0x0)
+## @}
+## B =
+## @{
+##   [1,1] =  1
+##   [2,1] = [](0x0)
+## @}
+## C =
+## @{
+##   [1,1] =  10
+##   [2,1] = [](0x0)
+## @}
+## @end group
+## @end example
+##
+## If the parameter @var{errfunc} after a further string input argument
+## "ErrorHandler" is another string, a function handle, an inline
+## function or an anonymous function, then @var{errfunc} defines a
+## function to call in the case that @var{func} generates an error.
+## The definition of the function must be of the form
+##
+## @example
+## function address@hidden = errfunc (@var{s}, @dots{})
+## @end example
+##
+## where there is an additional input argument to @var{errfunc}
+## relative to @var{func}, given by @var{s}.  This is a structure with
+## the elements "identifier", "message" and "index", giving
+## respectively the error identifier, the error message and the index of
+## the array elements that caused the error.  The size of the output
+## argument of @var{errfunc} must have the same size as the output
+## argument of @var{func}, otherwise a real error is thrown.  For
+## example
+##
+## @example
+## @group
+## function y = ferr (s, x), y = "MyString"; endfunction
+## arrayfun (@@str2num, [1234], \
+##           "UniformOutput", false, "ErrorHandler", @@ferr)
+## @result{} ans =
+## @{
+##  [1,1] = MyString
+## @}
+## @end group
+## @end example
+##
+## @seealso{cellfun, spfun, structfun}
 ## @end deftypefn
 
 ## Author: Bill Denney <address@hidden>
@@ -45,20 +142,20 @@ function varargout = arrayfun (func, var
   m2cargs{2} = ones (size (varargin{1}, 2), 1);
   cfarg{1} = mat2cell (varargin{1}, m2cargs{:});
   stillmatches = true;
-  idx = 1;
-  while (stillmatches && idx < numel (varargin))
+  idx = 1; len = length (varargin);
+  while (stillmatches && idx < len)
     idx++;
     thissize = size (varargin{idx});
     if (numel (thissize) == numel (sizetomatch)
-       && all (thissize == sizetomatch))
+        && all (thissize == sizetomatch))
       if (ischar (varargin{idx})
-         && (strcmpi (varargin{idx}, "UniformOutput")
-             || strcmpi (varargin{idx}, "ErrorHandler")))
-       ## Catch these strings just in case they happen to be the same
-       ## size as the other input.
-       stillmatches = false;
+          && (strcmpi (varargin{idx}, "UniformOutput")
+              || strcmpi (varargin{idx}, "ErrorHandler")))
+        ## Catch these strings just in case they happen to be the same
+        ## size as the other input.
+        stillmatches = false;
       else
-       cfarg{idx} = mat2cell (varargin{idx}, m2cargs{:});
+        cfarg{idx} = mat2cell (varargin{idx}, m2cargs{:});
       endif
     else
       stillmatches = false;
@@ -66,10 +163,190 @@ function varargout = arrayfun (func, var
   endwhile
 
   varargout = cell (max ([nargout, 1]), 1);
-  [varargout{:}] = cellfun (func, cfarg{:}, varargin{idx+1:numel(varargin)});
+  if (idx >= len)
+    [varargout{:}] = cellfun (func, cfarg{:});
+  else
+    [varargout{:}] = cellfun (func, cfarg{:}, varargin{idx:len});
+  end
 endfunction
 
+%% 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
-%! fun = @(x,y) 2*x+y;
-%! A = [1,2;3,4];
-%! assert(arrayfun(fun,A,A),fun(A,A))
+%!  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+6*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]);
+
+## Local Variables: ***
+## mode: octave ***
+## End: ***

reply via email to

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