Подтвердить что ты не робот

Какова семантика "конца" в Matlab?

Общепринято использовать ключевое слово end как ярлык для доступа или расширения массива в Matlab, как в

>> x = [1,2,3];
>> x(1:end-1)
ans =
    1   2
>> x(end+1) = 4
x =
    1   2   3   4

Однако я с удивлением обнаружил, что также работает

>> x(1:min(5, end))
ans =
    1   2   3   4

Я думал, что end может быть специальной формой, например :, которая может быть специально обрезана в операциях индексирования, поэтому я создал класс для обнаружения этого

classdef IndexDisplayer
  methods
    function subsref(self, s)
      disp(s);
    end
  end
end

Вы можете видеть, как : используется в следующем примере

>> a = IndexDisplayer;
>> a(1:3)
    type: '()'
    subs: {[1 2 3]}
>> a(:)
    type: '()'
    subs: {':'}

Однако, когда я индексирую с end, я просто вижу

>> a(end)
    type: '()'
    subs: {[1]}

Здесь end заменяется на 1. Откуда этот 1? Мое первое предположение заключалось в том, что любой end внутри выражения индексирования x(end) будет заменен вызовом на length(x), поэтому я попытался переопределить length, а также

classdef IndexDisplayer
  methods
    function subsref(self, s)
      disp(s);
    end
    function len = length(self)
      len = 10;
    end
  end
end

Однако это дает

>> a = IndexDisplayer;
>> length(a)
ans =
    10
>> a(end)
    type: '()'
    subs: {[1]}

так что теория выходит из окна. Может ли кто-нибудь объяснить семантику end?

4b9b3361

Ответ 1

Во-первых, я считаю это своего рода ошибкой или, по крайней мере, неожиданной особенностью, что ваш синтаксис x(1:min(5, end)) работает вообще. Когда я был в MathWorks, я помню, как кто-то указывал на это, и немало из разработчиков пришлось потратить некоторое время, выясняя, что происходит. Я не уверен, действительно ли они действительно договорились о том, была ли это проблемой или нет.

Чтобы объяснить (предполагаемую) семантику end: end реализована как функция ind = end(obj, k, n). k - это индекс выражения, содержащего end, а n - общее число индексов в выражении.

Так, например, когда вы вызываете a(1,end,1), k равно 2, поскольку end находится в аргументе 2, а n равно 3, поскольку есть 3 аргумента.

ind возвращается как индекс, который может заменить end в выражении.

Вы можете перегрузить end для своих собственных классов (так же, как вы можете перегрузить colon, size, subsref и т.д.).

Чтобы расширить свой пример:

classdef IndexDisplayer
  methods
    function ind = end(self,k,n)
        disp(k)
        disp(n)
        ind = builtin('end', self, k, n);
    end
  end
end

>> a = IndexDisplayer;
>> a(1,end,1)
 2
 3

Подробнее см. здесь.

Ответ 2

Я тоже нахожу это любопытством. Тем не менее, я часто использую (эксплуатировать?) Это поведение для сокращения операторов. Например, в этом ответе, чтобы получить все, кроме k -го элемента (ов) вектора, чистое решение, которое произошло со мной, было

vector(setdiff(1:end,k))

Этот end заменяет вызов на numel(vector). Для скаляра k это альтернатива vector(1:end ~= k) или vector([1:k-1 k+1:end]). В то время это казалось вполне разумным, хотя я обратил внимание на странность этого использования. Это действительно плохая практика? Возможно, но я принял его за то, что стоит и двигаться дальше.

Я не предлагаю никакого понимания того, как это работает или что такое правила, как делает Сэм Робертс в своем ответе, но концептуально я рассматриваю это как вопрос контекста. То есть, когда end происходит, я бы предположил, что он оценивает индекс (или индекс измерения) для массива с самой непосредственной областью, просматривая "вверх" через вложенные операторы для определения. Не уверен, что это правильная формулировка, но это, по-видимому, полезный способ интерпретировать операцию end.

Я еще не был укушен этой интерпретацией.