Как определить константы для использования с With [] в одном месте, а затем применить их позже? - PullRequest
4 голосов
/ 07 января 2012

Мне нравится использовать With[] с константами, которые мне нужно использовать в 2 разных местах.

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

Проблема в том, что мне нужно Hold список и затем ReleaseHold позже, когда придет времяиспользуйте это, но я не могу понять эту часть правильно.(перепробовал много вещей, у меня ничего не получалось)

Вот пример:

With[{$age = 2, $salary = 3},
 x = $age
 ]

With[{$age = 2, $salary = 3},
 y = $age
 ]

Я не хочу набирать одни и те же константы (в моем случае, они очень длинные), и я пытался сделать что-то вроде этого:

c = HoldForm[{$age = 2, $salary = 3}]

With[Release[c],
 x = $age
 ]

With[Release[c],
 y = $age
 ]

Я пробовал много других комбинаций выше.Так много версий Hold * и Release *, я нахожу их всех очень запутанными.

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

Чтобы поместить это в контекст, вот пример, где это было бы необходимо:

Я не могу все окружить With в Манипулировать :(и я не могу поставить With вне Manipulate для того, что я делаю, все должно быть внутри Manipulate)

Manipulate[

 Evaluate@With[{$age = 2, $salary = 3},
   x;
   $age,

   {{x, $age, "age="}, 0, 10, 1}
   ]

 ]

Недопустимый синтаксис.(из-за "," необходимо отделить выражение Manipulate от элементов управления).(теперь он думает, что у С есть 3 аргумента)

Я мог бы, конечно, сделать

Manipulate[

 With[{$age = 2, $salary = 3},
  x;
  $age
  ],

 Evaluate@With[{$age = 2, $salary = 3},
   {{x, $age, "age="}, 0, 10, 1}
   ]

 ]

Но, как вы видите, мне пришлось скопировать список констант вокруг.

Если я могу понять, как определить список один раз, я могу поместить его в раздел Initialization на Manipulate и использовать его следующим образом:

Manipulate[

 With[ReleaseHold[myList],
  x;
  $age
  ],

 Evaluate@With[ReleaseHold[myList],
   {{x, $age, "age="}, 0, 10, 1}
   ],

 Initialization :>
  (
   myList = HoldAll[{$age = 2, $salary = 3}];
   )

 ]

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

edit (1)

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

Я придумалэтот новый метод :), чтобы позволить мне моделировать запись или структуру.

Постоянными значениями будет именованное поле записи (на самом деле это просто список).

Для имени поля я даю ему порядковый номер (я начинаю с 1) иЯ использую $field=number, а затем в коде я пишу struct[[$field]]=... для доступа к полю.

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

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

Запись используется дляпередавать данные между областью управления и функциями выражения и нижнего уровня.

В моей текущей демонстрации у меня есть сотни таких параметров (у меня фактически есть несколько демонстраций, все в одной демонстрации, и я переключаюсь междуразличные макеты пользовательского интерфейса (элементы управления), основанные на том, какой выбор выбран в пользовательском интерфейсе) и использование записей упростят мне жизнь, поскольку теперь я могу выполнять вызовы функций и просто передавать несколько параметров, записей, которые содержат параметры пользовательского интерфейса, а не сотниотдельные параметры, что я должен сделать сейчас.Как я уже говорил много раз ранее, Mathematica нуждается в реальной записи / структуре в качестве базовой структуры данных, в дополнение к List, Array и т. Д., Которые интегрированы в M.

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

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

 (*verison 1.1*)
Manipulate[

 With[{$age = 1, $salary = 2, $newSalary = 3},

  updateRecord[record_] := Module[{},

    (*update/process UI input*)

    record[[$newSalary]] = record[[$salary]] + record[[$age]]*10;

    (*return result*)
    record
    ];

  (*call lower level function to process UI parameters*)
  myRecord = updateRecord[Unevaluated@myRecord];

  (*display the result *)
  Grid[{
    {"age=", myRecord[[$age]]},
    {"current salary=", myRecord[[$salary]]},
    {"new salary=", myRecord[[$newSalary]]}
    }]
  ],

 (* build the UI controls *)
 Evaluate@With[{$age = 1, $salary = 2, $newSalary = 3},
   Grid[{

     {"age=",
      Manipulator[Dynamic[age, {age = #; myRecord[[$age]] = age} &],
       {10, 100, 1}, ImageSize -> Tiny], Dynamic[age]},

     {"salary=",
      Manipulator[
       Dynamic[salary, {salary = #; myRecord[[$salary]] = salary} &],
       {10, 10000, 10}, ImageSize -> Tiny], Dynamic[salary]}

     }
    ]
   ],

 {{myRecord, {10, 100, 100}}, None},
 {{age, 10}, None},
 {{salary, 1000}, None},
 TrackedSymbols -> {age, salary}

 ]

enter image description here

edit (2)

При попытке использовать Леонида ниже, я могу использовать его в выражении Manipulate, но не могу понять, как его использовать в области управления.

Вот оригинальный пример использования With в 2 местах.:

Manipulate[

 With[{$age = 2, $salary = 3},
  x + $age
  ],

 Evaluate@With[
   {$age = 2, $salary = 3},
   {{x, $age, "age="}, 0, 10, 1}
   ],
 {x, None}
 ]

Теперь, используя новый метод Леонида, приведенный ниже, это то, что у меня есть:

Manipulate[

 env[
  x + $age
  ],

 Evaluate@With[
   {$age = 2, $salary = 3},
   {{x, $age, "age="}, 0, 10, 1}
   ],
 {x, None},

 Initialization :>
  (
   ClearAll[makeCustomEnvironment];
   SetAttributes[makeCustomEnvironment, HoldAll]; 
   makeCustomEnvironment[values : (_Symbol = _) ..] := 
    Function[code, With @@ Hold[{values}, code], HoldAll];

   env = makeCustomEnvironment[$age = 2, $salary = 3];

   )
 ]

Но возможно ли использовать его и для управления?Я не могу просто сделать это:

Manipulate[

 env[
  x + $age
  ],

 env[
  {$age = 2, $salary = 3},
  {{x, $age, "age="}, 0, 10, 1}
  ],
 {x, None},

 Initialization :>
  (
   ClearAll[makeCustomEnvironment];
   SetAttributes[makeCustomEnvironment, HoldAll]; 
   makeCustomEnvironment[values : (_Symbol = _) ..] := 
    Function[code, With @@ Hold[{values}, code], HoldAll];

   env = makeCustomEnvironment[$age = 2, $salary = 3];

   )
 ]

, так как выше выдает много ошибок.

редактировать (3)

удалено как было неверно

редактировать (4)

удалил содержание правки edit (3) выше, так как он содержал ошибку пользователя, сообщавшую мне о проблеме.

Вот ответ службы поддержки WRI на причину, по которой я получаю ошибку

Manipulate::vsform: Manipulate argument env[{{age,100,age},10,200,1}] does
not have the correct form for a variable specification. >>

Когда я писал следующий код:

Manipulate[
  env[
   record[[$age]] = age;
   record[[$salary]] = 60*age;
   {record[[$age]], record[[$salary]]}
   ],

 env[
    {{age, 100, "age"}, 10, 200, 1}
    ],

  {{record, {40, 5}}, None},
  {{salary, 40000}, None},
  TrackedSymbols :> {age},
  Initialization :>
   (
    makeCustomEnvironmentAlt =
     Function[Null, Function[code, With @@ Hold[{##}, code], HoldAll],
      HoldAll];
    env = makeCustomEnvironmentAlt[$age = 1, $salary = 2];
    )

  ]

Это объяснение поддержки, почему появилась эта ошибка:

The issue is specifically with the section:

Evaluate@env[{{age, 100, "age"}, 10, 200, 1}]

Manipulate doesn't really evaluate until it gets to the Initialization
option, but it will check its input for correct form. Mathematica reads the
main body of the Manipulate before running the Initialization option. This
is can be verified by using a Print statement:

Initialization -> (Print["Test"]; 
  makeCustomEnvironmentAlt = 
   Function[Null, Function[code, With @@ Hold[{##}, code], HoldAll], 
    HoldAll];
  env = makeCustomEnvironmentAlt[$age = 1, $salary = 2]; 
  Print["Test"])

Test does not print. 

Getting around this will be probably not be clean.
....
Having  the code for the controller for age depend on evaluation of 
some function which must be initialized does not appear to be possible 
with simply Manipulate. 

Надеюсь, эта информация поможет.И спасибо всем за помощь, а также за поддержку и объяснение WRI.

Ответы [ 2 ]

6 голосов
/ 07 января 2012

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

ClearAll[makeCustomEnvironment];
SetAttributes[makeCustomEnvironment, HoldAll];
makeCustomEnvironment[values : (_Symbol = _) ..] :=
   Function[code, With @@ Hold[{values}, code], HoldAll];

Это берет список объявлений и создает чистую функцию, которая использует With внутри с инкапсулированными константами.,Я использовал With@@Hold[...], чтобы обмануть механизм переименования Function, чтобы он не переименовал переменные внутри With (вместо With можно было бы использовать функцию withRules, предложенную @Szabolcs, что привело бы кнемного другая семантика замещения).

Теперь мы создаем нашу пользовательскую функцию:

env = makeCustomEnvironment[$age = 2, $salary = 3];

И используем ее так:

In[25]:= 
env[x=$age];
x

Out[26]= 2

In[27]:= 
env[y=$age];
y

Out[28]= 2

Преимущество этой конструкцииЧто касается сохраненной переменной (с правилами или чем-то еще), то здесь мы инкапсулируем поведение, а не состояние.Это, возможно, чище и больше соответствует парадигме функционального программирования (здесь мы создаем замыкание, а не создаем экземпляр класса).

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

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

makeCustomEnvironmentAlt = 
 Function[Null,
   Function[code, With @@ Hold[{##}, code], HoldAll],
   HoldAll]

Но вам придется запомнить формат входных аргументов (который был понятен для первоначального решения из-за использования внушительного шаблона).

1 голос
/ 07 января 2012

Я бы рекомендовал использовать для этой цели Replace и Rule с вместо With. См., Например, функцию withRules, которую я описал здесь , которую я использую для цели, аналогичной описанной вами: внедрение предопределенного списка констант в код без необходимости определять эти константы как глобальные переменные.

ClearAll[withRules]
SetAttributes[withRules, HoldAll]
withRules[rules_, expr_] :=
  Internal`InheritedBlock[
    {Rule, RuleDelayed},
    SetAttributes[{Rule, RuleDelayed}, HoldFirst];
    Unevaluated[expr] /. rules
  ]

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

constants = { a -> 1, b :> 2+2 }

withRules[ constants, a+b ]

Обратите внимание, что вы можете использовать как ->, так и :>, поэтому вам не нужно Hold вводить константы.

Слово предупреждения:

Также обратите внимание, что он отличается от With тем, что на самом деле он не локализует переменные. Рассмотрим With[{x=1}, With[{x=2}, x]], который переименовывает x во внутренний With, чтобы избежать конфликта. withRules не делает этого: безжалостно заменяет каждые x в пределах withRules[{x-> ...}, ...] независимо от того, появляется ли оно в другом Function, With или Module или нет.


Что касается вашего Manipulate примера, я бы просто обернул все Manipulate With, как в

With[
  {age = 2}, 
  Manipulate[{x, age}, {{x, age}, 0, 10, 1}]
]

Можете ли вы объяснить, почему вы хотите, чтобы все было внутри Manipulate (если вы не манипулируете age), и почему вы не можете просто обернуть это With?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...