Какова семантика "конца" в 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
?
Ответы
Ответ 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
.
Я еще не был укушен этой интерпретацией.