GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
cellfun.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2005-2026 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <list>
31#include <memory>
32#include <string>
33#include <vector>
34
35#include "mappers.h"
36#include "oct-locbuf.h"
37#include "oct-string.h"
38
39#include "Cell.h"
40#include "oct-map.h"
41#include "defun.h"
42#include "interpreter-private.h"
43#include "interpreter.h"
44#include "variables.h"
45#include "unwind-prot.h"
46#include "errwarn.h"
47#include "utils.h"
48
49#include "ov-bool.h"
50#include "ov-class.h"
51#include "ov-colon.h"
52#include "ov-complex.h"
53#include "ov-float.h"
54#include "ov-flt-complex.h"
55#include "ov-int16.h"
56#include "ov-int32.h"
57#include "ov-int64.h"
58#include "ov-int8.h"
59#include "ov-scalar.h"
60#include "ov-uint16.h"
61#include "ov-uint32.h"
62#include "ov-uint64.h"
63#include "ov-uint8.h"
64
65#include "ov-fcn-handle.h"
66
68
70fcn_eval (interpreter& interp, const octave_idx_type count,
71 octave_idx_type nargout, const octave_value_list& inputlist,
72 const octave_value& fcn, const octave_value& error_handler)
73{
74 octave_value_list retval;
75
76 bool execution_error = false;
77
78 try
79 {
80 retval = interp.feval (fcn, inputlist, nargout);
81 }
82 catch (const execution_exception& ee)
83 {
84 if (error_handler.is_defined ())
85 {
86 error_system& es = interp.get_error_system ();
87
88 es.save_exception (ee);
89 interp.recover_from_exception ();
90
91 execution_error = true;
92 }
93 else
94 throw;
95 }
96
97 if (execution_error)
98 {
99 // ErrorHandler exists or this code would not have been reached.
100 // Call ErrorHandler() to create substitute output.
101 error_system& es = interp.get_error_system ();
102
104 msg.assign ("identifier", es.last_error_id ());
105 msg.assign ("message", es.last_error_message ());
106 msg.assign ("index",
107 static_cast<double> (count
108 + static_cast<octave_idx_type> (1)));
109
110 octave_value_list errlist = inputlist;
111 errlist.prepend (msg);
112
113 retval = interp.feval (error_handler, errlist, nargout);
114 }
115
116 return retval;
117}
118
120try_cellfun_accelfcns (const octave_value_list& args, const int nargin)
121{
122 octave_value_list retval;
123
124 std::string fcn_name = args(0).string_value ();
125
126 const Cell f_arg = args(1).cell_value ();
127
128 octave_idx_type nel = f_arg.numel ();
129
130 if (fcn_name == "isempty")
131 {
132 if (nargin > 2)
133 error_with_id ("Octave:invalid-input-arg",
134 "cellfun: accelerated function must be called with only one argument");
135 boolNDArray result (f_arg.dims ());
136 for (octave_idx_type count = 0; count < nel; count++)
137 result(count) = f_arg.xelem (count).isempty ();
138 retval(0) = result;
139 }
140 else if (fcn_name == "islogical")
141 {
142 if (nargin > 2)
143 error_with_id ("Octave:invalid-input-arg",
144 "cellfun: accelerated function must be called with only one argument");
145 boolNDArray result (f_arg.dims ());
146 for (octave_idx_type count= 0; count < nel; count++)
147 result(count) = f_arg.xelem (count).islogical ();
148 retval(0) = result;
149 }
150 else if (fcn_name == "isnumeric")
151 {
152 if (nargin > 2)
153 error_with_id ("Octave:invalid-input-arg",
154 "cellfun: accelerated function must be called with only one argument");
155 boolNDArray result (f_arg.dims ());
156 for (octave_idx_type count= 0; count < nel; count++)
157 result(count) = f_arg.xelem (count).isnumeric ();
158 retval(0) = result;
159 }
160 else if (fcn_name == "isreal")
161 {
162 if (nargin > 2)
163 error_with_id ("Octave:invalid-input-arg",
164 "cellfun: accelerated function must be called with only one argument");
165 boolNDArray result (f_arg.dims ());
166 for (octave_idx_type count= 0; count < nel; count++)
167 result(count) = f_arg.xelem (count).isreal ();
168 retval(0) = result;
169 }
170 else if (fcn_name == "length")
171 {
172 if (nargin > 2)
173 error_with_id ("Octave:invalid-input-arg",
174 "cellfun: accelerated function must be called with only one argument");
175 NDArray result (f_arg.dims ());
176 for (octave_idx_type count= 0; count < nel; count++)
177 result(count) = static_cast<double> (f_arg.xelem (count).length ());
178 retval(0) = result;
179 }
180 else if (fcn_name == "ndims")
181 {
182 if (nargin > 2)
183 error_with_id ("Octave:invalid-input-arg",
184 "cellfun: accelerated function must be called with only one argument");
185 NDArray result (f_arg.dims ());
186 for (octave_idx_type count = 0; count < nel; count++)
187 result(count) = static_cast<double> (f_arg.xelem (count).ndims ());
188 retval(0) = result;
189 }
190 else if (fcn_name == "numel" || fcn_name == "prodofsize")
191 {
192 if (nargin > 2)
193 error_with_id ("Octave:invalid-input-arg",
194 "cellfun: accelerated function must be called with only one argument");
195 NDArray result (f_arg.dims ());
196 for (octave_idx_type count = 0; count < nel; count++)
197 result(count) = static_cast<double> (f_arg.xelem (count).numel ());
198 retval(0) = result;
199 }
200 else if (fcn_name == "size")
201 {
202 if (nargin != 3)
203 error_with_id ("Octave:invalid-input-arg",
204 "cellfun: accelerated function 'size' must be called with exactly two arguments");
205
206 int d = args(2).strict_int_value () - 1;
207
208 if (d < 0)
209 error_with_id ("Octave:invalid-input-arg",
210 "cellfun: K must be a positive integer");
211
212 NDArray result (f_arg.dims ());
213
214 for (octave_idx_type count = 0; count < nel; count++)
215 {
216 const dim_vector& dv = f_arg.xelem (count).dims ();
217 if (d < dv.ndims ())
218 result(count) = static_cast<double> (dv(d));
219 else
220 result(count) = 1.0;
221 }
222
223 retval(0) = result;
224 }
225 else if (fcn_name == "isclass")
226 {
227 if (nargin != 3)
228 error_with_id ("Octave:invalid-input-arg",
229 "cellfun: accelerated function 'isclass' must be called with exactly two arguments");
230
231 std::string class_name = args(2).xstring_value ("cellfun: CLASS argument to 'isclass' must be a string");
232 boolNDArray result (f_arg.dims ());
233 for (octave_idx_type count = 0; count < nel; count++)
234 result(count) = (f_arg.xelem (count).class_name () == class_name);
235
236 retval(0) = result;
237 }
238
239 return retval;
240}
241
242static void
243parse_options (symbol_table& symtab, const octave_value_list& args,
244 int& nargin, bool& uniform_output, octave_value& error_handler)
245{
246 while (nargin > 3 && args(nargin-2).is_string ())
247 {
248 std::string arg = args(nargin-2).string_value ();
249
250 std::size_t compare_len = std::max (arg.length (),
251 static_cast<std::size_t> (2));
252
253 if (string::strncmpi (arg, "uniformoutput", compare_len))
254 uniform_output = args(nargin-1).xbool_value ("cellfun: UniformOutput value must be boolean");
255 else if (string::strncmpi (arg, "errorhandler", compare_len))
256 {
257 if (args(nargin-1).is_function_handle ()
258 || args(nargin-1).is_inline_function ())
259 {
260 error_handler = args(nargin-1);
261 }
262 else if (args(nargin-1).is_string ())
263 {
264 std::string err_name = args(nargin-1).string_value ();
265
266 error_handler = symtab.find_function (err_name);
267
268 if (error_handler.is_undefined ())
269 error_with_id ("Octave:invalid-input-arg",
270 "cellfun: invalid function NAME: %s",
271 err_name.c_str ());
272 }
273 else
274 error_with_id ("Octave:invalid-input-arg",
275 "cellfun: invalid value for 'ErrorHandler' function");
276 }
277 else
278 error_with_id ("Octave:invalid-input-arg",
279 "cellfun: unrecognized parameter %s", arg.c_str ());
280
281 nargin -= 2;
282 }
283
284 nargin -= 1;
285}
286
287DEFMETHOD (cellfun, interp, args, nargout,
288 doc: /* -*- texinfo -*-
289@deftypefn {} {@var{A} =} cellfun (@@@var{fcn}, @var{C})
290@deftypefnx {} {@var{A} =} cellfun ("@var{fcn}", @var{C})
291@deftypefnx {} {@var{A} =} cellfun (@var{fcn}, @var{C})
292@deftypefnx {} {@var{A} =} cellfun (@var{fcn}, @var{C1}, @var{C2}, @dots{})
293@deftypefnx {} {[@var{A1}, @var{A2}, @dots{}] =} cellfun (@dots{})
294@deftypefnx {} {@var{A} =} cellfun (@dots{}, "UniformOutput", @var{val})
295@deftypefnx {} {@var{A} =} cellfun (@dots{}, "ErrorHandler", @var{errfcn})
296@deftypefnx {} {@var{A} =} cellfun ('isempty', @var{C})
297@deftypefnx {} {@var{A} =} cellfun ('islogical', @var{C})
298@deftypefnx {} {@var{A} =} cellfun ('isnumeric', @var{C})
299@deftypefnx {} {@var{A} =} cellfun ('isreal', @var{C})
300@deftypefnx {} {@var{A} =} cellfun ('length', @var{C})
301@deftypefnx {} {@var{A} =} cellfun ('numel', @var{C})
302@deftypefnx {} {@var{A} =} cellfun ('prodofsize', @var{C})
303@deftypefnx {} {@var{A} =} cellfun ('size', @var{C}, @var{dim})
304@deftypefnx {} {@var{A} =} cellfun ('isclass', @var{C}, @var{class})
305
306Evaluate the function @var{fcn} on the elements of the cell array @var{C}.
307
308@code{cellfun} accepts an arbitrary function @var{fcn} given as a name in a
309character string, as a function handle, or as an inline function. Specifying
310@var{fcn} with a character string is preferred as the performance is ~3X better
311for builtin functions and equivalent for m-files.
312
313@code{cellfun} has a limited number of functions which have been
314specially-coded for high-performance (~8X faster than a function handle).
315These functions are only used if the function is specified by name as a string,
316and only the simplest calling form---@w{@code{cellfun ('@var{fcn}'),
317@var{C})}}---without options is supported. If you need access to an
318overloaded version of a function, such as @code{numel} for a particular
319@code{classdef} file, then you cannot use the accelerated function name and
320must use a function handle instead, e.g., @code{@@numel}.
321
322The high-performance functions are
323
324@table @asis
325@item @qcode{'isempty'}
326Return true for empty elements.
327
328@item @qcode{'islogical'}
329Return true for logical elements.
330
331@item @qcode{'isnumeric'}
332Return true for numeric elements.
333
334@item @qcode{'isreal'}
335Return true for real elements.
336
337@item @qcode{'length'}
338Return a vector of the lengths of cell elements.
339
340@item @qcode{'ndims'}
341Return the number of dimensions of each element.
342
343@item @qcode{'numel'}
344@itemx @qcode{'prodofsize'}
345Return the number of elements contained within each cell element. The
346number is the product of the dimensions of the object of each cell element.
347
348@item @qcode{'size'}
349Return the size along dimension @var{dim}.
350
351@item @qcode{'isclass'}
352Return true for elements which are of type @var{class}.
353@end table
354
355Elements in @var{C} are passed to the function individually and the result of
356each function invocation is collected in the output. The function can take
357more than one argument with the inputs arguments given by @var{C1}, @var{C2},
358etc. Input arguments that are singleton (1x1) cells will be automatically
359expanded to the size of the other arguments. For example:
360
361@example
362@group
363cellfun ("atan2", @{1, 0@}, @{0, 1@})
364 @xresult{} [ 1.57080 0.00000 ]
365@end group
366@end example
367
368The number of output arguments of @code{cellfun} matches the number of output
369arguments of the function and can be greater than one. When there are multiple
370outputs of the function they will be collected into the output arguments of
371@code{cellfun} like this:
372
373@example
374@group
375function [a, b] = twoouts (x)
376 a = x;
377 b = x*x;
378endfunction
379[aa, bb] = cellfun (@@twoouts, @{1, 2, 3@})
380 @xresult{}
381 aa =
382 1 2 3
383 bb =
384 1 4 9
385@end group
386@end example
387
388Note that, by default, the output argument(s) are arrays of the same size as
389the input arguments.
390
391If the parameter @qcode{"UniformOutput"} is set to true (the default), then the
392function must return scalars which will be concatenated into the return
393array(s). If @qcode{"UniformOutput"} is false, the outputs are concatenated
394into a cell array (or cell arrays). For example:
395
396@example
397@group
398cellfun ("lower", @{"Foo", "Bar", "FooBar"@},
399 "UniformOutput", false)
400@xresult{} @{"foo", "bar", "foobar"@}
401@end group
402@end example
403
404The parameter @qcode{"ErrorHandler"} specifies a function @var{errfcn} to call
405if @var{fcn} generates an error. The form of the function is
406
407@example
408function [@dots{}] = errfcn (@var{s}, @dots{})
409@end example
410
411@noindent
412where there is an additional input argument to @var{errfcn} relative to
413@var{fcn}, given by @var{s}. This is a structure with the elements
414@qcode{"identifier"}, @qcode{"message"}, and @qcode{"index"} giving
415respectively the error identifier, the error message, and the index into the
416input arguments of the element that caused the error. For example:
417
418@example
419@group
420function y = errfcn (s, x), y = NaN; endfunction
421cellfun ("factorial", @{-1, 2@}, "ErrorHandler", @@errfcn)
422@xresult{} [NaN 2]
423@end group
424@end example
425
426Programming Note: Use @code{cellfun} intelligently. The @code{cellfun}
427function is a useful tool for avoiding loops. It is often used with anonymous
428function handles; however, calling an anonymous function involves an overhead
429quite comparable to the overhead of an m-file function. Passing a handle to a
430built-in function is faster, because the interpreter is not involved in the
431internal loop. For example:
432
433@example
434@group
435C = @{@dots{}@}
436v = cellfun (@@(x) det (x), C); # compute determinants
437v = cellfun (@@det, C); # 40% faster
438@end group
439@end example
440
441@seealso{arrayfun, structfun, spfun}
442@end deftypefn */)
443{
444 int nargin = args.length ();
445
446 if (nargin < 2)
447 print_usage ();
448
449 if (! args(1).iscell ())
450 error_with_id ("Octave:invalid-input-arg",
451 "cellfun: C must be a cell array");
452
453 octave_value_list retval;
454
455 octave_value fcn = args(0);
456
457 // Possibly try accelerated functions, and return immediately
458 if (fcn.is_string ())
459 {
460 retval = try_cellfun_accelfcns (args, nargin);
461
462 if (! retval.empty ())
463 return retval;
464 }
465
466 // Function is not accelerated. Have to execute the full function.
467
468 symbol_table& symtab = interp.get_symbol_table ();
469
470 if (fcn.is_string ())
471 {
472 // See if we can convert the string into a function.
473 std::string name = args(0).string_value ();
474
475 if (! valid_identifier (name))
476 fcn = get_function_handle (interp, args(0), "x");
477 else
478 {
479 fcn = symtab.find_function (name);
480
481 if (fcn.is_undefined ())
482 error_with_id ("Octave:invalid-input-arg",
483 "cellfun: invalid function NAME: %s",
484 name.c_str ());
485 }
486 }
487
488 if (! fcn.is_function_handle () && ! fcn.is_function ()
489 && ! fcn.is_inline_function ())
490 error_with_id ("Octave:invalid-input-arg",
491 "cellfun: argument NAME must be a string or function handle");
492
493 // Parse remaining inputs ("UniformOutput" or "ErrorHandler" options)
494 bool uniform_output = true;
495 octave_value error_handler;
496 int nargout1 = (nargout < 1 ? 1 : nargout);
497
498 parse_options (symtab, args, nargin, uniform_output, error_handler);
499
500 // Optimize which function will be called in loop
501 if (fcn.is_function ())
502 {
503 // The symbol table can give a more specific function class for a general
504 // function, and this will result in fewer polymorphic function calls.
505 std::string name = fcn.function_value () -> name ();
506 octave_value f = symtab.find_function (name);
507 if (f.is_defined ())
508 fcn = f;
509 }
510 else // fcn is a function handle or inline function
511 {
512 // FIXME: Should check whether the types of all the cell array elements
513 // are all the same, then lookup the function for that type just once.
514 }
515
516 // Extract cell arguments.
517 octave_value_list inputovl (nargin);
518
519 OCTAVE_LOCAL_BUFFER (Cell, inputs, nargin);
520 OCTAVE_LOCAL_BUFFER (bool, isarray, nargin);
521
522 // This is to prevent copy-on-write.
523 const Cell *cinputs = inputs;
524
525 octave_idx_type nel = 1;
526 dim_vector inputdims (1, 1);
527
528 // Collect and validate arguments.
529 // Pre-fill scalar elements of inputovl array.
530 for (int j = 0; j < nargin; j++)
531 {
532 if (! args(j+1).iscell ())
533 error_with_id ("Octave:invalid-input-arg",
534 "cellfun: arguments must be cells");
535
536 inputs[j] = args(j+1).cell_value ();
537 isarray[j] = (inputs[j].numel () != 1);
538 if (isarray[j])
539 {
540 nel = inputs[j].numel ();
541 if (inputdims.numel () == 1) // first time an array encountered
542 inputdims = inputs[j].dims ();
543 else if (inputs[j].dims () != inputdims)
544 error ("cellfun: input cell dimensions mismatch");
545 }
546 else
547 inputovl(j) = cinputs[j](0); // scalar, pre-fill inputovl
548 }
549
550 // Apply function
551 if (uniform_output)
552 {
553 OCTAVE_LOCAL_BUFFER (octave_value, results, nargout1);
554
555 int expected_nargout;
556 for (octave_idx_type count = 0; count < nel; count++)
557 {
558 // Initialize inputovl with this loop count's input data
559 for (int j = 0; j < nargin; j++)
560 {
561 if (isarray[j])
562 inputovl.xelem (j) = cinputs[j](count);
563 }
564
565 // Evaluate function
566 const octave_value_list y
567 = fcn_eval (interp, count, nargout, inputovl, fcn, error_handler);
568
569 // Validate number of generated results is correct
570 int y_nel = y.length ();
571 if (count == 0)
572 {
573 // First time through loop. Initialize expected number of
574 // outputs based on nargout and output from first function
575 // evaluation.
576 if (nargout == 0)
577 expected_nargout = (y_nel > 0 && y(0).is_defined ()) ? 1 : 0;
578 else
579 expected_nargout = nargout;
580 }
581 if (y_nel < expected_nargout)
582 error ("cellfun: function returned fewer than nargout values");
583 else if (expected_nargout == 0 && y_nel > 0 && y(0).is_defined ())
584 error ("cellfun: function returned unexpected number of values");
585
586 // Copy loop results to output if necessary
587 if (expected_nargout > 0)
588 {
589 if (count == 0)
590 {
591 // First time through loop create output with class of
592 // function output and size of input cell arrays.
593 // Predeclaring output is more efficient than growing array.
594 for (int j = 0; j < expected_nargout; j++)
595 {
596 if (y(j).is_undefined ())
597 error ("cellfun: function returned fewer than nargout values");
598 octave_value val = y(j);
599
600 if (val.numel () != 1)
601 error ("cellfun: all values must be scalars when UniformOutput = true");
602
603 results[j] = val.resize (inputdims);
604 }
605 }
606 else
607 {
608 for (int j = 0; j < expected_nargout; j++)
609 {
610 if (y(j).is_undefined ())
611 error ("cellfun: function returned fewer than nargout values");
612 octave_value val = y(j);
613
614 if (! results[j].fast_elem_insert (count, val))
615 {
616 if (val.numel () != 1)
617 error ("cellfun: all values must be scalars when UniformOutput = true");
618
619 error ("cellfun: all values should be of the same type when UniformOutput = true");
620 }
621 }
622 }
623 }
624 }
625
626 // Resize return array and fill with collected results
627 retval.resize (nargout1);
628 for (int j = 0; j < nargout1; j++)
629 {
630 if (nargout > 0 && results[j].is_undefined ())
631 retval(j) = NDArray (inputdims);
632 else
633 retval(j) = results[j];
634 }
635 }
636 else // UniformOutput = false (return Cells)
637 {
638 OCTAVE_LOCAL_BUFFER (Cell, results, nargout1);
639
640 for (int j = 0; j < nargout1; j++)
641 results[j].resize (inputdims);
642
643 bool have_output = false;
644
645 for (octave_idx_type count = 0; count < nel; count++)
646 {
647 for (int j = 0; j < nargin; j++)
648 {
649 if (isarray[j])
650 inputovl.xelem (j) = cinputs[j](count);
651 }
652
653 const octave_value_list y
654 = fcn_eval (interp, count, nargout, inputovl, fcn, error_handler);
655
656 int y_nel = y.length ();
657
658 if (nargout > 0 && y_nel < nargout)
659 error ("cellfun: function returned fewer than nargout values");
660
661 if (nargout > 0
662 || (nargout == 0 && y_nel > 0 && y(0).is_defined ()))
663 {
664 have_output = true;
665
666 int num_to_copy = std::min (y_nel, nargout1);
667
668 for (int j = 0; j < num_to_copy; j++)
669 {
670 if (y(j).is_undefined ())
671 error ("cellfun: function returned fewer than nargout values");
672 results[j](count) = y(j);
673 }
674 }
675 }
676
677 if (have_output || inputdims.any_zero ())
678 {
679 retval.resize (nargout1);
680 for (int j = 0; j < nargout1; j++)
681 retval(j) = results[j];
682 }
683 }
684
685 return retval;
686}
687
688/*
689
690%!function r = __f11 (x)
691%! r = x;
692%!endfunction
693
694%!function __f01 (x)
695%! ## Empty function
696%!endfunction
697
698%!function varargout = __f02 (out)
699%! if (out)
700%! varargout{1} = out;
701%! endif
702%!endfunction
703
704%!test
705%! __cellfun_test_num_outputs__ = -1;
706%!
707%! function r = __subf11 (x)
708%! __cellfun_test_num_outputs__ = nargout;
709%! r = x;
710%! endfunction
711%!
712%! cellfun ("__subf11", {1});
713%! assert (__cellfun_test_num_outputs__, 0);
714%!
715%! __cellfun_test_num_outputs__ = -1;
716%! x = cellfun ("__subf11", {1});
717%! assert (__cellfun_test_num_outputs__, 1);
718
719%!test
720%! __cellfun_test_num_outputs__ = -1;
721%! function __subf01 (x)
722%! __cellfun_test_num_outputs__ = nargout;
723%! endfunction
724%! cellfun ("__subf01", {1});
725%! assert (__cellfun_test_num_outputs__, 0);
726
727%!error x = cellfun (@__f01, {1, 2})
728%!error x = cellfun (@__f01, {1, 2})
729
730%!error x = cellfun (@__f02, {0, 2}, "uniformoutput", false)
731%!error x = cellfun (@__f02, {0, 2}, "uniformoutput", true)
732
733%!test
734%! assert (cellfun (@__f11, {1, 2}), [1, 2]);
735%! assert (cellfun (@__f11, {1, 2}, 'uniformoutput', false), {1, 2});
736
737%!test
738%! [a,b] = cellfun (@(x) x, cell (2, 0));
739%! assert (a, zeros (2, 0));
740%! assert (b, zeros (2, 0));
741
742%!test
743%! [a,b] = cellfun (@(x) x, cell (2, 0), "uniformoutput", false);
744%! assert (a, cell (2, 0));
745%! assert (b, cell (2, 0));
746
747## Test function to check the "Errorhandler" option
748%!function z = __cellfunerror (S, varargin)
749%! z = S;
750%!endfunction
751
752## First input argument can be a string, an inline function,
753## a function_handle or an anonymous function
754%!test
755%! A = cellfun ("islogical", {true, 0.1, false, i*2});
756%! assert (A, [true, false, true, false]);
757%!test
758%! warning ('off', 'Octave:legacy-function', 'local');
759%! A = cellfun (inline ("islogical (x)", "x"), {true, 0.1, false, i*2});
760%! assert (A, [true, false, true, false]);
761%!test
762%! A = cellfun ('islogical', {true, 0.1, false, i*2});
763%! assert (A, [true, false, true, false]);
764%!test
765%! A = cellfun (@(x) islogical (x), {true, 0.1, false, i*2});
766%! assert (A, [true, false, true, false]);
767
768## First input argument can be the special string "isreal",
769## "isempty", "islogical", "isnumeric", "length", "ndims" or "prodofsize"
770%!test
771%! A = cellfun ("isreal", {true, 0.1, {}, i*2, [], "abc"});
772%! assert (A, [true, true, false, false, true, true]);
773%!test
774%! A = cellfun ("isempty", {true, 0.1, false, i*2, [], "abc"});
775%! assert (A, [false, false, false, false, true, false]);
776%!test
777%! A = cellfun ("islogical", {true, 0.1, false, i*2, [], "abc"});
778%! assert (A, [true, false, true, false, false, false]);
779%!test
780%! A = cellfun ("isnumeric", {true, 0.1, false, i*2, [], "abc"});
781%! assert (A, [false, true, false, true, true, false]);
782%!test
783%! A = cellfun ("length", {true, 0.1, false, i*2, [], "abc"});
784%! assert (A, [1, 1, 1, 1, 0, 3]);
785%!test
786%! A = cellfun ("ndims", {[1, 2; 3, 4]; (cell (1,2,3,4))});
787%! assert (A, [2; 4]);
788%!test
789%! A = cellfun ("prodofsize", {[1, 2; 3, 4], (cell (1,2,3,4))});
790%! assert (A, [4, 24]);
791
792## Number of input and output arguments may not be limited to one
793%!test
794%! A = cellfun (@(x,y,z) x + y + z, {1, 1, 1}, {2, 2, 2}, {3, 4, 5});
795%! assert (A, [6, 7, 8]);
796%!test
797%! A = cellfun (@(x,y,z) x + y + z, {1, 1, 1}, {2, 2, 2}, {3, 4, 5}, ...
798%! "UniformOutput", false);
799%! assert (A, {6, 7, 8});
800%!test # Two input arguments of different types
801%! A = cellfun (@(x,y) islogical (x) && ischar (y), {false, true}, {"a", 3});
802%! assert (A, [true, false]);
803%!test # Pass another variable to the anonymous function
804%! y = true;
805%! A = cellfun (@(x) islogical (x) && y, {false, 0.3});
806%! assert (A, [true, false]);
807%!test # Three output arguments of different type
808%! [A, B, C] = cellfun ('find', {10, 11; 0, 12}, "UniformOutput", false);
809%! assert (isequal (A, {true, true; [], true}));
810%! assert (isequal (B, {true, true; [], true}));
811%! assert (isequal (C, {10, 11; [], 12}));
812
813## Input arguments can be of type cell array of logical
814%!test
815%! A = cellfun (@(x,y) x == y, {false, true}, {true, true});
816%! assert (A, [false, true]);
817%!test
818%! A = cellfun (@(x,y) x == y, {false; true}, {true; true}, ...
819%! "UniformOutput", true);
820%! assert (A, [false; true]);
821%!test
822%! A = cellfun (@(x) x, {false, true; false, true}, "UniformOutput", false);
823%! assert (A, {false, true; false, true});
824%!test # Three output arguments of same type
825%! [A, B, C] = cellfun ('find', {true, false; false, true}, ...
826%! "UniformOutput", false);
827%! assert (isequal (A, {true, []; [], true}));
828%! assert (isequal (B, {true, []; [], true}));
829%! assert (isequal (C, {true, []; [], true}));
830%!test
831%! A = cellfun (@(x,y) cell2str (x,y), {true}, {true}, ...
832%! "ErrorHandler", @__cellfunerror);
833%! assert (isfield (A, "identifier"), true);
834%! assert (isfield (A, "message"), true);
835%! assert (isfield (A, "index"), true);
836%! assert (isempty (A.message), false);
837%! assert (A.index, 1);
838%!test # Overwriting setting of "UniformOutput" true
839%! A = cellfun (@(x,y) cell2str (x,y), {true}, {true}, ...
840%! "UniformOutput", true, "ErrorHandler", @__cellfunerror);
841%! assert (isfield (A, "identifier"), true);
842%! assert (isfield (A, "message"), true);
843%! assert (isfield (A, "index"), true);
844%! assert (isempty (A.message), false);
845%! assert (A.index, 1);
846
847## Input arguments can be of type cell array of numeric
848%!test
849%! A = cellfun (@(x,y) x>y, {1.1, 4.2}, {3.1, 2+3*i});
850%! assert (A, [false, true]);
851%!test
852%! A = cellfun (@(x,y) x>y, {1.1, 4.2; 2, 4}, {3.1, 2; 2, 4+2*i}, ...
853%! "UniformOutput", true);
854%! assert (A, [false, true; false, false]);
855%!test
856%! A = cellfun (@(x,y) x:y, {1.1, 4}, {3.1, 6}, "UniformOutput", false);
857%! assert (isequal (A{1}, [1.1, 2.1, 3.1]));
858%! assert (isequal (A{2}, [4, 5, 6]));
859%!test # Three output arguments of different type
860%! [A, B, C] = cellfun ('find', {10, 11; 0, 12}, "UniformOutput", false);
861%! assert (isequal (A, {true, true; [], true}));
862%! assert (isequal (B, {true, true; [], true}));
863%! assert (isequal (C, {10, 11; [], 12}));
864%!test
865%! A = cellfun (@(x,y) cell2str (x,y), {1.1, 4}, {3.1, 6}, ...
866%! "ErrorHandler", @__cellfunerror);
867%! B = isfield (A(1), "message") && isfield (A(1), "index");
868%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
869%! [true, true]);
870%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
871%! [true, true]);
872%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
873%! [true, true]);
874%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
875%! [false, false]);
876%! assert ([A(1).index, A(2).index], [1, 2]);
877%!test # Overwriting setting of "UniformOutput" true
878%! A = cellfun (@(x,y) cell2str (x,y), {1.1, 4}, {3.1, 6}, ...
879%! "UniformOutput", true, "ErrorHandler", @__cellfunerror);
880%! B = isfield (A(1), "message") && isfield (A(1), "index");
881%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
882%! [true, true]);
883%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
884%! [true, true]);
885%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
886%! [true, true]);
887%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
888%! [false, false]);
889%! assert ([A(1).index, A(2).index], [1, 2]);
890
891## Input arguments can be of type cell arrays of character or strings
892%!error # "UniformOutput" false should be used
893%! A = cellfun (@(x,y) x>y, {"ad", "c", "ghi"}, {"cc", "d", "fgh"});
894%!test
895%! A = cellfun (@(x,y) x>y, {"a"; "f"}, {"c"; "d"}, "UniformOutput", true);
896%! assert (A, [false; true]);
897%!test
898%! A = cellfun (@(x,y) x:y, {"a", "d"}, {"c", "f"}, "UniformOutput", false);
899%! assert (A, {"abc", "def"});
900%!test
901%! A = cellfun (@(x,y) cell2str (x,y), {"a", "d"}, {"c", "f"}, ...
902%! "ErrorHandler", @__cellfunerror);
903%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
904%! [true, true]);
905%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
906%! [true, true]);
907%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
908%! [true, true]);
909%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
910%! [false, false]);
911%! assert ([A(1).index, A(2).index], [1, 2]);
912%!test # Overwriting setting of "UniformOutput" true
913%! A = cellfun (@(x,y) cell2str (x,y), {"a", "d"}, {"c", "f"}, ...
914%! "UniformOutput", true, "ErrorHandler", @__cellfunerror);
915%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
916%! [true, true]);
917%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
918%! [true, true]);
919%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
920%! [true, true]);
921%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
922%! [false, false]);
923%! assert ([A(1).index, A(2).index], [1, 2]);
924
925## Structures cannot be handled by cellfun
926%!error
927%! vst1.a = 1.1; vst1.b = 4.2; vst2.a = 3.1; vst2.b = 2;
928%! A = cellfun (@(x,y) (x.a < y.a) && (x.b > y.b), vst1, vst2);
929
930## Input arguments can be of type cell array of cell arrays
931%!test
932%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}, {4.2}}, {{3.1}, {2}});
933%! assert (A, [1, 0], 1e-16);
934%!test
935%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}; {4.2}}, {{3.1}; {2}}, ...
936%! "UniformOutput", true);
937%! assert (A, [1; 0], 1e-16);
938%!test
939%! A = cellfun (@(x,y) x{1} < y{1}, {{1.1}, {4.2}}, {{3.1}, {2}}, ...
940%! "UniformOutput", false);
941%! assert (A, {true, false});
942%!test
943%! A = cellfun (@(x,y) mat2str (x,y), {{1.1}, {4.2}}, {{3.1}, {2}}, ...
944%! "ErrorHandler", @__cellfunerror);
945%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
946%! [true, true]);
947%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
948%! [true, true]);
949%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
950%! [true, true]);
951%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
952%! [false, false]);
953%! assert ([A(1).index, A(2).index], [1, 2]);
954%!test # Overwriting setting of "UniformOutput" true
955%! A = cellfun (@(x,y) mat2str (x,y), {{1.1}, {4.2}}, {{3.1}, {2}}, ...
956%! "UniformOutput", true, "ErrorHandler", @__cellfunerror);
957%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
958%! [true, true]);
959%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
960%! [true, true]);
961%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
962%! [true, true]);
963%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
964%! [false, false]);
965%! assert ([A(1).index, A(2).index], [1, 2]);
966
967## Input arguments can be of type cell array of structure arrays
968%!test
969%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
970%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b), {a}, {b});
971%! assert (A, true);
972%!test
973%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
974%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b) , {a}, {b}, ...
975%! "UniformOutput", true);
976%! assert (A, true);
977%!test
978%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
979%! A = cellfun (@(x,y) (x.a == y.a) && (x.b < y.b) , {a}, {b}, ...
980%! "UniformOutput", false);
981%! assert (A, {true});
982%!test
983%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
984%! A = cellfun (@(x,y) cell2str (x.a, y.a), {a}, {b}, ...
985%! "ErrorHandler", @__cellfunerror);
986%! assert (isfield (A, "identifier"), true);
987%! assert (isfield (A, "message"), true);
988%! assert (isfield (A, "index"), true);
989%! assert (isempty (A.message), false);
990%! assert (A.index, 1);
991%!test # Overwriting setting of "UniformOutput" true
992%! a = struct ("a", 1, "b", 2); b = struct ("a", 1, "b", 3);
993%! A = cellfun (@(x,y) cell2str (x.a, y.a), {a}, {b}, ...
994%! "UniformOutput", true, "ErrorHandler", @__cellfunerror);
995%! assert (isfield (A, "identifier"), true);
996%! assert (isfield (A, "message"), true);
997%! assert (isfield (A, "index"), true);
998%! assert (isempty (A.message), false);
999%! assert (A.index, 1);
1000
1001## A lot of other tests
1002%!assert (cellfun (@sin, {0,1}), sin ([0,1]))
1003%!test
1004%! warning ('off', 'Octave:legacy-function', 'local');
1005%! assert (cellfun (inline ("sin (x)"), {0,1}), sin ([0,1]));
1006%!assert (cellfun ("sin", {0,1}), sin ([0,1]))
1007%!assert (cellfun ("isempty", {1,[]}), [false,true])
1008%!assert (cellfun ("islogical", {false,pi}), [true,false])
1009%!assert (cellfun ("isnumeric", {false,pi,struct()}), [false,true,false])
1010%!assert (cellfun ("isreal", {1i,1}), [false,true])
1011%!assert (cellfun ("length", {zeros(2,2),1}), [2,1])
1012%!assert (cellfun ("prodofsize", {zeros(2,2),1}), [4,1])
1013%!assert (cellfun ("ndims", {zeros([2,2,2]),1}), [3,2])
1014%!assert (cellfun ("isclass", {zeros([2,2,2]),"test"}, "double"), [true,false])
1015%!assert (cellfun ("size", {zeros([1,2,3]),1}, 1), [1,1])
1016%!assert (cellfun ("size", {zeros([1,2,3]),1}, 2), [2,1])
1017%!assert (cellfun ("size", {zeros([1,2,3]),1}, 3), [3,1])
1018%!assert (cellfun ('atan2', {1,1}, {1,2}), [atan2(1,1), atan2(1,2)])
1019%!assert (cellfun ('atan2', {1,1}, {1,2},"UniformOutput", false),
1020%! {atan2(1,1), atan2(1,2)})
1021%!assert (cellfun ('sin', {1,2;3,4}), sin ([1,2;3,4]))
1022%!assert (cellfun ('atan2', {1,1;1,1}, {1,2;1,2}), atan2 ([1,1;1,1],[1,2;1,2]))
1023%!error cellfun ('factorial', {-1,3})
1024%!assert (cellfun ('factorial', {-1,3}, "ErrorHandler", @(x,y) NaN), [NaN,6])
1025%!assert (cellfun (@(x) x(2),{[1],[1,2]}, "ErrorHandler", @(x,y) NaN), [NaN,2])
1026%!test
1027%! [a,b,c] = cellfun ('fileparts', {fullfile("a","b","c.d"), fullfile("e","f","g.h")}, "UniformOutput", false);
1028%! assert (a, {fullfile("a","b"), fullfile("e","f")});
1029%! assert (b, {"c", "g"});
1030%! assert (c, {".d", ".h"});
1031
1032%!assert <*40467> (cellfun ('isreal', {1 inf NaN []}), [true, true, true, true])
1033%!assert <*40467> (cellfun (@isreal, {1 inf NaN []}, "UniformOutput", false),
1034%! {true, true, true, true})
1035%!assert <*40467> (cellfun ('iscomplex', {1 inf NaN []}),
1036%! [false, false, false, false])
1037%!assert <*40467> (cellfun ('iscomplex', {1 inf NaN []}, "UniformOutput", false),
1038%! {false, false, false, false})
1039
1040## Output values of different sizes
1041%!test <*67217>
1042%! fail ("cellfun (@(x) x, {5, [1, 2]})", "all values must be scalars");
1043
1044## Output values of different types
1045%!test <*67217>
1046%! fail ("cellfun (@(x) x, {5, true})", "all values should be of the same type");
1047
1048%!function retval = __errfcn (S, varargin)
1049%! global __errmsg;
1050%! __errmsg = S.message;
1051%! retval = NaN;
1052%!endfunction
1053%!test <*58411>
1054%! global __errmsg;
1055%! assert (cellfun ('factorial', {1, 2, -3}, "ErrorHandler", @__errfcn),
1056%! [1, 2, NaN]);
1057%! assert (! isempty (__errmsg));
1058%! clear -global __errmsg;
1059
1060## An error should occur if called function does not return requested outputs
1061%!function [a, b] = __counterror (x)
1062%! a = x;
1063%! if (x > 0)
1064%! b = x;
1065%! endif
1066%!endfunction
1067%!test <*66642> # First call returns fewer outputs
1068%! fail ("[a, b] = cellfun (@__counterror, {-1, 4})",
1069%! "function returned fewer than nargout values");
1070%!test <*66642> # Subsequent call returns fewer outputs
1071%! fail ("[a, b] = cellfun (@__counterror, {1, -4})",
1072%! "function returned fewer than nargout values");
1073%!test <*66642> # Non-uniform output with one call returning fewer outputs
1074%! fail ("[a, b] = cellfun (@__counterror, {1, -4}, 'UniformOutput', false)",
1075%! "function returned fewer than nargout values");
1076%!test # It's OK to return more outputs than requested
1077%! a = cellfun (@__counterror, {1, -4});
1078%! assert (a, [1, -4]);
1079%! a = cellfun (@__counterror, {-1, 4});
1080%! assert (a, [-1, 4]);
1081%! cellfun (@__counterror, {-1, 4});
1082%! assert (ans, [-1, 4]);
1083%! cellfun (@__counterror, {1, -4});
1084%! assert (ans, [1, -4]);
1085%!
1086## Testing nargout=0
1087%!function r = __counterror1 (x)
1088%! if (x > 0)
1089%! r = x;
1090%! endif
1091%!endfunction
1092## When nargout=0 and UniformOutput=true, either every call returns something,
1093## or none does.
1094%!test <*66642>
1095%! fail ("cellfun (@__counterror1, {1, -2})",
1096%! "cellfun: function returned fewer than nargout values");
1097%!test <*66642>
1098%! fail ("cellfun (@__counterror1, {-1, 2})",
1099%! "function returned unexpected number of values");
1100## When nargout=0 and UniformOutput=false, each function call can either
1101## return something or not.
1102%!test
1103%! cellfun (@__counterror1, {1, -2}, "UniformOutput", false);
1104%! assert (ans, {1, []});
1105%!test
1106%! cellfun (@__counterror1, {-1, 2}, "UniformOutput", false);
1107%! assert (ans, {[], 2});
1108%!
1109%!function varargout = __fvarg (n)
1110%! varargout = num2cell (n:-1:1);
1111%!endfunction
1112%!
1113%!test <*66642> # varargout, with number of outputs >= nargout
1114%! a = cellfun (@__fvarg, {2, 1, 3});
1115%! assert (a, [2,1,3]);
1116
1117## Test input validation
1118%!error <Invalid call> cellfun (1)
1119%!error <C must be a cell array> cellfun (@sin, [1 2 3])
1120%!error <accelerated function must be called with only one argument>
1121%! cellfun ('isempty', {1}, {2});
1122%!error <'isclass' must be called with exactly two arg> cellfun ('isclass', {1})
1123%!error <CLASS argument .* must be a string> cellfun ('isclass', {1}, 1)
1124%!error <'size' must be called with exactly two arg> cellfun ('size', {1})
1125%!error <K must be a positive integer> cellfun ('size', {1}, -1)
1126%!error <UniformOutput value must be boolean>
1127%! cellfun (@sin, {1}, 'UniformOutput', {'Cell'})
1128%!error <invalid function NAME> cellfun ('foo123bar567', {1})
1129%!error <NAME must be a string or function handle> cellfun ([1 2 3], {1})
1130%!error <arguments must be cells> cellfun (@atan2, {1}, [1])
1131%!error <dimensions mismatch> cellfun (@atan2, {1; 2}, {1, 2})
1132%!error <unrecognized parameter> cellfun ('sin', {1}, "BadParam", false)
1133%!error cellfun ('sin', {[]}, "UniformOuput")
1134%!error cellfun ('sin', {[]}, "ErrorHandler")
1135
1136*/
1137
1138
1139// Arrayfun was originally a .m file written by Bill Denney and Jaroslav
1140// Hajek. It was converted to C++ by jwe so that it could properly
1141// handle the nargout = 0 case.
1142
1143// FIXME: Function should be re-written to determine function calling form
1144// just once (since for an array the data type cannot change) and then
1145// execute that function repeatedly without the overhead of looking up
1146// the correct function in symbol table every time.
1147DEFMETHOD (arrayfun, interp, args, nargout,
1148 doc: /* -*- texinfo -*-
1149@deftypefn {} {@var{B} =} arrayfun (@var{fcn}, @var{A})
1150@deftypefnx {} {@var{B} =} arrayfun (@var{fcn}, @var{A1}, @var{A2}, @dots{})
1151@deftypefnx {} {[@var{B1}, @var{B2}, @dots{}] =} arrayfun (@var{fcn}, @var{A}, @dots{})
1152@deftypefnx {} {@var{B} =} arrayfun (@dots{}, "UniformOutput", @var{val})
1153@deftypefnx {} {@var{B} =} arrayfun (@dots{}, "ErrorHandler", @var{errfcn})
1154
1155Execute a function on each element of an array.
1156
1157This is useful for functions that do not accept array arguments. If the
1158function does accept array arguments it is @emph{better} to call the function
1159directly.
1160
1161The first input argument @var{fcn} can be a string, a function handle, an
1162inline function, or an anonymous function. The input argument @var{A} can be a
1163logical array, a numeric array, a string array, a structure array, or a cell
1164array. @code{arrayfun} passes all elements of @var{A} individually to the
1165function @var{fcn} and collects the results. The equivalent pseudo-code is
1166
1167@example
1168@group
1169cls = class (@var{fcn} (@var{A}(1));
1170@var{B} = zeros (size (@var{A}), cls);
1171for i = 1:numel (@var{A})
1172 @var{B}(i) = @var{fcn} (@var{A}(i))
1173endfor
1174@end group
1175@end example
1176
1177The named function can also take more than two input arguments, with the input
1178arguments given as third input argument @var{A2}, fourth input argument
1179@var{A2}, @enddots{} If given more than one array input argument then all
1180input arguments must have the same sizes. For example:
1181
1182@example
1183@group
1184arrayfun (@@atan2, [1, 0], [0, 1])
1185 @xresult{} [ 1.57080 0.00000 ]
1186@end group
1187@end example
1188
1189If the parameter @var{val} after a further string input argument
1190@qcode{"UniformOutput"} is set @code{true} (the default), then the named
1191function @var{fcn} must return a single element which then will be concatenated
1192into the return value and is of type matrix. Otherwise, if that parameter is
1193set to @code{false}, then the outputs are concatenated in a cell array. For
1194example:
1195
1196@example
1197@group
1198arrayfun (@@(x,y) x:y, "abc", "def", "UniformOutput", false)
1199@xresult{}
1200 @{
1201 [1,1] = abcd
1202 [1,2] = bcde
1203 [1,3] = cdef
1204 @}
1205@end group
1206@end example
1207
1208If more than one output arguments are given then the named function must return
1209the number of return values that also are expected, for example:
1210
1211@example
1212@group
1213[A, B, C] = arrayfun (@@find, [10; 0], "UniformOutput", false)
1214@xresult{}
1215A =
1216@{
1217 [1,1] = 1
1218 [2,1] = [](0x0)
1219@}
1220B =
1221@{
1222 [1,1] = 1
1223 [2,1] = [](0x0)
1224@}
1225C =
1226@{
1227 [1,1] = 10
1228 [2,1] = [](0x0)
1229@}
1230@end group
1231@end example
1232
1233If the parameter @var{errfcn} after a further string input argument
1234@qcode{"ErrorHandler"} is another string, a function handle, an inline
1235function, or an anonymous function, then @var{errfcn} defines a function to
1236call in the case that @var{fcn} generates an error. The definition of the
1237function must be of the form
1238
1239@example
1240function [@dots{}] = errfcn (@var{s}, @dots{})
1241@end example
1242
1243@noindent
1244where there is an additional input argument to @var{errfcn} relative to
1245@var{fcn}, given by @var{s}. This is a structure with the elements
1246@qcode{"identifier"}, @qcode{"message"}, and @qcode{"index"} giving,
1247respectively, the error identifier, the error message, and the index of the
1248array elements that caused the error. The size of the output argument of
1249@var{errfcn} must have the same size as the output argument of @var{fcn},
1250otherwise a real error is thrown. For example:
1251
1252@example
1253@group
1254function y = ferr (s, x), y = "MyString"; endfunction
1255arrayfun (@@str2num, [1234],
1256 "UniformOutput", false, "ErrorHandler", @@ferr)
1257@xresult{}
1258 @{
1259 [1,1] = MyString
1260 @}
1261@end group
1262@end example
1263
1264@seealso{spfun, cellfun, structfun}
1265@end deftypefn */)
1266{
1267 int nargin = args.length ();
1268
1269 if (nargin < 2)
1270 print_usage ();
1271
1272 octave_value_list retval;
1273 int nargout1 = (nargout < 1 ? 1 : nargout);
1274 bool symbol_table_lookup = false;
1275 octave_value fcn = args(0);
1276
1277 symbol_table& symtab = interp.get_symbol_table ();
1278
1279 if (fcn.is_string ())
1280 {
1281 // See if we can convert the string into a function.
1282 std::string name = args(0).string_value ();
1283
1284 if (! valid_identifier (name))
1285 fcn = get_function_handle (interp, args(0), "x");
1286 else
1287 {
1288 fcn = symtab.find_function (name);
1289
1290 if (fcn.is_undefined ())
1291 error_with_id ("Octave:invalid-input-arg",
1292 "arrayfun: invalid function NAME: %s",
1293 name.c_str ());
1294
1295 symbol_table_lookup = true;
1296 }
1297 }
1298
1299 if (fcn.is_function_handle () || fcn.is_inline_function ()
1300 || fcn.is_function ())
1301 {
1302 // The following is an optimization because the symbol table can give a
1303 // more specific function class, so this can result in fewer polymorphic
1304 // function calls as the function gets called for each value of the array.
1305
1306 if (! symbol_table_lookup)
1307 {
1308 if (fcn.is_function_handle () || fcn.class_name () == "inline")
1309 {
1310 // FIXME: instead of checking for overloaded functions as
1311 // we did previously but is no longer possible, we could
1312 // check whether the types of all the cell array elements
1313 // are the same, then lookup the function for that type
1314 // just once.
1315
1316 goto nevermind;
1317 }
1318
1320 = symtab.find_function (fcn.function_value () -> name ());
1321
1322 if (f.is_defined ())
1323 fcn = f;
1324 }
1325
1326 nevermind:
1327
1328 bool uniform_output = true;
1329 octave_value error_handler;
1330
1331 parse_options (symtab, args, nargin, uniform_output, error_handler);
1332
1333 octave_value_list inputlist (nargin, octave_value ());
1334
1335 OCTAVE_LOCAL_BUFFER (octave_value, inputs, nargin);
1336 OCTAVE_LOCAL_BUFFER (bool, mask, nargin);
1337
1338 octave_idx_type k = 1;
1339
1340 dim_vector fdims (1, 1);
1341
1342 // Collect arguments. Pre-fill scalar elements of inputlist array.
1343
1344 for (int j = 0; j < nargin; j++)
1345 {
1346 inputs[j] = args(j+1);
1347 mask[j] = inputs[j].numel () != 1;
1348
1349 if (! mask[j])
1350 inputlist(j) = inputs[j];
1351 }
1352
1353 for (int j = 0; j < nargin; j++)
1354 {
1355 if (mask[j])
1356 {
1357 fdims = inputs[j].dims ();
1358 k = inputs[j].numel ();
1359
1360 for (int i = j+1; i < nargin; i++)
1361 {
1362 if (mask[i] && inputs[i].dims () != fdims)
1363 error_with_id ("Octave:invalid-input-arg",
1364 "arrayfun: dimensions mismatch");
1365 }
1366 break;
1367 }
1368 }
1369
1370 // Apply functions.
1371
1372 if (uniform_output)
1373 {
1374 std::list<octave_value_list> idx_list (1);
1375 idx_list.front ().resize (1);
1376 std::string idx_type = "(";
1377
1378 OCTAVE_LOCAL_BUFFER (octave_value, retv, nargout1);
1379
1380 // FIXME: Initialized to prevent compiler warning.
1381 // However, it would be better if the code guaranteed that variable
1382 // was always assigned a valid value.
1383 int expected_nargout = 0;
1384 for (octave_idx_type count = 0; count < k; count++)
1385 {
1386 idx_list.front ()(0) = count + 1.0;
1387
1388 for (int j = 0; j < nargin; j++)
1389 {
1390 if (mask[j])
1391 inputlist.xelem (j) = inputs[j].index_op (idx_list);
1392 }
1393
1394 const octave_value_list y
1395 = fcn_eval (interp, count, nargout, inputlist, fcn,
1396 error_handler);
1397
1398 // Validate number of generated results is correct
1399 int y_nel = y.length ();
1400 if (count == 0)
1401 {
1402 // First time through loop. Initialize expected number of
1403 // outputs based on output from first function evaluation.
1404 if (nargout == 0)
1405 expected_nargout = (y_nel > 0 && y(0).is_defined ())
1406 ? 1 : 0;
1407 else
1408 expected_nargout = nargout;
1409 }
1410 if (y_nel < expected_nargout)
1411 error_with_id ("Octave:invalid-fun-call",
1412 "arrayfun: function returned fewer than nargout values");
1413 else if (expected_nargout == 0 && y_nel > 0 && y(0).is_defined ())
1414 error ("cellfun: function returned unexpected number of values");
1415
1416 if (expected_nargout > 0)
1417 {
1418 if (count == 0)
1419 {
1420 for (int j = 0; j < expected_nargout; j++)
1421 {
1422 if (y(j).is_defined ())
1423 {
1424 octave_value val = y(j);
1425
1426 if (val.numel () == 1)
1427 retv[j] = val.resize (fdims);
1428 else
1429 error_with_id ("Octave:invalid-fun-call",
1430 "arrayfun: all values must be scalars when UniformOutput = true");
1431 }
1432 else
1433 error ("arrayfun: function returned fewer than nargout values");
1434 }
1435 }
1436 else
1437 {
1438 for (int j = 0; j < expected_nargout; j++)
1439 {
1440 if (y(j).is_defined ())
1441 {
1442 octave_value val = y(j);
1443
1444 if (! retv[j].fast_elem_insert (count, val))
1445 {
1446 if (val.numel () == 1)
1447 {
1448 idx_list.front ()(0) = count + 1.0;
1449 retv[j].assign (octave_value::op_asn_eq,
1450 idx_type, idx_list, val);
1451 }
1452 else
1453 error_with_id ("Octave:invalid-fun-call",
1454 "arrayfun: all values must be scalars when UniformOutput = true");
1455 }
1456 }
1457 else
1458 error ("arrayfun: function returned fewer than nargout values");
1459 }
1460 }
1461 }
1462 }
1463
1464 retval.resize (nargout1);
1465
1466 for (int j = 0; j < nargout1; j++)
1467 {
1468 if (nargout > 0 && retv[j].is_undefined ())
1469 retval(j) = NDArray (fdims);
1470 else
1471 retval(j) = retv[j];
1472 }
1473 }
1474 else
1475 {
1476 std::list<octave_value_list> idx_list (1);
1477 idx_list.front ().resize (1);
1478 std::string idx_type = "(";
1479
1480 OCTAVE_LOCAL_BUFFER (Cell, results, nargout1);
1481
1482 for (int j = 0; j < nargout1; j++)
1483 results[j].resize (fdims, Matrix ());
1484
1485 bool have_some_output = false;
1486
1487 for (octave_idx_type count = 0; count < k; count++)
1488 {
1489 idx_list.front ()(0) = count + 1.0;
1490
1491 for (int j = 0; j < nargin; j++)
1492 {
1493 if (mask[j])
1494 inputlist.xelem (j) = inputs[j].index_op (idx_list);
1495 }
1496
1497 const octave_value_list y
1498 = fcn_eval (interp, count, nargout, inputlist, fcn,
1499 error_handler);
1500
1501 if (nargout > 0 && y.length () < nargout)
1502 error_with_id ("Octave:invalid-fun-call",
1503 "arrayfun: function returned fewer than nargout values");
1504
1505 if (nargout > 0
1506 || (nargout == 0
1507 && y.length () > 0 && y(0).is_defined ()))
1508 {
1509 int num_to_copy = y.length ();
1510
1511 if (num_to_copy > nargout1)
1512 num_to_copy = nargout1;
1513
1514 if (num_to_copy > 0)
1515 have_some_output = true;
1516
1517 for (int j = 0; j < num_to_copy; j++)
1518 {
1519 if (y(j).is_undefined ())
1520 error ("arrayfun: function returned fewer than nargout values");
1521 results[j](count) = y(j);
1522 }
1523 }
1524 }
1525
1526 if (have_some_output || fdims.any_zero ())
1527 {
1528 retval.resize (nargout1);
1529
1530 for (int j = 0; j < nargout1; j++)
1531 retval(j) = results[j];
1532 }
1533 }
1534 }
1535 else
1536 error_with_id ("Octave:invalid-fun-call",
1537 "arrayfun: argument NAME must be a string or function handle");
1538
1539 return retval;
1540}
1541
1542/*
1543%!function r = __f11 (x)
1544%! r = x;
1545%!endfunction
1546
1547%!function __f01 (x)
1548%! ## Empty function
1549%!endfunction
1550
1551%!test
1552%! __arrayfun_test_num_outputs__ = -1;
1553%!
1554%! function r = __subf11 (x)
1555%! __arrayfun_test_num_outputs__ = nargout;
1556%! r = x;
1557%! endfunction
1558%!
1559%! arrayfun ("__subf11", {1});
1560%! assert (__arrayfun_test_num_outputs__, 0);
1561%!
1562%! __arrayfun_test_num_outputs__ = -1;
1563%! x = arrayfun ("__subf11", {1});
1564%! assert (__arrayfun_test_num_outputs__, 1);
1565
1566%!test
1567%! __arrayfun_test_num_outputs__ = -1;
1568%!
1569%! function __subf01 (x)
1570%! __arrayfun_test_num_outputs__ = nargout;
1571%! endfunction
1572%!
1573%! arrayfun ("__subf01", {1});
1574%! assert (__arrayfun_test_num_outputs__, 0);
1575
1576%!error x = arrayfun (@__f01, [1, 2])
1577
1578%!test
1579%! assert (arrayfun (@__f11, [1, 2]), [1, 2]);
1580%! assert (arrayfun (@__f11, [1, 2], "uniformoutput", false), {1, 2});
1581%! assert (arrayfun (@__f11, {1, 2}), {1, 2});
1582%! assert (arrayfun (@__f11, {1, 2}, "uniformoutput", false), {{1}, {2}});
1583
1584%!assert (arrayfun (@ones, 1, [2,3], "uniformoutput", false), {[1,1], [1,1,1]})
1585
1586## Test function to check the "Errorhandler" option
1587%!function z = __arrayfunerror (S, varargin)
1588%! z = S;
1589%!endfunction
1590## First input argument can be a string, an inline function, a
1591## function_handle or an anonymous function
1592%!test
1593%! arrayfun (@isequal, [false, true], [true, true]); # No output argument
1594%!error
1595%! arrayfun (@isequal); # One or less input arguments
1596%!test
1597%! A = arrayfun ("isequal", [false, true], [true, true]);
1598%! assert (A, [false, true]);
1599%!test
1600%! warning ('off', 'Octave:legacy-function', 'local');
1601%! A = arrayfun (inline ("(x == y)", "x", "y"), [false, true], [true, true]);
1602%! assert (A, [false, true]);
1603%!test
1604%! A = arrayfun (@isequal, [false, true], [true, true]);
1605%! assert (A, [false, true]);
1606%!test
1607%! A = arrayfun (@(x,y) isequal (x,y), [false, true], [true, true]);
1608%! assert (A, [false, true]);
1609
1610## Number of input and output arguments may be greater than one
1611%!test
1612%! A = arrayfun (@(x) islogical (x), false);
1613%! assert (A, true);
1614%!test
1615%! A = arrayfun (@(x,y,z) x + y + z, [1, 1, 1], [2, 2, 2], [3, 4, 5]);
1616%! assert (A, [6, 7, 8], 1e-16);
1617%!test # Two input arguments of different types
1618%! A = arrayfun (@(x,y) islogical (x) && ischar (y), false, "a");
1619%! assert (A, true);
1620%!test # Pass another variable to the anonymous function
1621%! y = true;
1622%! A = arrayfun (@(x) islogical (x && y), false);
1623%! assert (A, true);
1624%!test # Three output arguments of different type
1625%! [A, B, C] = arrayfun (@find, [10, 11; 0, 12], "UniformOutput", false);
1626%! assert (isequal (A, {true, true; [], true}));
1627%! assert (isequal (B, {true, true; [], true}));
1628%! assert (isequal (C, {10, 11; [], 12}));
1629
1630## Input arguments can be of type logical
1631%!test
1632%! A = arrayfun (@(x,y) x == y, [false, true], [true, true]);
1633%! assert (A, [false, true]);
1634%!test
1635%! A = arrayfun (@(x,y) x == y, [false; true], [true; true], "UniformOutput", true);
1636%! assert (A, [false; true]);
1637%!test
1638%! A = arrayfun (@(x) x, [false, true, false, true], "UniformOutput", false);
1639%! assert (A, {false, true, false, true});
1640%!test # Three output arguments of same type
1641%! [A, B, C] = arrayfun (@find, [true, false; false, true], "UniformOutput", false);
1642%! assert (isequal (A, {true, []; [], true}));
1643%! assert (isequal (B, {true, []; [], true}));
1644%! assert (isequal (C, {true, []; [], true}));
1645%!test
1646%! A = arrayfun (@(x,y) array2str (x,y), true, true, ...
1647%! "ErrorHandler", @__arrayfunerror);
1648%! assert (isfield (A, "identifier"), true);
1649%! assert (isfield (A, "message"), true);
1650%! assert (isfield (A, "index"), true);
1651%! assert (isempty (A.message), false);
1652%! assert (A.index, 1);
1653%!test # Overwriting setting of "UniformOutput" true
1654%! A = arrayfun (@(x,y) array2str (x,y), true, true, "UniformOutput", true, ...
1655%! "ErrorHandler", @__arrayfunerror);
1656%! assert (isfield (A, "identifier"), true);
1657%! assert (isfield (A, "message"), true);
1658%! assert (isfield (A, "index"), true);
1659%! assert (isempty (A.message), false);
1660%! assert (A.index, 1);
1661
1662## Input arguments can be of type numeric
1663%!test
1664%! A = arrayfun (@(x,y) x>y, [1.1, 4.2], [3.1, 2+3*i]);
1665%! assert (A, [false, true]);
1666%!test
1667%! A = arrayfun (@(x,y) x>y, [1.1, 4.2; 2, 4], [3.1, 2; 2, 4+2*i], "UniformOutput", true);
1668%! assert (A, [false, true; false, false]);
1669%!test
1670%! A = arrayfun (@(x,y) x:y, [1.1, 4], [3.1, 6], "UniformOutput", false);
1671%! assert (isequal (A{1}, [1.1, 2.1, 3.1]));
1672%! assert (isequal (A{2}, [4, 5, 6]));
1673%!test # Three output arguments of different type
1674%! [A, B, C] = arrayfun (@find, [10, 11; 0, 12], "UniformOutput", false);
1675%! assert (isequal (A, {true, true; [], true}));
1676%! assert (isequal (B, {true, true; [], true}));
1677%! assert (isequal (C, {10, 11; [], 12}));
1678%!test
1679%! A = arrayfun (@(x,y) array2str (x,y), {1.1, 4}, {3.1, 6}, ...
1680%! "ErrorHandler", @__arrayfunerror);
1681%! B = isfield (A(1), "message") && isfield (A(1), "index");
1682%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
1683%! [true, true]);
1684%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
1685%! [true, true]);
1686%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
1687%! [true, true]);
1688%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
1689%! [false, false]);
1690%! assert ([A(1).index, A(2).index], [1, 2]);
1691%!test # Overwriting setting of "UniformOutput" true
1692%! A = arrayfun (@(x,y) array2str (x,y), {1.1, 4}, {3.1, 6}, ...
1693%! "UniformOutput", true, "ErrorHandler", @__arrayfunerror);
1694%! B = isfield (A(1), "message") && isfield (A(1), "index");
1695%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
1696%! [true, true]);
1697%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
1698%! [true, true]);
1699%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
1700%! [true, true]);
1701%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
1702%! [false, false]);
1703%! assert ([A(1).index, A(2).index], [1, 2]);
1704
1705## Input arguments can be of type character or strings
1706%!test
1707%! A = arrayfun (@(x,y) x>y, ["ad", "c", "ghi"], ["cc", "d", "fgh"]);
1708%! assert (A, [false, true, false, true, true, true]);
1709%!test
1710%! A = arrayfun (@(x,y) x>y, ["a"; "f"], ["c"; "d"], "UniformOutput", true);
1711%! assert (A, [false; true]);
1712%!test
1713%! A = arrayfun (@(x,y) x:y, ["a", "d"], ["c", "f"], "UniformOutput", false);
1714%! assert (A, {"abc", "def"});
1715%!test
1716%! A = arrayfun (@(x,y) cell2str (x,y), ["a", "d"], ["c", "f"], ...
1717%! "ErrorHandler", @__arrayfunerror);
1718%! B = isfield (A(1), "identifier") && isfield (A(1), "message") ...
1719%! && isfield (A(1), "index");
1720%! assert (B, true);
1721
1722## Input arguments can be of type structure
1723%!test
1724%! a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2);
1725%! A = arrayfun (@(x,y) (x.a < y.a) && (x.b > y.b), a, b);
1726%! assert (A, true);
1727%!test
1728%! a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2);
1729%! A = arrayfun (@(x,y) (x.a < y.a) && (x.b > y.b), a, b, "UniformOutput", true);
1730%! assert (A, true);
1731%!test
1732%! a = struct ("a", 1.1, "b", 4.2); b = struct ("a", 3.1, "b", 2);
1733%! A = arrayfun (@(x,y) x.a:y.a, a, b, "UniformOutput", false);
1734%! assert (isequal (A, {[1.1, 2.1, 3.1]}));
1735%!test
1736%! A = arrayfun (@(x) mat2str (x), "a", "ErrorHandler", @__arrayfunerror);
1737%! assert (isfield (A, "identifier"), true);
1738%! assert (isfield (A, "message"), true);
1739%! assert (isfield (A, "index"), true);
1740%! assert (isempty (A.message), false);
1741%! assert (A.index, 1);
1742%!test # Overwriting setting of "UniformOutput" true
1743%! A = arrayfun (@(x) mat2str (x), "a", "UniformOutput", true, ...
1744%! "ErrorHandler", @__arrayfunerror);
1745%! assert (isfield (A, "identifier"), true);
1746%! assert (isfield (A, "message"), true);
1747%! assert (isfield (A, "index"), true);
1748%! assert (isempty (A.message), false);
1749%! assert (A.index, 1);
1750
1751## Input arguments can be of type cell array
1752%!test
1753%! A = arrayfun (@(x,y) x{1} < y{1}, {1.1, 4.2}, {3.1, 2});
1754%! assert (A, [true, false]);
1755%!test
1756%! A = arrayfun (@(x,y) x{1} < y{1}, {1.1; 4.2}, {3.1; 2}, "UniformOutput", true);
1757%! assert (A, [true; false]);
1758%!test
1759%! A = arrayfun (@(x,y) x{1} < y{1}, {1.1, 4.2}, {3.1, 2}, "UniformOutput", false);
1760%! assert (A, {true, false});
1761%!test
1762%! A = arrayfun (@(x,y) num2str(x,y), {1.1, 4.2}, {3.1, 2}, "ErrorHandler", @__arrayfunerror);
1763%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
1764%! [true, true]);
1765%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
1766%! [true, true]);
1767%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
1768%! [true, true]);
1769%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
1770%! [false, false]);
1771%! assert ([A(1).index, A(2).index], [1, 2]);
1772%!test
1773%! A = arrayfun (@(x,y) num2str (x,y), {1.1, 4.2}, {3.1, 2}, ...
1774%! "UniformOutput", true, "ErrorHandler", @__arrayfunerror);
1775%! assert ([(isfield (A(1), "identifier")), (isfield (A(2), "identifier"))],
1776%! [true, true]);
1777%! assert ([(isfield (A(1), "message")), (isfield (A(2), "message"))],
1778%! [true, true]);
1779%! assert ([(isfield (A(1), "index")), (isfield (A(2), "index"))],
1780%! [true, true]);
1781%! assert ([(isempty (A(1).message)), (isempty (A(2).message))],
1782%! [false, false]);
1783%! assert ([A(1).index, A(2).index], [1, 2]);
1784
1785## An error should occur if called function does not return requested outputs
1786%!test <*66642> # First call returns fewer outputs
1787%! fail ("[a, b] = arrayfun (@__counterror, [-1, 4])",
1788%! "function returned fewer than nargout values");
1789%!test <*66642> # Subsequent call returns fewer outputs
1790%! fail ("[a, b] = arrayfun (@__counterror, [1, -4])",
1791%! "function returned fewer than nargout values");
1792%!test <*66642> # Non-uniform output
1793%! fail ("[a, b] = arrayfun (@__counterror, [1, -4], 'UniformOutput', false)",
1794%! "function returned fewer than nargout values");
1795%!test # It's OK to return more outputs than requested
1796%! a = arrayfun (@__counterror, [1, -4]);
1797%! assert (a, [1, -4]);
1798%! a = arrayfun (@__counterror, [-1, 4]);
1799%! assert (a, [-1, 4]);
1800%! arrayfun (@__counterror, [-1, 4]);
1801%! assert (ans, [-1, 4]);
1802%! arrayfun (@__counterror, [1, -4]);
1803%! assert (ans, [1, -4]);
1804%!
1805## Testing nargout=0
1806## With nargout=0 and UniformOutput=true, either every call returns something,
1807## or none does.
1808%!test <*66642>
1809%! fail ("arrayfun (@__counterror1, [1, -2])",
1810%! "arrayfun: function returned fewer than nargout values");
1811%!test <*66642>
1812%! fail ("arrayfun (@__counterror1, [-1, 2])",
1813%! "function returned unexpected number of values");
1814## When nargout=0 and UniformOutput=false, each function call can either
1815## return something or not.
1816%!test
1817%! arrayfun (@__counterror1, [1,-2], "UniformOutput", false);
1818%! assert (ans, {1, []});
1819%!test
1820%! arrayfun (@__counterror1, [-1,2], "UniformOutput", false);
1821%! assert (ans, {[], 2});
1822%!
1823%!function varargout = __fvarg (n)
1824%! varargout = num2cell (n:-1:1);
1825%!endfunction
1826%!
1827%!test # varargout, with number of outputs >= nargout
1828%! a = arrayfun (@__fvarg, [2, 1, 3]);
1829%! assert (a, [2, 1, 3]);
1830*/
1831
1832static void
1833do_num2cell_helper (const dim_vector& dv,
1834 const Array<int>& dimv,
1835 dim_vector& celldv, dim_vector& arraydv,
1836 Array<int>& perm)
1837{
1838 int dvl = dimv.numel ();
1839 int maxd = dv.ndims ();
1840 celldv = dv;
1841 for (int i = 0; i < dvl; i++)
1842 maxd = std::max (maxd, dimv(i));
1843 if (maxd > dv.ndims ())
1844 celldv.resize (maxd, 1);
1845 arraydv = celldv;
1846
1847 OCTAVE_LOCAL_BUFFER_INIT (bool, sing, maxd, false);
1848
1849 perm.clear (maxd, 1);
1850 for (int i = 0; i < dvl; i++)
1851 {
1852 int k = dimv(i) - 1;
1853 if (k < 0)
1854 error ("num2cell: dimension indices must be positive");
1855
1856 if (i > 0 && k < dimv(i-1) - 1)
1857 error ("num2cell: dimension indices must be strictly increasing");
1858
1859 sing[k] = true;
1860 perm(i) = k;
1861 }
1862
1863 for (int k = 0, i = dvl; k < maxd; k++)
1864 if (! sing[k])
1865 perm(i++) = k;
1866
1867 for (int i = 0; i < maxd; i++)
1868 if (sing[i])
1869 celldv(i) = 1;
1870 else
1871 arraydv(i) = 1;
1872}
1873
1874template <typename NDA>
1875static inline typename NDA::element_type
1876do_num2cell_elem (const NDA& array, octave_idx_type i)
1877{ return array(i); }
1878
1879static inline Cell
1880do_num2cell_elem (const Cell& array, octave_idx_type i)
1881{ return Cell (array(i)); }
1882
1883template <typename NDA>
1884static Cell
1885do_num2cell (const NDA& array, const Array<int>& dimv)
1886{
1887 if (dimv.isempty ())
1888 {
1889 Cell retval (array.dims ());
1890 octave_idx_type nel = array.numel ();
1891 for (octave_idx_type i = 0; i < nel; i++)
1892 retval.xelem (i) = do_num2cell_elem (array, i);
1893
1894 return retval;
1895 }
1896 else
1897 {
1898 dim_vector celldv, arraydv;
1899 Array<int> perm;
1900 do_num2cell_helper (array.dims (), dimv, celldv, arraydv, perm);
1901
1902 NDA parray = array.permute (perm);
1903
1904 octave_idx_type nela = arraydv.numel ();
1905 octave_idx_type nelc = celldv.numel ();
1906 parray = parray.reshape (dim_vector (nela, nelc));
1907
1908 Cell retval (celldv);
1909 for (octave_idx_type i = 0; i < nelc; i++)
1910 {
1911 retval.xelem (i) = NDA (parray.column (i).reshape (arraydv));
1912 }
1913
1914 return retval;
1915 }
1916}
1917
1918// FIXME: this is a mess, but if a size method for the object exists,
1919// we have to call it to get the size of the object instead of using the
1920// internal dims method.
1921
1922static dim_vector
1923get_object_dims (octave_value& obj)
1924{
1925 dim_vector retval;
1926
1927 Matrix m = obj.size ();
1928
1929 int n = m.numel ();
1930
1931 retval.resize (n);
1932
1933 for (int i = 0; i < n; i++)
1934 retval(i) = m(i);
1935
1936 return retval;
1937}
1938
1939static Cell
1940do_object2cell (const octave_value& obj, const Array<int>& dimv)
1941{
1942 Cell retval;
1943
1944 // FIXME: this copy is only needed because the octave_value::size
1945 // method is not const.
1946 octave_value array = obj;
1947
1948 if (! dimv.isempty ())
1949 error ("num2cell (A, dim) not implemented for class objects");
1950
1951 const dim_vector& dv = get_object_dims (array);
1952
1953 retval.resize (dv);
1954
1955 octave_value_list idx (1);
1956
1957 for (octave_idx_type i = 0; i < dv.numel (); i++)
1958 {
1959 octave_quit ();
1960
1961 idx(0) = double (i+1);
1962
1963 retval.xelem (i) = array.single_subsref ("(", idx);
1964 }
1965
1966 return retval;
1967}
1968
1969DEFUN (num2cell, args, ,
1970 doc: /* -*- texinfo -*-
1971@deftypefn {} {@var{C} =} num2cell (@var{A})
1972@deftypefnx {} {@var{C} =} num2cell (@var{A}, @var{dim})
1973Convert the numeric matrix @var{A} to a cell array.
1974
1975When no @var{dim} is specified, each element of @var{A} becomes a 1x1 element
1976in the output @var{C}.
1977
1978If @var{dim} is defined then individual elements of @var{C} contain all of the
1979elements from @var{A} along the specified dimension. @var{dim} may also be a
1980vector of dimensions with the same rule applied.
1981
1982For example:
1983
1984@example
1985x = [1,2;3,4]
1986@xresult{}
1987 1 2
1988 3 4
1989
1990## each element of A becomes a 1x1 element of C
1991num2cell (x)
1992 @xresult{}
1993 @{
1994 [1,1] = 1
1995 [2,1] = 3
1996 [1,2] = 2
1997 [2,2] = 4
1998 @}
1999## all rows (dim 1) of A appear in each element of C
2000num2cell (x, 1)
2001 @xresult{}
2002 @{
2003 [1,1] =
2004 1
2005 3
2006 [1,2] =
2007 2
2008 4
2009 @}
2010## all columns (dim 2) of A appear in each element of C
2011num2cell (x, 2)
2012 @xresult{}
2013 @{
2014 [1,1] =
2015 1 2
2016 [2,1] =
2017 3 4
2018 @}
2019## all rows and cols appear in each element of C
2020## (hence, only 1 output)
2021num2cell (x, [1, 2])
2022 @xresult{}
2023 @{
2024 [1,1] =
2025 1 2
2026 3 4
2027 @}
2028@end example
2029
2030@seealso{mat2cell}
2031@end deftypefn */)
2032{
2033 int nargin = args.length ();
2034
2035 if (nargin < 1 || nargin > 2)
2036 print_usage ();
2037
2038 octave_value retval;
2039
2040 octave_value array = args(0);
2041
2042 Array<int> dimv;
2043 if (nargin > 1)
2044 dimv = args(1).int_vector_value (true);
2045
2046 if (array.islogical ())
2047 retval = do_num2cell (array.bool_array_value (), dimv);
2048 else if (array.is_char_matrix ())
2049 retval = do_num2cell (array.char_array_value (), dimv);
2050 else if (array.isnumeric ())
2051 {
2052 if (array.isinteger ())
2053 {
2054 if (array.is_int8_type ())
2055 retval = do_num2cell (array.int8_array_value (), dimv);
2056 else if (array.is_int16_type ())
2057 retval = do_num2cell (array.int16_array_value (), dimv);
2058 else if (array.is_int32_type ())
2059 retval = do_num2cell (array.int32_array_value (), dimv);
2060 else if (array.is_int64_type ())
2061 retval = do_num2cell (array.int64_array_value (), dimv);
2062 else if (array.is_uint8_type ())
2063 retval = do_num2cell (array.uint8_array_value (), dimv);
2064 else if (array.is_uint16_type ())
2065 retval = do_num2cell (array.uint16_array_value (), dimv);
2066 else if (array.is_uint32_type ())
2067 retval = do_num2cell (array.uint32_array_value (), dimv);
2068 else if (array.is_uint64_type ())
2069 retval = do_num2cell (array.uint64_array_value (), dimv);
2070 }
2071 else if (array.iscomplex ())
2072 {
2073 if (array.is_single_type ())
2074 retval = do_num2cell (array.float_complex_array_value (), dimv);
2075 else
2076 retval = do_num2cell (array.complex_array_value (), dimv);
2077 }
2078 else
2079 {
2080 if (array.is_single_type ())
2081 retval = do_num2cell (array.float_array_value (), dimv);
2082 else
2083 retval = do_num2cell (array.array_value (), dimv);
2084 }
2085 }
2086 else if (array.isobject ())
2087 retval = do_object2cell (array, dimv);
2088 else if (array.isstruct ())
2089 retval = do_num2cell (array.map_value (), dimv);
2090 else if (array.iscell ())
2091 retval = do_num2cell (array.cell_value (), dimv);
2092 else
2093 err_wrong_type_arg ("num2cell", array);
2094
2095 return retval;
2096}
2097
2098/*
2099%!assert (num2cell ([1,2;3,4]), {1,2;3,4})
2100%!assert (num2cell ([1,2;3,4], 1), {[1;3],[2;4]})
2101%!assert (num2cell ([1,2;3,4], 2), {[1,2];[3,4]})
2102*/
2103
2104static bool
2105mat2cell_mismatch (const dim_vector& dv,
2106 const Array<octave_idx_type> *d, int nd)
2107{
2108 for (int i = 0; i < nd; i++)
2109 {
2110 octave_idx_type s = 0;
2111 for (octave_idx_type j = 0; j < d[i].numel (); j++)
2112 s += d[i](j);
2113
2114 octave_idx_type r = (i < dv.ndims () ? dv(i) : 1);
2115
2116 if (s != r)
2117 error ("mat2cell: mismatch on dimension %d (%" OCTAVE_IDX_TYPE_FORMAT
2118 " != %" OCTAVE_IDX_TYPE_FORMAT ")", i+1, r, s);
2119 }
2120
2121 return false;
2122}
2123
2124template <typename container>
2125static void
2126prepare_idx (container *idx, int idim, int nd,
2128{
2129 octave_idx_type nidx = (idim < nd ? d[idim].numel () : 1);
2130 if (nidx == 1)
2131 idx[0] = idx_vector::colon;
2132 else
2133 {
2134 octave_idx_type l = 0;
2135 for (octave_idx_type i = 0; i < nidx; i++)
2136 {
2137 octave_idx_type u = l + d[idim](i);
2138 idx[i] = idx_vector (l, u);
2139 l = u;
2140 }
2141 }
2142}
2143
2144// 2-D specialization, works for Array, Sparse and octave_map.
2145// Uses 1-D or 2-D indexing.
2146
2147template <typename Array2D>
2148static Cell
2149do_mat2cell_2d (const Array2D& a, const Array<octave_idx_type> *d, int nd)
2150{
2151 Cell retval;
2152
2153 panic_unless (nd == 1 || nd == 2);
2154 panic_unless (a.ndims () == 2);
2155
2156 if (mat2cell_mismatch (a.dims (), d, nd))
2157 return retval;
2158
2159 octave_idx_type nridx = d[0].numel ();
2160 octave_idx_type ncidx = (nd == 1 ? 1 : d[1].numel ());
2161 retval.clear (nridx, ncidx);
2162
2163 int ivec = -1;
2164 if (a.rows () > 1 && a.cols () == 1 && ncidx == 1)
2165 ivec = 0;
2166 else if (a.rows () == 1 && nridx == 1 && nd == 2)
2167 ivec = 1;
2168
2169 if (ivec >= 0)
2170 {
2171 // Vector split. Use 1-D indexing.
2172 octave_idx_type l = 0;
2173 octave_idx_type nidx = (ivec == 0 ? nridx : ncidx);
2174 for (octave_idx_type i = 0; i < nidx; i++)
2175 {
2176 octave_idx_type u = l + d[ivec](i);
2177 if constexpr (std::is_same_v<Array2D, Cell>)
2178 retval.xelem (i) = Cell (a.index (idx_vector (l, u)));
2179 else
2180 retval.xelem (i) = a.index (idx_vector (l, u));
2181 l = u;
2182 }
2183 }
2184 else
2185 {
2186 // General 2-D case. Use 2-D indexing.
2187 OCTAVE_LOCAL_BUFFER (idx_vector, ridx, nridx);
2188 prepare_idx (ridx, 0, nd, d);
2189
2190 OCTAVE_LOCAL_BUFFER (idx_vector, cidx, ncidx);
2191 prepare_idx (cidx, 1, nd, d);
2192
2193 for (octave_idx_type j = 0; j < ncidx; j++)
2194 for (octave_idx_type i = 0; i < nridx; i++)
2195 {
2196 octave_quit ();
2197
2198 if constexpr (std::is_same_v<Array2D, Cell>)
2199 retval.xelem (i, j) = Cell (a.index (ridx[i], cidx[j]));
2200 else
2201 retval.xelem (i, j) = a.index (ridx[i], cidx[j]);
2202 }
2203 }
2204
2205 return retval;
2206}
2207
2208// Nd case. Works for Arrays and octave_map.
2209// Uses Nd indexing.
2210
2211template <typename ArrayND>
2212Cell
2213do_mat2cell_nd (const ArrayND& a, const Array<octave_idx_type> *d, int nd)
2214{
2215 Cell retval;
2216
2217 panic_unless (nd >= 1);
2218
2219 if (mat2cell_mismatch (a.dims (), d, nd))
2220 return retval;
2221
2222 // For each dimension, count the number of partitions specified.
2223 // For example, "mat2cell (A, [1 1 1], [2 2])" has 3 partitions on dim1
2224 // and 2 partitions on dim2. Number of dimension (nd) is 2 for this example.
2225 dim_vector retdv = dim_vector::alloc (nd);
2227 octave_idx_type idxtot = 0; // Number of idx operations. 5 in example.
2228 for (int i = 0; i < nd; i++)
2229 {
2230 retdv(i) = nidx[i] = d[i].numel ();
2231 idxtot += nidx[i];
2232 }
2233
2234 if (nd == 1)
2235 retdv(1) = 1; // All Octave arrays have at least two dimensions.
2236 retval.clear (retdv); // Resize retval based on calculated partitions.
2237
2238 OCTAVE_LOCAL_BUFFER (idx_vector, xidx, idxtot);
2239 OCTAVE_LOCAL_BUFFER (idx_vector *, idx, nd);
2240
2241 // Loop over all dimensions (specified partitions) and prepare an idx_vector
2242 // to retrieve the requested elements. The partitions are specified in
2243 // input parameter 'd' which is an Array of octave_idx_type. In the example,
2244 // d[0] = [1 1 1].
2245 idxtot = 0;
2246 for (int i = 0; i < nd; i++)
2247 {
2248 idx[i] = xidx + idxtot;
2249 prepare_idx (idx[i], i, nd, d);
2250 idxtot += nidx[i];
2251 }
2252
2254 // Declare array of index vectors which will perform indexing.
2255 // Initialize to magic colon (':') so that dimensions that are not actually
2256 // specified will be collapsed.
2257 Array<idx_vector> ra_idx (dim_vector (1, std::max (nd, a.ndims ())),
2258 idx_vector::colon);
2259
2260 const octave_idx_type retnumel = retval.numel ();
2261 for (octave_idx_type j = 0; j < retnumel; j++)
2262 {
2263 octave_quit ();
2264
2265 // Copy prepared indices for this iteration to ra_idx.
2266 for (int i = 0; i < nd; i++)
2267 ra_idx.xelem (i) = idx[i][ridx[i]];
2268
2269 // Perform indexing operation and store in output retval.
2270 if constexpr (std::is_same_v<ArrayND, Cell>)
2271 retval.xelem (j) = Cell (a.index (ra_idx));
2272 else
2273 retval.xelem (j) = a.index (ra_idx);
2274
2275 // DO NOT increment on last loop because it will overflow past
2276 // declared size of ridx (bug #63682).
2277 if (j < (retnumel - 1))
2278 retdv.increment_index (ridx);
2279 }
2280
2281 return retval;
2282}
2283
2284// Dispatcher.
2285template <typename ArrayND>
2286Cell
2287do_mat2cell (const ArrayND& a, const Array<octave_idx_type> *d, int nd)
2288{
2289 if (a.ndims () == 2 && nd <= 2)
2290 return do_mat2cell_2d (a, d, nd);
2291 else
2292 return do_mat2cell_nd (a, d, nd);
2293}
2294
2295// General case. Works for any class supporting do_index_op.
2296// Uses Nd indexing.
2297
2298Cell
2300{
2301 Cell retval;
2302
2303 panic_unless (nd >= 1);
2304
2305 if (mat2cell_mismatch (a.dims (), d, nd))
2306 return retval;
2307
2308 dim_vector rdv = dim_vector::alloc (nd);
2310 octave_idx_type idxtot = 0;
2311 for (int i = 0; i < nd; i++)
2312 {
2313 rdv(i) = nidx[i] = d[i].numel ();
2314 idxtot += nidx[i];
2315 }
2316
2317 retval.clear (rdv);
2318
2319 OCTAVE_LOCAL_BUFFER (octave_value, xidx, idxtot);
2320 OCTAVE_LOCAL_BUFFER (octave_value *, idx, nd);
2321
2322 idxtot = 0;
2323 for (int i = 0; i < nd; i++)
2324 {
2325 idx[i] = xidx + idxtot;
2326 prepare_idx (idx[i], i, nd, d);
2327 idxtot += nidx[i];
2328 }
2329
2331 octave_value_list ra_idx (std::max (nd, a.ndims ()),
2333
2334 for (octave_idx_type j = 0; j < retval.numel (); j++)
2335 {
2336 octave_quit ();
2337
2338 for (int i = 0; i < nd; i++)
2339 ra_idx(i) = idx[i][ridx[i]];
2340
2341 retval.xelem (j) = a.index_op (ra_idx);
2342
2343 rdv.increment_index (ridx);
2344 }
2345
2346 return retval;
2347}
2348
2349DEFUN (mat2cell, args, ,
2350 doc: /* -*- texinfo -*-
2351@deftypefn {} {@var{C} =} mat2cell (@var{A}, @var{dim1}, @var{dim2}, @dots{}, @var{dimi}, @dots{}, @var{dimn})
2352@deftypefnx {} {@var{C} =} mat2cell (@var{A}, @var{rowdim})
2353Convert the matrix @var{A} to a cell array @var{C}.
2354
2355Each dimension argument (@var{dim1}, @var{dim2}, etc.@:) is a vector of
2356integers which specifies how to divide that dimension's elements amongst the
2357new elements in the output @var{C}. The number of elements in the @var{i}-th
2358dimension is @code{size (@var{A}, @var{i})}. Because all elements in @var{A}
2359must be partitioned, there is a requirement that @code{sum (@var{dimi}) == size
2360(@var{A}, i)}. The size of the output cell @var{C} is numel (@var{dim1}) x
2361numel (@var{dim2}) x @dots{} x numel (@var{dimn}).
2362
2363Given a single dimensional argument, @var{rowdim}, the output is divided into
2364rows as specified. All other dimensions are not divided and thus all
2365columns (dim 2), pages (dim 3), etc.@: appear in each output element.
2366
2367Examples
2368
2369@example
2370x = reshape (1:12, [3, 4])'
2371@xresult{}
2372 1 2 3
2373 4 5 6
2374 7 8 9
2375 10 11 12
2376
2377@group
2378## The 4 rows (dim1) are divided in to two cell elements
2379## with 2 rows each.
2380## The 3 cols (dim2) are divided in to three cell elements
2381## with 1 col each.
2382mat2cell (x, [2,2], [1,1,1])
2383@xresult{}
2384@{
2385 [1,1] =
2386
2387 1
2388 4
2389
2390 [2,1] =
2391
2392 7
2393 10
2394
2395 [1,2] =
2396
2397 2
2398 5
2399
2400 [2,2] =
2401 8
2402 11
2403
2404 [1,3] =
2405
2406 3
2407 6
2408
2409 [2,3] =
2410 9
2411 12
2412@}
2413@end group
2414
2415@group
2416## The 4 rows (dim1) are divided in to two cell elements
2417## with a 3/1 split.
2418## All columns appear in each output element.
2419mat2cell (x, [3,1])
2420@xresult{}
2421@{
2422 [1,1] =
2423
2424 1 2 3
2425 4 5 6
2426 7 8 9
2427
2428 [2,1] =
2429
2430 10 11 12
2431@}
2432@end group
2433@end example
2434
2435@seealso{num2cell, cell2mat}
2436@end deftypefn */)
2437{
2438 int nargin = args.length ();
2439
2440 if (nargin < 2)
2441 print_usage ();
2442
2443 octave_value retval;
2444
2445 // Prepare indices.
2447
2448 for (int i = 1; i < nargin; i++)
2449 d[i-1] = args(i).octave_idx_type_vector_value (true);
2450
2451 octave_value a = args(0);
2452 bool sparse = a.issparse ();
2453 if (sparse && nargin > 3)
2454 error ("mat2cell: sparse arguments only support 2-D indexing");
2455
2456 switch (a.builtin_type ())
2457 {
2458 case btyp_double:
2459 {
2460 if (sparse)
2461 retval = do_mat2cell_2d (a.sparse_matrix_value (), d, nargin-1);
2462 else
2463 retval = do_mat2cell (a.array_value (), d, nargin - 1);
2464 }
2465 break;
2466
2467 case btyp_complex:
2468 {
2469 if (sparse)
2470 retval = do_mat2cell_2d (a.sparse_complex_matrix_value (), d,
2471 nargin-1);
2472 else
2473 retval = do_mat2cell (a.complex_array_value (), d, nargin - 1);
2474 }
2475 break;
2476
2477#define BTYP_BRANCH(X, Y) \
2478 case btyp_ ## X: \
2479 retval = do_mat2cell (a.Y ## _value (), d, nargin - 1); \
2480 break
2481
2482 BTYP_BRANCH (float, float_array);
2483 BTYP_BRANCH (float_complex, float_complex_array);
2484 BTYP_BRANCH (bool, bool_array);
2485 BTYP_BRANCH (char, char_array);
2486
2487 BTYP_BRANCH (int8, int8_array);
2488 BTYP_BRANCH (int16, int16_array);
2489 BTYP_BRANCH (int32, int32_array);
2490 BTYP_BRANCH (int64, int64_array);
2491 BTYP_BRANCH (uint8, uint8_array);
2492 BTYP_BRANCH (uint16, uint16_array);
2493 BTYP_BRANCH (uint32, uint32_array);
2494 BTYP_BRANCH (uint64, uint64_array);
2495
2496 BTYP_BRANCH (cell, cell);
2497 BTYP_BRANCH (struct, map);
2498
2499#undef BTYP_BRANCH
2500
2501 case btyp_func_handle:
2502 err_wrong_type_arg ("mat2cell", a);
2503 break;
2504
2505 default:
2506 retval = do_mat2cell (a, d, nargin-1);
2507 break;
2508 }
2509
2510 return retval;
2511}
2512
2513/*
2514%!test
2515%! x = reshape (1:20, 5, 4);
2516%! c = mat2cell (x, [3,2], [3,1]);
2517%! assert (c, {[1,6,11;2,7,12;3,8,13],[16;17;18];[4,9,14;5,10,15],[19;20]});
2518
2519%!test
2520%! x = "abcdefghij";
2521%! c = mat2cell (x, 1, [0,4,2,0,4,0]);
2522%! empty1by0str = resize ("", 1, 0);
2523%! assert (c, {empty1by0str,"abcd","ef",empty1by0str,"ghij",empty1by0str});
2524
2525## Omitted input for trailing dimensions means not splitting on them.
2526%!test <*63682>
2527%! x = reshape (1:16, 4, 2, 2);
2528%! c1 = mat2cell (x, [2, 2], 2, 2);
2529%! c2 = mat2cell (x, [2, 2]);
2530%! assert (c1, c2);
2531%! assert (c1, {cat(3, [1,5;2,6], [9,13;10,14]); ...
2532%! cat(3, [3,7;4,8], [11,15;12,16])});
2533*/
2534
2535// FIXME: it would be nice to allow ranges being handled without a conversion.
2536template <typename NDA>
2537static Cell
2538do_cellslices_nda (const NDA& array,
2539 const Array<octave_idx_type>& lb,
2540 const Array<octave_idx_type>& ub,
2541 int dim = -1)
2542{
2543 octave_idx_type n = lb.numel ();
2544 Cell retval (1, n);
2545 if (array.isvector () && (dim == -1
2546 || (dim == 0 && array.columns () == 1)
2547 || (dim == 1 && array.rows () == 1)))
2548 {
2549 for (octave_idx_type i = 0; i < n; i++)
2550 retval.xelem (i) = array.index (idx_vector (lb(i) - 1, ub(i)));
2551 }
2552 else
2553 {
2554 const dim_vector dv = array.dims ();
2555 int ndims = dv.ndims ();
2556 if (dim < 0)
2557 dim = dv.first_non_singleton ();
2558 ndims = std::max (ndims, dim + 1);
2559
2560 Array<idx_vector> idx (dim_vector (ndims, 1), idx_vector::colon);
2561
2562 for (octave_idx_type i = 0; i < n; i++)
2563 {
2564 idx(dim) = idx_vector (lb(i) - 1, ub(i));
2565 retval.xelem (i) = array.index (idx);
2566 }
2567 }
2568
2569 return retval;
2570}
2571
2572DEFUN (cellslices, args, ,
2573 doc: /* -*- texinfo -*-
2574@deftypefn {} {@var{sl} =} cellslices (@var{x}, @var{lb}, @var{ub}, @var{dim})
2575Given an array @var{x}, this function produces a cell array of slices from
2576the array determined by the index vectors @var{lb}, @var{ub}, for lower and
2577upper bounds, respectively.
2578
2579In other words, it is equivalent to the following code:
2580
2581@example
2582@group
2583n = length (lb);
2584sl = cell (1, n);
2585for i = 1:length (lb)
2586 sl@{i@} = x(:,@dots{},lb(i):ub(i),@dots{},:);
2587endfor
2588@end group
2589@end example
2590
2591The position of the index is determined by @var{dim}. If not specified,
2592slicing is done along the first non-singleton dimension.
2593@seealso{cell2mat, cellindexmat, cellfun}
2594@end deftypefn */)
2595{
2596 int nargin = args.length ();
2597
2598 if (nargin < 3 || nargin > 4)
2599 print_usage ();
2600
2601 octave_value x = args(0);
2602 Array<octave_idx_type> lb = args(1).octave_idx_type_vector_value ();
2603 Array<octave_idx_type> ub = args(2).octave_idx_type_vector_value ();
2604 int dim = -1;
2605 if (nargin == 4)
2606 {
2607 dim = args(3).int_value () - 1;
2608 if (dim < 0)
2609 error ("cellslices: DIM must be a valid dimension");
2610 }
2611
2612 if (lb.numel () != ub.numel ())
2613 error ("cellslices: the lengths of LB and UB must match");
2614
2615 Cell retcell;
2616 if (! x.issparse () && x.is_matrix_type ())
2617 {
2618 // specialize for some dense arrays.
2619 if (x.islogical ())
2620 retcell = do_cellslices_nda (x.bool_array_value (),
2621 lb, ub, dim);
2622 else if (x.is_char_matrix ())
2623 retcell = do_cellslices_nda (x.char_array_value (),
2624 lb, ub, dim);
2625 else if (x.isinteger ())
2626 {
2627 if (x.is_int8_type ())
2628 retcell = do_cellslices_nda (x.int8_array_value (),
2629 lb, ub, dim);
2630 else if (x.is_int16_type ())
2631 retcell = do_cellslices_nda (x.int16_array_value (),
2632 lb, ub, dim);
2633 else if (x.is_int32_type ())
2634 retcell = do_cellslices_nda (x.int32_array_value (),
2635 lb, ub, dim);
2636 else if (x.is_int64_type ())
2637 retcell = do_cellslices_nda (x.int64_array_value (),
2638 lb, ub, dim);
2639 else if (x.is_uint8_type ())
2640 retcell = do_cellslices_nda (x.uint8_array_value (),
2641 lb, ub, dim);
2642 else if (x.is_uint16_type ())
2643 retcell = do_cellslices_nda (x.uint16_array_value (),
2644 lb, ub, dim);
2645 else if (x.is_uint32_type ())
2646 retcell = do_cellslices_nda (x.uint32_array_value (),
2647 lb, ub, dim);
2648 else if (x.is_uint64_type ())
2649 retcell = do_cellslices_nda (x.uint64_array_value (),
2650 lb, ub, dim);
2651 }
2652 else if (x.iscomplex ())
2653 {
2654 if (x.is_single_type ())
2655 retcell = do_cellslices_nda (x.float_complex_array_value (),
2656 lb, ub, dim);
2657 else
2658 retcell = do_cellslices_nda (x.complex_array_value (),
2659 lb, ub, dim);
2660 }
2661 else
2662 {
2663 if (x.is_single_type ())
2664 retcell = do_cellslices_nda (x.float_array_value (),
2665 lb, ub, dim);
2666 else
2667 retcell = do_cellslices_nda (x.array_value (),
2668 lb, ub, dim);
2669 }
2670 }
2671 else
2672 {
2673 // generic code.
2674 octave_idx_type n = lb.numel ();
2675 retcell = Cell (1, n);
2676 const dim_vector dv = x.dims ();
2677 int ndims = dv.ndims ();
2678 if (dim < 0)
2679 dim = dv.first_non_singleton ();
2680 ndims = std::max (ndims, dim + 1);
2682 for (octave_idx_type i = 0; i < n; i++)
2683 {
2684 idx(dim) = range<double> (lb(i), ub(i));
2685 retcell.xelem (i) = x.index_op (idx);
2686 }
2687 }
2688
2689 return ovl (retcell);
2690}
2691
2692/*
2693%!test
2694%! m = [1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12];
2695%! c = cellslices (m, [1, 2], [2, 3], 2);
2696%! assert (c, {[1, 2; 5, 6; 9, 10], [2, 3; 6, 7; 10, 11]});
2697*/
2698
2699DEFUN (cellindexmat, args, ,
2700 doc: /* -*- texinfo -*-
2701@deftypefn {} {@var{y} =} cellindexmat (@var{x}, @var{varargin})
2702Perform indexing of matrices in a cell array.
2703
2704Given a cell array of matrices @var{x}, this function computes
2705
2706@example
2707@group
2708Y = cell (size (X));
2709for i = 1:numel (X)
2710 Y@{i@} = X@{i@}(varargin@{1@}, varargin@{2@}, @dots{}, varargin@{N@});
2711endfor
2712@end group
2713@end example
2714
2715The indexing arguments may be scalar (@code{2}), arrays (@code{[1, 3]}),
2716ranges (@code{1:3}), or the colon operator (@qcode{":"}). However, the
2717indexing keyword @code{end} is not available.
2718@seealso{cellslices, cellfun}
2719@end deftypefn */)
2720{
2721 if (args.length () == 0)
2722 print_usage ();
2723
2724 const Cell x = args(0).xcell_value ("cellindexmat: X must be a cell");
2725
2726 Cell y (x.dims ());
2727 octave_idx_type nel = x.numel ();
2728 octave_value_list idx = args.slice (1, args.length () - 1);
2729
2730 for (octave_idx_type i = 0; i < nel; i++)
2731 {
2732 octave_quit ();
2733
2734 octave_value tmp = x(i);
2735
2736 y.xelem (i) = tmp.index_op (idx);
2737 }
2738
2739 return octave_value (y);
2740}
2741
2742OCTAVE_END_NAMESPACE(octave)
Cell do_mat2cell(const ArrayND &a, const Array< octave_idx_type > *d, int nd)
Definition cellfun.cc:2287
#define BTYP_BRANCH(X, Y)
Cell do_mat2cell_nd(const ArrayND &a, const Array< octave_idx_type > *d, int nd)
Definition cellfun.cc:2213
N Dimensional Array with copy-on-write semantics.
Definition Array-base.h:130
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array-base.h:529
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition Array-base.h:547
void clear()
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
bool isempty() const
Size of the specified dimension.
Definition Array-base.h:674
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
Definition Cell.h:41
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:92
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition dim-vector.h:341
void resize(int n, int fill_value=0)
Definition dim-vector.h:278
static dim_vector alloc(int n)
Definition dim-vector.h:208
int increment_index(octave_idx_type *idx, int start=0) const
Increment a multi-dimensional index tuple, optionally starting from an offset position and return the...
Definition dim-vector.h:510
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:263
bool any_zero() const
Definition dim-vector.h:322
int first_non_singleton(int def=0) const
Definition dim-vector.h:481
octave_value last_error_id(const octave_value_list &args, int nargout)
Definition error.cc:359
void save_exception(const execution_exception &ee)
Definition error.cc:932
octave_value last_error_message(const octave_value_list &args, int nargout)
Definition error.cc:337
static const idx_vector colon
Definition idx-vector.h:464
error_system & get_error_system()
octave_value_list feval(const char *name, const octave_value_list &args=octave_value_list(), int nargout=0)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
void recover_from_exception()
symbol_table & get_symbol_table()
void assign(const std::string &k, const octave_value &val)
Definition oct-map.h:230
void resize(octave_idx_type n, const octave_value &rfv=octave_value())
Definition ovl.h:115
bool empty() const
Definition ovl.h:113
octave_value & xelem(octave_idx_type i)
Definition ovl.h:169
Cell cell_value() const
Definition ovl.h:103
octave_value_list slice(octave_idx_type offset, octave_idx_type len, bool tags=false) const
Definition ovl.h:129
octave_idx_type length() const
Definition ovl.h:111
octave_value_list & prepend(const octave_value &val)
Definition ovl.cc:80
bool is_function_handle() const
Definition ov.h:766
bool is_undefined() const
Definition ov.h:593
bool is_uint32_type() const
Definition ov.h:722
bool isinteger() const
Definition ov.h:728
boolNDArray bool_array_value(bool warn=false) const
Definition ov.h:898
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition ov.h:907
std::string class_name() const
Definition ov.h:1360
bool is_inline_function() const
Definition ov.h:772
octave_function * function_value(bool silent=false) const
int32NDArray int32_array_value() const
Definition ov.h:963
uint16NDArray uint16_array_value() const
Definition ov.h:972
octave_value index_op(const octave_value_list &idx, bool resize_ok=false)
Definition ov.h:502
int16NDArray int16_array_value() const
Definition ov.h:960
Cell cell_value() const
int8NDArray int8_array_value() const
Definition ov.h:957
int ndims() const
Definition ov.h:549
bool isreal() const
Definition ov.h:736
bool is_string() const
Definition ov.h:635
bool isnumeric() const
Definition ov.h:748
bool is_int8_type() const
Definition ov.h:704
Matrix size()
Definition ov.h:460
ComplexNDArray complex_array_value(bool frc_str_conv=false) const
Definition ov.h:882
bool is_single_type() const
Definition ov.h:696
bool is_defined() const
Definition ov.h:590
charNDArray char_array_value(bool frc_str_conv=false) const
Definition ov.h:904
bool isempty() const
Definition ov.h:599
bool is_function() const
Definition ov.h:775
@ op_asn_eq
Definition ov.h:135
bool is_uint8_type() const
Definition ov.h:716
bool is_uint64_type() const
Definition ov.h:725
bool is_uint16_type() const
Definition ov.h:719
uint64NDArray uint64_array_value() const
Definition ov.h:978
bool is_int16_type() const
Definition ov.h:707
bool issparse() const
Definition ov.h:751
bool is_char_matrix() const
Definition ov.h:626
bool iscell() const
Definition ov.h:602
octave_idx_type numel() const
Definition ov.h:557
octave_map map_value() const
@ magic_colon_t
Definition ov.h:170
bool is_int64_type() const
Definition ov.h:713
bool iscomplex() const
Definition ov.h:739
octave_value single_subsref(const std::string &type, const octave_value_list &idx)
int64NDArray int64_array_value() const
Definition ov.h:966
NDArray array_value(bool frc_str_conv=false) const
Definition ov.h:863
octave_idx_type length() const
bool is_int32_type() const
Definition ov.h:710
uint8NDArray uint8_array_value() const
Definition ov.h:969
FloatComplexNDArray float_complex_array_value(bool frc_str_conv=false) const
Definition ov.h:886
octave_value resize(const dim_vector &dv, bool fill=false) const
Definition ov.h:578
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition ov.h:866
bool isstruct() const
Definition ov.h:647
uint32NDArray uint32_array_value() const
Definition ov.h:975
builtin_type_t builtin_type() const
Definition ov.h:688
bool isobject() const
Definition ov.h:662
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition ov.h:911
bool islogical() const
Definition ov.h:733
dim_vector dims() const
Definition ov.h:539
octave_value find_function(const std::string &name, const symbol_scope &search_scope=symbol_scope::invalid())
Definition symtab.cc:254
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage()
Definition defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition defun.h:111
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition defun.h:56
void error_with_id(const char *id, const char *fmt,...)
Definition error.cc:1053
void error(const char *fmt,...)
Definition error.cc:1008
void err_wrong_type_arg(const char *name, const char *s)
Definition errwarn.cc:166
octave_value get_function_handle(interpreter &interp, const octave_value &arg, const std::string &parameter_name)
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44
#define OCTAVE_LOCAL_BUFFER_INIT(T, buf, size, value)
Definition oct-locbuf.h:50
const octave_base_value const Array< octave_idx_type > & ra_idx
@ btyp_func_handle
Definition ov-base.h:100
@ btyp_double
Definition ov-base.h:84
@ btyp_complex
Definition ov-base.h:86
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
#define panic_unless(cond)
Definition panic.h:59
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
bool valid_identifier(const char *s)
Definition utils.cc:79