Является ли перенос функции оператором {} допустимой заменой "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 без создания нового файла.