[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: ***
- arrayfun.m reworked,
Thomas Treichl <=