простой вопрос о передаче данных между функциями - PullRequest
9 голосов
/ 11 сентября 2011

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

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

a = Table[0,{10}]
a = update[a]

и так как я не могу использовать передачу по ссылке (в CDF нельзя изменить Атрибуты функции к чему-либо, например, HoldFirst), то я вынужден сделать копию списка внутри самой функции, чтобы обновить его, и верните обратно копию.

Мой вопрос, кроме использования «глобальных переменных», который не годится, Есть ли более эффективный способ сделать это?

пс. около года назад я спрашивал про копию по ссылке, вот ссылка на мой вопрос Mathgroup. (Спасибо Леониду за ответ, кстати, был полезный ответ).

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

(нельзя использовать SetAttributes и его друзей, не разрешено в CDF).

Сначала я покажу базовый пример, затем покажу, как бы я это сделал, если Я мог бы использовать HoldFirst.

Пример

update[a_List] := Module[{copyOfa = a}, copyOfa[[1]] = 5; copyOfa]
a = Table[0, {10}];
a = update[a]

----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

Если бы я мог использовать HoldFirst, я бы написал

update[a_] := Module[{}, a[[1]] = 5; a]
Attributes[update] = {HoldFirst};

a = Table[0, {10}];
a = update[a]

----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

Гораздо эффективнее, поскольку копирование не производится. Передайте по ссылке.

Я мог бы использовать глобальную переменную, как в

a = Table[0, {10}];
updateMya[] := Module[{}, a[[1]] = 5]
updateMya[];
a
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

Но это, конечно, плохое программирование, даже если оно очень быстрое.

Поскольку у меня большие буферы данных, и я хотел бы модульно кодировать мой код Mathematica, Мне нужно создать функции, которые я передаю большие данные для обработки, но в то же время время хотело, чтобы оно было «эффективным».

Какие-нибудь другие варианты, которые можно увидеть, чтобы сделать это?

извините, если раньше об этом спрашивали, трудно искать ТАК.

спасибо,

сложение 1

Использование Unevaluated легко в использовании, но я больше не могу использовать проверку типов, мне нужно было убедиться, что список передается. Например

update[a_List] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

Теперь вызов не «привязывается» к определению, поскольку «a» теперь не имеет заголовка List.

Итак, я теряю некоторую надежность, которую я имел в коде. Но использование Unevaluated работает в CDF, и изменить код для его использования было легко. Мне просто нужно было убрать те дополнительные «проверки типов», которые у меня были, чтобы заставить его работать.

Ответы [ 2 ]

16 голосов
/ 11 сентября 2011

Функция Unevaluated имеет почти такой же эффект, как (временно) установка атрибута HoldFirst, чтобы вы могли сделать что-то вроде

update[a_] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

Редактировать

Относительно добавления 1: вы можете добавить проверку типов, выполнив что-то вроде

Clear[update];
update[a_] := Module[{}, a[[1]] = 5; a] /; Head[a] == List

Тогда

a = Table[0, {10}];
update[Unevaluated[a]]

работает как прежде, но

b = f[1,2,3];
update[Unevaluated[b]]

просто возвращает последнее утверждение в неоцененной форме.

12 голосов
/ 11 сентября 2011

В качестве альтернативы, и если CDF позволяет это, вы можете использовать чистую функцию с атрибутом Hold *, например, так:

update = Function[a, a[[1]] = 5; a, HoldFirst]

Затем вы используете его как обычно:

In[1408]:= 
a=Table[0,{10}];
update[a];
a

Out[1410]= {5,0,0,0,0,0,0,0,0,0}

EDIT

Просто для полноты, вот еще один способ, который менее изящен, но который я использовал время от времени, особенно когда у вас есть несколько параметров и вы хотите хранить более одного (но такого, что HoldFirst или HoldRest недостаточно хороши (например, первый и третий): просто оберните ваш параметр в Hold и запишите его в сигнатуру функции, например:

updateHeld[Hold[sym_], value_] := (sym[[1]] = value; sym)

Вы используете это как:

In[1420]:= a=Table[0,{10}];
updateHeld[Hold[a],10];
a

Out[1422]= {10,0,0,0,0,0,0,0,0,0}

РЕДАКТИРОВАТЬ 2

Если ваша основная задача - инкапсуляция, вы также можете использовать Module для создания постоянной локальной переменной и методов для доступа и изменения к ней, например:

Module[{a},
   updateA[partIndices__, value_] := a[[partIndices]] = value;
   setA[value_] := a = value;
   getA[] := a
]

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

In[1444]:= 
setA[Table[0,{10}]];
updateA[1,5];
getA[]

Out[1446]= {5,0,0,0,0,0,0,0,0,0}

Это похоже на создание упрощенного JavaBean в Java - контейнера для изменяемых данных (способ инкапсуляции состояния). У вас будут небольшие накладные расходы из-за дополнительных вызовов методов (относительно методов Hold-attribute или Unevaluated - based), и во многих случаях вам это не нужно, но в некоторых случаях вы можете захотеть инкапсулировать подобное состояние - это может упростить тестирование вашего кода с сохранением состояния Лично я делал это несколько раз для UI-программирования и в коде, связанном с взаимодействием с базой данных.

В том же духе вы также можете делить некоторые переменные между функциями, определяя эти функции внутри области действия Module - в этом случае вам могут не понадобиться методы получения и установки, и такие глобальные функции с общим состоянием равны closures. Вы можете найти более подробное обсуждение этого в моем третьем посте в этой теме MathGroup.

...