Какой-нибудь простой механизм в Mathematica, чтобы сделать модуль с постоянным состоянием? - PullRequest
3 голосов
/ 24 декабря 2011

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

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

http://en.wikipedia.org/wiki/Local_variable#Static_local_variables

Возможно ли эмулировать это в Mathematica, используя Module[]?или DynamicModule[]?

(мы знаем, Module[] не сохраняет состояние локальных переменных по умолчанию, но как насчет использования DynamicModule[]?)

Нет использования пакетов.Контекст использования этого находится внутри Manipulate[].

Нет Module[] может быть вне Manipulate.Следовательно, все должно быть примерно так:

Manipulate[

    foo[]:=Module[{a,b,c},....];
    boo[]:=Module[{a,b,c},....];

      ... foo[] ..
      ... boo[]...
    ..,

    control_variables...,

    Initialization:>
    (
     .... global area....
    )
]

Я пытался использовать DynamicModule вместо Module[] выше, но DynamicModules не может быть вызвано как функция?и я не мог понять, как это сделать.

вопрос в том, можно ли заставить функцию запоминать значение ее локальных переменных между вызовами, как, например, можно использовать C с локальными статическими переменными?

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

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

Обновление

Для ясности, вот очень простой пример C

#include <stdio.h>     

void my_add(void)  
{
 static int total = 0;

 total = total + 1;

 printf("current total %d\n",total);     
}

int main()
{
  int i;
  for(i = 1; i<=3; i++)
       my_add();

    return 0;
}

$ gcc ex.c
$ ./a.exe
current total 1
current total 2
current total 3
$

Обновление 8:45

Это для решения WReach.Вот что я попробовал:

 Manipulate[
 n;
 Module[{total = 0},
  processA[] :=
   (
    total = total + 1
    )
  ];
 Module[{total = 0},
  processB[] :=
   (
    total = total + 1
    )
  ];

 Grid[{
   {"module A total=", processA[]},
   {"module B total=", processB[]}
   }],

 Button["press to update state", n++],
 {{n, 0}, None},
 TrackedSymbols :> {n}
 ]

enter image description here

Общая сумма остается 1 каждый раз, когда я нажимаю кнопку.Не сохраняется последнее значение.

Обновление 9:13

Для решения WReach внутри Manipulate:

Вот тест:

Manipulate[

 n;

 Grid[{
   {"module A result=", aResult}
   }],

 Button["press to update process A", {n++; aResult = processA[n]}],
 {{n, 0}, None},
 {{aResult, {}}, None},
 TrackedSymbols :> {n},
 Initialization :>
  (
   Module[{total = 0},
    processA[n_] :=
     (
      total = total + 1;
      {n, total}
      )
    ]
   )
 ]

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

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

обновление 9:40 *

Ответ на второй пример мистера Волшебника, приведенный ниже, для примера, который он показал:

Manipulate[
  {x, myAdd[]},
  {x, 1, 10},
  {{total, 0}, None},
  {{myAdd, (total += 1; total) &}, None}
]

Проблема в том, что имя total не может использоваться двумя различными функциями. Пространство имен для total находится во всем Manipulate. Что я хотел бы, используя этот шаблон, примерно так:

Manipulate[
 ctrl;
 Grid[{
   {"processA result=", processA[]},
   {"processB result=", processB[]}
   }],

 Button["step", ctrl++],

 {{ctrl, 0}, None},

 {{processA, Module[{total = 0}, total++] &}, None},
 {{processB, Module[{total = 0}, total++] &}, None},

 TrackedSymbols :> {ctrl}
 ]

Вы видите, что выше, у processA есть свой локальный итог, а также processB. То же самое локальное имя. Вышеприведенное не работает. Если я заменим вышеприведенное на следующее, чтобы сделать этоwork '

Manipulate[
 ctrl;
 Grid[{
   {"processA result=", processA[]},
   {"processB result=", processB[]}
   }],

 Button["step", ctrl++],

 {{ctrl, 0}, None},
 {{total, 0}, None},

 {{processA, Module[{}, total++] &}, None},
 {{processB, Module[{}, total++] &}, None},

 TrackedSymbols :> {ctrl}
 ]

Тогда processA и processB теперь совместно используют одну и ту же переменную итогового значения. Это сводит на нет весь смысл наличия каждого в отдельном пространстве имен, но при этом сохраняетсяВ течение срока службы вызовов.

Обновление 10:00

Оппс, у меня был плохой способ написания этого модуля [] в приведенном выше примере.Вот почему это не работает.Пожалуйста, игнорируйте мое обновление 9:40 утра.Я исправляю это сейчас, и обновлю через несколько минут.Это может на самом деле работать.

обновление 10:08

Хорошо, вот совок: в 9:40 утра, когда я сказал «вышеописанное не работает», это потому, что у меня была неправильная установка, неправильное место (). Я исправил это. Сейчас я показываю решение, на которое ссылается WReach, под заголовком Избегание глобальных определений

Новость заключается в том, что он работает для ОДНОЙ манипуляции. Когда я копирую манипуляцию в другую ячейку, счетчики распределяются между двумя манипуляциями.

Вот пример: (исправленная версия того, что было у меня в 9:40 утра)

Manipulate[
 ctrl;
 Print["in top of Manipulate"];
 Grid[{
   {"processA current total=", aResult},
   {"processA current total=", bResult}
   }],

 Button["update A process", {ctrl++; aResult = processA[]}],
 Button["update B process", {ctrl++; bResult = processB[]}],

 {{ctrl, 0}, None},
 {{aResult, 0}, None},
 {{bResult, 0}, None},

 {{processA, Module[{total = 0}, ( total = total + 1; total) &]}, 
  None},
 {{processB, Module[{total = 0}, (total = total + 1; total) &]}, None},

 TrackedSymbols :> {ctrl}
 ]

enter image description here

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

enter image description here

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

обновление 12:00

Чтобы проиллюстрировать, что я делаю сейчас, в ответ на приведенный ниже MrWizard, для каждого имеющегося у меня «решателя» я предварительно фиксирую имя каждого из его параметров с помощью pn, где n - это идентификационный номер решателя. Итак, у меня есть p1StepNumber, p2StepNumber, p3StepNumber, p1Solution, p2Solution, p3Solution и т. Д. *

Затем, когда я хочу вызвать solver 1, я передаю ему параметры p1 * и при возврате возвращает обратно решение и любые обновления, которые будут сохранены в области Manipulate Control-> None для последующего вызова, и так далее.

Следовательно, состояние каждого решателя сохраняется / сохраняется внутри Манипуляции как переменная Control-> None. Поскольку Manipulate - это DynamicModule, они сохраняются между вызовами, и даже когда я закрываю M и снова открываю его.

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

Благодаря использованию метода Leonid Macro, я теперь могу легко это сделать:).

Было бы лучше, если бы я мог сохранять эти «внутри» каждого решателя, и чтобы каждый решатель был отдельным модулем, где каждый решатель будет сохранять свое собственное состояние, а Manipulate будет просто передавать ему параметр пользовательского интерфейса каждый раз, когда это необходимо. обновите, а все остальное будет сохранено внутри решателя (ей), к которому они принадлежат.

Отсутствие даже структуры для управления этим означает, что каждый мой вызов содержит более 20-30 параметров. Так как мне нужно каждый раз передавать решателю все его состояние и возвращать его вместе с возвратами. Это не так уж и плохо, я просто должен привыкнуть делать вызовы с 30 параметрами каждый раз, что я не использую. Но я пока не вижу другого чистого пути. Вот почему я задал этот вопрос.

enter image description here

обновление 24.12.11 18:00 Это ниже ответ на предложение Szabolcs и telefunkenvf14 использовать имя контекста функции.

Я пришел к выводу, что этот метод тоже не работает. Та же проблема, что и у других методов.

Как только я делаю копию самого Манипулятора, переменные становятся общими для двух копий Манипулятора.

Ниже приведен пример, показывающий проблему:

Remove["Global`*"]
Manipulate[
 ctrl;
 Print["in top of Manipulate"];
 Grid[{
   {"processA current total=", aResult},
   {"processA current total=", bResult}
   }],

 Button["update A process", {ctrl++; aResult = processA[]}],
 Button["update B process", {ctrl++; bResult = processB[]}],

 {{ctrl, 0}, None},
 {{aResult, 0}, None},
 {{bResult, 0}, None},

 {{processA, Module[{},
    (
      If[! ValueQ[processA`total], processA`total = 0];
       processA`total = processA`total + 1; 
      processA`total
      ) &]}, None},

 {{processB, Module[{},
    (
      If[! ValueQ[processB`total], processB`total = 0];
       processB`total = processB`total + 1; 
      processB`total
      ) &]}, None},

 TrackedSymbols :> {ctrl}
 ]

enter image description here

Теперь я скопировал вышеупомянутое в новую ячейку и нажал на кнопки, и я вижу, что счетчики теперь опережают значения в предыдущей копии Manipulate. Таким образом, процесс`A является общим. Это локально для каждой конкретной функции.

enter image description here

Точно такая же проблема с другим опробованным методом, поэтому этот метод у меня не работает.

Ответы [ 2 ]

3 голосов
/ 24 декабря 2011

Попробуйте это:

Module[{total = 0}
, myAdd[] := (total += 1; Print["current total ", total])
]

Использование:

In[2]:= myAdd[]

        current total 1

In[3]:= myAdd[]

        current total 2

In[4]:= myAdd[]

        current total 3

Для практических целей переменная модуля total видна только для myAdd. Module реализует это путем создания нового уникального символа, имя которого генерируется из предоставленного символа (total в данном случае). Вы можете увидеть это, оценив выражение, такое как:

In[5]:= Module[{total}, total]

Out[5]= total$562

На самом деле мы можем получить доступ к символу, используемому myAdd:

In[6]:= Names["total*"]

Out[6]= {"total", "total$557", "total$562"}

In[7]:= total$557

Out[7]= 3

Мы также можем увидеть эффект переименования, изучив определение myAdd:

In[8]:= ??myAdd

Global`myAdd
myAdd[]:=(total$557+=1; Print[current total, total$557])

Использование внутри манипулятора

Мы можем использовать эту технику внутри Manipulate таким образом:

Manipulate[
  {x, myAdd[]}
, {x, 1, 10}
, Initialization :>
    Module[{total = 0}
    , myAdd[] := (total += 1; total)
    ]
]

Manipulate screenshot

Как избежать глобальных определений

Если мы отчаянно пытаемся избежать глобального определения myAdd, мы можем сделать это:

Manipulate[
  {x, myAdd[]}
, {x, 1, 10}
, { myAdd
  , Module[{total = 0}, (total += 1; total) &]
  , None
  }
]

Таким образом, определение myAdd локализуется в Manipulate. Обратите внимание, однако, что символ myAdd по-прежнему глобален, как и x и total. Это связано с тем, что все выражение Manipulate считывается в глобальный контекст. Если это неудовлетворительно, то нам придется поместить все выражение в другой контекст - нельзя избежать того факта, что символы должны жить в некотором контексте.

Еще раз в прорыв

Хорошо, вот еще одна попытка удовлетворить дополнительные требования:

Manipulate[
  Module[{f, g}
  , Module[{total = 0}, f[] := ++total]
  ; Module[{total = 0}, g[] := --total]
  ; Dynamic[{x, f[], g[]}]
  ]
, {x, 1, 10}
]

Это решение имеет следующие особенности:

  • определения f и g локализованы в ячейке
  • два вхождения total изолированы друг от друга и локализованы в ячейке
  • переменные total не сбрасываются в ноль всякий раз, когда вы манипулируете x
  • если вы сделаете копию полученной выходной ячейки, копия не будет иметь никакого состояния с первой ячейкой
2 голосов
/ 24 декабря 2011

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

https://stackoverflow.com/a/8552728/618728

То есть, используяэлементы управления, такие как: {{x, 0}, None}

Адаптирует ли ваш добавленный пример, это работает?

Manipulate[
 n;
 processA[] := (total = total + 1);
 processB[] := (total = total + 1);
 Grid[{{"module A total=", processA[]}, {"module B total=", processB[]}}], 
 Button["press to update state", n++],
 {{n, 0}, None},
 {{total, 0}, None},
 TrackedSymbols :> {n}
]

Адаптирует недавний пример WReach:

Manipulate[
  {x, myAdd[]},
  {x, 1, 10},
  {{total, 0}, None},
  {{myAdd, (total += 1; total) &}, None}
]

Вот пример использованияпоследнее обновление вопроса.Насколько я могу сказать, это работает.Я предлагаю вам найти способ обойти необходимость в отдельных символах.

Manipulate[ctrl;
 Grid[{
   {"processA current total=", aResult},
   {"processA current total=", bResult}
 }], 
 Button["update A process", {ctrl++; aResult = processA[]}],
 Button["update B process", {ctrl++; bResult = processB[]}],
 {{ctrl, 0}, None},
 {{aResult, 0}, None},
 {{bResult, 0}, None},
 {{total1, 0}, None},
 {{total2, 0}, None},
 {{processA, (++total1) &}, None},
 {{processB, (++total2) &}, None},
 TrackedSymbols :> {ctrl}
]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...