Является ли перенос функции оператором {} допустимой заменой "UniformOutput", false в cellfun?
Я использую cellfun
для применения функции к каждой ячейке в массиве ячеек.
Я знаю, что я должен установить 'UniformOutput'
в false
всякий раз, когда функция возвращает нескалярные значения, так что выходы функции возвращаются инкапсулированными в массив ячеек.
В качестве примера возьмем следующий массив ячеек:
C1 = {[1 2 3], [4 5 6]};
C1
имеет две ячейки, и каждая ячейка содержит вектор из трех элементов:
C1 =
1×2 cell array
[1×3 double] [1×3 double]
Если я хочу добавить 1
к содержимому в каждой ячейке, я могу определить функцию @(x) x + 1
и применить ее с помощью cellfun
следующим образом:
C2 = cellfun(@(x) x + 1, C1, 'UniformOutput', false);
Это работает очень хорошо, но обратите внимание, что мне нужно убедиться, что 'UniformOutput'
установлен в false
, как я объяснял ранее, в противном случае выдается ошибка.
Однако, прочитав этот поток, я понял, что если я завершу функцию с помощью оператор построения ячейки ячейки {}
, как этот @(x) {x + 1}
, тогда мне не нужно устанавливать 'UniformOutput'
в false
.
Таким образом, следующая команда будет генерировать те же результаты, что и в C2
, без каких-либо ошибок:
C3 = cellfun(@(x) {x + 1}, C1);
В терминах макета кода я предпочитаю этот подход, поскольку он более компактный и менее подробный, чем первый, но я не уверен, что это всегда безопасно.
Таким образом, мой вопрос:
Можно ли всегда включать функцию {}
, чтобы избежать установки 'UniformOutput'
на false
? Или существуют ли сценарии, в которых такая замена не будет работать?
Мои исследования:
help cellfun
'UniformOutput'
- логическое значение, указывающее, вывод FUN
может быть возвращен без инкапсуляции в ячейку массив. Если true
(по умолчанию), FUN
должен возвращать скалярные значения, которые могут быть объединены в массив. Если true
, выходы должны быть следующие типы: числовые, логические, char, struct, cell. Если false
, cellfun
возвращает массив ячеек (или несколько массивов ячеек), где (I, J,...) -я ячейка содержит значение FUN (C {I, J,...},...). Если 'UniformOutput'
false
, выходы могут быть любого типа.
Следующий фрагмент является частью ответа на соответствующий вопрос:
[...] cellfun
выполняет операцию разыменования, которая требуется выполнять подробные операции над отдельными элементами ячейки при циклировании (т.е. {}
) [...]
Ответы
Ответ 1
Есть два сценария, которые я могу представить, используя инкапсуляцию ячеек вместо дополнительных аргументов ...'UniformOutput', false)
вызовет проблему, первая из которых будет гораздо более вероятным сценарием, чем вторая:
-
Захват нескольких выходов: Иногда вам может потребоваться захват нескольких выходов из функции, которую вы применяете, например вызов unique
для каждого элемента вашего массива ячеек и получения дополнительных аргументов индекса. Используя ...'UniformOutput', false)
, мы можем легко сделать это:
>> C1 = {[1 2 3], [4 5 6]};
>> [C2, index] = cellfun(@(x) unique(x), C1, 'UniformOutput', false)
C2 =
1×2 cell array
[1×3 double] [1×3 double]
index =
1×2 cell array
[3×1 double] [3×1 double]
Однако это не удается при использовании инкапсуляции ячеек:
>> [C2, index] = cellfun(@(x) {unique(x)}, C1)
Output argument "varargout{2}" (and maybe others) not assigned during call to
"@(x){unique(x)}".
-
Функции без вывода: Я знаю, что вы думаете: "Но если они не производят выход, то мне не нужно беспокоиться о сборе нескалярных значений!" Правда, но я представляю себе необычный сценарий, в котором функция, подлежащая оценке, может быть дополнительным параметром, таким как дескриптор функции, переданный в качестве аргумента, поэтому вы не знаете априорно, сколько вы будете иметь с вами. Несмотря на это, два подхода различаются для такого случая:
>> C1 = {[1 2 3], [4 5 6]};
>> cellfun(@(x) disp(x), C1, 'UniformOutput', false);
1 2 3
4 5 6
>> cellfun(@(x) {disp(x)}, C1);
Error using disp
Too many output arguments.
Error in @(x){disp(x)}
Наверное, не то, о чем вам придется беспокоиться, но я подумал, что включу его ради полноты.
Ответ 2
От вас вопрос...
Могу ли я всегда обертывать функцию с помощью {}, чтобы исключить установку "UniformOutput" на false? Или существуют ли сценарии, в которых такая замена не будет работать?
Короткий ответ "НЕТ" . И я верю, что основная причина, по которой ваша анонимная функция потерпит неудачу, связана с отсутствием проверки типа, и у программиста мало знаний о возвращаемом типе функции.
На самом деле, IMO вы не должны пытаться либо использовать оператор индексирования клеток {}
, чтобы обернуть ваше возвращаемое значение, либо нет в вашей анонимной функции. Лучшей практикой будет определение типа возвращаемого значения после вызова cellfun
или проверка типа входного значения перед вызовом cellfun
. Конечно, вы можете реализовать процедуру проверки типов или try...catch
в дескрипторе функции, чтобы обеспечить полный контроль над вашим типом возврата. И, честно говоря, знание того, что вы отправляете и что вы собираетесь получать, чрезвычайно важно в Matlab.
Вкратце, вы не должны использовать {}
для инкапсуляции вашей анонимной функции, если вы не намеренно создаете массив ячеек как тип возвращаемого значения, и вы знали, что возвращаемое значение из вызываемой функции может быть используется для создания массива ячеек.
FYI, если у вас есть 2016b или более поздняя версия, в то время как функция в script возможно, вы можете создать дескриптор функции для cellfun
внутри вашего script без создания нового файла.