Вызовы по ссылке и вызовы по значению

Блокноты MuPAD® будут демонтированы в будущем релизе. Используйте live скрипты MATLAB® вместо этого.

Live скрипты MATLAB поддерживают большую часть функциональности MuPAD, хотя существуют некоторые различия. Для получения дополнительной информации смотрите, Преобразовывают Notebook MuPAD в Live скрипты MATLAB.

Вызовы по значению

При вызове процедуры с некоторыми аргументами вы ожидаете, что процедура присвоит эти значения для своих локальных переменных и выполнит некоторые вычисления с теми переменными. Например, эта процедура делит любой номер, который вы передаете ей 10:

f := x -> (x := x/10):

В этом примере x является локальной переменной f. Когда вы вызываете f с любым значением, процедура присваивает то значение локальной переменной x, использует его, чтобы вычислить результат, и затем уничтожает локальную переменную x, освобождая выделенную память:

x := 10:
f(x), x

Несмотря на то, что значение локальной переменной, изменения x в 1 в процедуре и затем уничтожаются, значение глобальной переменной x, остается то же самое. Поэтому можно прийти к заключению, что процедура не получает доступ к фактическому блоку памяти, который содержит значение x.

Когда вы вызываете эту процедуру, MuPAD® выделяет новый блок памяти и копирует значение x к тому блоку. В то время как процедура выполняется, система сопоставляет локальную переменную x с этим новым блоком памяти. В конце вызова процедуры это освобождает этот блок памяти. Блок памяти, который содержит значение глобальной переменной x, не изменяется.

Стратегия копирования значений аргументов процедуры к новым блокам памяти и обращения к этим блокам во время вызовов процедуры известна как вызов значением. Большинство функций MuPAD использует эту стратегию.

Начиная с вызова значением создает дополнительные копии данных, это может быть неэффективно с точки зрения управления памятью. Создание дополнительных копий больших наборов данных, особенно когда ваши вызовы процедуры являются рекурсивными или вкладываются, может значительно уменьшать свободную память на вашем компьютере. На многих языках программирования, вызывающих значением всегда, означает копировать данные, даже когда процедура не изменяет те данные.

MuPAD использует ленивое копирование, чтобы не создавать ненужные копии данных. Когда вы вызываете процедуру, MuPAD не выделяет новые блоки памяти сразу же. Вместо этого это соединяет локальные переменные с блоками памяти, где аргументы процедуры хранятся. Система всегда рассчитывает, сколько объектов соединяется с тем же блоком памяти. Когда процедура изменяет значение локальной переменной, проверки MuPAD, если существуют другие объекты, соединенные с тем же блоком памяти, и создает копию только если необходимый.

Например, когда вы вызываете f(x), MuPAD указывает и глобальной переменной x (DOM_IDENT) и локальной переменной (DOM_VAR) x к тому же блоку памяти. Только, когда локальная переменная, x изменяет свое значение, MuPAD, выделяет новый блок памяти для него.

Вызовы по ссылке

Как правило, когда вы вызываете процедуру MuPAD с некоторыми аргументами, система использует подход вызова значением и создает копии значений этих аргументов. Этот подход препятствует тому, чтобы процедуры изменили объекты, переданные процедуре в качестве аргументов. Для некоторых функций, таких как присвоение, MuPAD использует подход вызова ссылкой. В вызове по ссылке система не копирует значения аргументов к новым блокам памяти. Вместо этого это копирует ссылки (указатели) на эти аргументы.

Некоторые языки программирования позволяют вам задать который подход использовать для каждой конкретной функции. MuPAD не предлагает определенные построения языка для вызова ссылкой. Тем не менее, вы можете все еще вызов по ссылке в MuPAD.

Примечание

Для опытных пользователей MuPAD объекты со ссылочной семантикой могут неожиданно вести себя. Будьте осторожны при представлении ссылочной семантики пользователям, потому что она может сбить с толку.

Предположим, что ваша задача требует вызова функции смочь изменить значения его аргументов. Простая стратегия состоит в том, чтобы возвратить измененную копию аргументов и перезаписать исходный объект при помощи присвоения. Например, матрица замены A с его верхней формой эшелона строки:

A := linalg::hilbert(3)

A := linalg::gaussElim(A)

При работе с большими наборами данных и глубоко вложенными вызовами, эта стратегия может вызвать проблемы памяти. Проверяйте профильный отчет видеть, имеет ли ваша реализация такие проблемы. Для получения дополнительной информации смотрите Профилирование Вашего Кода.

Также можно достигнуть эффекта вызова ссылкой в использовании MuPAD:

Лексическое определение объема

Вместо передающих данных в качестве аргументов процедуры можно использовать локальную переменную внешней процедуры, чтобы хранить данные. Для внутренней процедуры эта переменная не локальна. Поэтому внутренняя процедура может изменить значение той переменной, и переменная не уничтожается после того, как внутренняя процедура выполняется:

f :=
proc(x)
  local n, incr;
begin
  n := 0;
  incr := () -> (n := n + 1);
  while x > n do
    incr();
  end_while;
end_proc:

Этот подход не соответствует многим задачам программирования, но он рекомендуется везде, где можно применить его. Это является самым простым и большая часть читаемого подхода, чтобы получить эффект вызова ссылкой в MuPAD.

Закрытия в объектах

При работе с областями также можно использовать подход наличия фактических данных в закрытии. Например, вместо того, чтобы хранить фактические данные в объектах, можно сохранить функции, которые получают доступ к данным:

domain d
  local get, set;
  inherits Dom::BaseDomain;
  
  new := proc(x)
    option escape;
  begin
    new(dom, () -> x, y -> (x := y));
  end;

  incr := x -> set(x, get(x) + 1);
  print := x -> get(x);

begin
  get := x -> extop(x, 1)();
  set := (x, y) -> extop(x, 2)(y);
end_domain:

e := d(4)

d::incr(e)

e

Дополнительную информацию см. в Закрытиях.

Области в объектах

Можно реализовать подход вызова ссылкой в коде при помощи областей как таблицы со ссылочными эффектами. (Используя области как таблицы не связано с объектно-ориентированным программированием.) Следующий пример демонстрирует эту стратегию:

domain dd
  local get, set;
  inherits Dom::BaseDomain;

  new := proc(x)
    local d;
  begin
    d := newDomain(genident());
    d::number := x;
    new(dom, d);
  end;

  get := (x, entry) -> slot(extop(x, 1), entry);
  set := (x, entry, value) -> slot(extop(x, 1), entry, value);

  incr := x -> (dom::set(x, "number",
      dom::get(x, "number") + 1); x);
  print := x -> dom::get(x, "number");

end_domain:

e := dd(4)

dd::incr(e)

e

Примитивы библиотеки plot используют эту стратегию. Например, когда вы выполняете следующий код, доменный plot::Function2d перегружает функцию slot:

f := plot::Function2d(sin(x), x=-PI..PI):
f::Color := RGB::Green:

Контекстное переключение

Функция context и option hold также позволяют вам реализовать вызов быть ссылочным эффектом в ваших процедурах. option hold препятствует тому, чтобы процедура оценила свои аргументы прежде, чем выполнить код в теле процедуры. Функция context позволяет вам выполнить операции, как будто они происходят в процедуре вызова.

Например, в этом коде option hold препятствует тому, чтобы процедура incr оценила свой аргумент. Поэтому система не копирует значение x к новому блоку памяти:

incr :=
proc(x)
  option hold;
begin
  context(hold(_assign)(x, x + 1));
end_proc:
operator("++", incr, Prefix, 500):

При выполнении этой процедуры система выполняет операцию присвоения в контексте процедуры f (процедура вызова для incr). Таким образом incr изменяет значение аргумента, n, которым это было вызвано:

f :=
proc(x)
  local n;
begin
  n := 0;
  while x > n do
    ++n;
  end_while;
end_proc:

Если вы используете оператор ++ на неизменном объекте, MuPAD выдает ошибку. Например, вы не можете присвоить значение 2 значению 1:

++1
Error: Invalid left side. [_assign]

Это сообщение об ошибке не упоминает incr, потому что ошибка происходит в присвоении, которое выполняется в различном контексте оценки. Процедура incr ведет себя по существу как динамический макрос.