如何在一处定义与 With[] 一起使用的常量,然后再应用它们? [英] How to define constants for use with With[] in one place and then apply them later?

查看:27
本文介绍了如何在一处定义与 With[] 一起使用的常量,然后再应用它们?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我喜欢将 With[] 与我需要在 2 个不同地方使用的常量一起使用.

I like to use With[] with constants that I need to use in 2 different places.

我没有在 2 个地方输入同样长的常量列表,而是试图弄清楚如何为这个列表使用一个变量,然后在我想使用列表的几个地方使用这个变量.

Instead of typing the same long list of constants in 2 places, I have been trying to figure how to use a variable for this list, and then use this variable in the few places I want to use the list.

问题是我需要 Hold 列表,然后 ReleaseHold 稍后使用它,但我无法正确完成这部分.(尝试了很多东西,对我不起作用)

The problem is that I need to Hold the list and then ReleaseHold it later when time to use it, but I can't get this part right. (tried many things, nothing working for me)

这是一个例子:

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

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

我不想输入相同的常量(在我的例子中,这些常量很长),我试图做这样的事情:

I do not want to type the same constants around (in my case, these are very long), and I was trying to do something like this:

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

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

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

我尝试了上述的许多其他组合.这么多版本的 Hold* 和 Release*,我都觉得很混乱.

I tried many other combinations of the above. So many version of Hold* and Release*, I find them all very confusing.

问题是:有谁知道怎么做上面的,所以我可以在一个以上的地方重复使用这个列表而无需实际复制它?

为了将其放在上下文中,这是一个需要这样做的示例:

To put this in context, here is an example where this would be needed:

我不能在 Manipulate 中用 With 包围所有东西:(而且我不能在 Manipulate 外面放一个 With 来做我正在做的事情,一切都必须在里面操作)

I can't do surround everything with With in Manipulate: (and I can't put a With outside Manipulate for what I am doing, everything must be inside Manipulate)

Manipulate[

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

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

 ]

无效的语法.(由于,"需要将 Manipulate 表达式与控件分开).(它现在认为 With 有 3 个参数)

Not valid syntax. (due to the "," needed to separate the Manipulate expression from the controls). (it now thinks With has 3 arguments)

我当然可以

Manipulate[

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

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

 ]

但是如您所见,我不得不复制常量列表.

But as you see, I had to copy the list of constants around.

如果我能弄清楚如何定义列表一次,我可以把它放在ManipulateInitialization部分并使用它,就像这样:

If I can figure how to define the list once, I can put it in the Initialization section on Manipulate and use it, like this:

Manipulate[

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

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

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

 ]

我认为我想做的事情是可能的,我只是想不出用于保持/释放保持部分的正确命令.

I think what I want to do is possible, I just can't figure the correct commands to use for the Hold/Release hold part.

编辑 (1)

我想我举了一个例子来说明为什么要在 With 中使用常量.

I thought I put an example of why I want to use constants in With.

我想出了这个新方法:) 允许我模拟记录或结构.

I came up with this new method :) to allow me to simulate a record or a struct.

常量值将是记录的命名字段(实际上只是一个列表).

The constant values will be the named field of the record (which is just a list really).

对于字段名,我给它一个序列号,(我从1开始)并使用$field=number,然后在代码中,我写struct[[$field]]=... 访问该字段.

For field name, I give it a sequential number, (I start from 1) and I use $field=number, and then in the code, I write struct[[$field]]=... to access the field.

我需要将命名字段的值共享给 Manipulate 表达式和控制区域之间的结构,因为两者都需要使用相同的结构.

I need to share the values of the named fields to the struct between the Manipulate expression and the control area, since both need to use the same struct.

下面是一个简单的 Manipulate 示例,它从 UI 和表达式中读取年龄和当前薪水,并根据当前薪水和以前分配新薪水.

Here is a simple example below of a Manipulate that reads the age and current salary from the UI and the expression and assigns new salary based on current salary and ago.

记录用于在控制区与表达式和低级函数之间进行数据通信.

The record is used to communicate the data between the control area and the expression and lower level functions.

在我当前的演示中,我有数百个这样的参数,(我现在几乎没有演示,全部在一个演示中,我根据在 UI 上选择的选项在不同的 UI 布局(控件)之间切换)并使用记录将简化我的生活,因为现在我可以进行函数调用,并且只传递几个参数,包含 UI 参数的记录,而不是 100 个单独的参数,这就是我现在必须做的.之前说过很多次了,Mathematica 需要一个真实的record/struct 作为基础数据结构,除了List、Array 之类的,集​​成到M 中.

In my current demo, I have hundreds of such parameters, (I actually have now few demos, all in one demo, and I flip between different UI layouts (controls) based on which choice is selected on the UI) and using records will simplify life for me, since now I can make function calls, and just pass few parameters, records that contain the UI parameters, and not 100's of the individual parameters, which is what I have to do now. As I said many times before, Mathematica needs a real record/struct as a basic data struct, in addition to List, and Array and such, which is integrated into M.

(UI 参数必须全部发送到较低级别的函数,除此之外别无选择.我不想使用全局变量.糟糕的设计).

(the UI parameters have to be send, all, to lower level functions, and there is no other choice than doing this. I do not want to use global variables. Bad design).

我现在也可以通过引用传递这个记录,如果我想允许更新在其他更低级别的函数中发生.我还在评估这个方法,看看我是否真的可以在我的主代码中使用它.

I also can now pass this record by reference If I want to to allow updates to happen into it inside other much lower level functions. I am still evaluating this method to see if I can actually use it in my main code.

 (*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}

 ]

编辑(2)

在下面尝试使用Leonid时,我可以在Manipulate表达式中使用它,但无法弄清楚如何在控制区域中使用.

On trying to use Leonid below, I can use it in the Manipulate expression, but can't figure how to use in the control area.

这是在 2 个地方使用 With 的原始示例:

Here is the original example using With in 2 places:

Manipulate[

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

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

现在使用下面的新列昂尼德方法,这是我目前所拥有的:

now using the new Leonid method below, this is what I have so far:

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];

   )
 ]

但是它也可以用于控制吗?我不能只是这样做:

But is it possible to use it for the control also? I can't just do this:

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];

   )
 ]

因为上面给出了很多错误.

as the above gives many errors.

编辑(3)

删除不正确

编辑 (4)

删除了上面 edit(3) 的内容,因为它包含我这边报告问题的用户错误.

deleted content of edit(3) above as it included a user error on my side reporting an issue.

这是 WRI 支持对我收到错误原因的回应

Here is the WRI support response to why I was getting the error

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];
    )

  ]

这是为什么出现此错误的支持解释:

This is the support explanation of why this error came showed up:

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 的支持和解释.

I hope this information helps. And thanks for everyone's help and also for WRI support and explanation.

推荐答案

我要做的是编写一个函数生成器来创建自定义(词法)环境:

What I'd do is to write a function generator to create custom (lexical) environments:

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

这需要一个声明列表并创建一个纯函数,该函数在内部使用带有封装常量的 With.我使用 With@@Hold[...] 来欺骗 Function 的重命名机制,使其不会重命名 With 中的变量(可以使用@Szabolcs 建议的 withRules 函数代替 With,这会导致替换语义略有不同.

This takes a declaration list and creates a pure function which uses With internally with encapsulated constants. I used With@@Hold[...] to fool the renaming mechanism of Function so that it would not rename the variables inside With (instead of With, one could use the withRules function suggested by @Szabolcs, which would lead to a slightly different substitution semantics).

现在,我们创建自定义函数:

Now, we create our custom function:

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

并像这样使用它:

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

Out[26]= 2

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

Out[28]= 2

这种结构相对于保存的变量(带有规则或其他)的优势在于,我们在这里封装了行为而不是状态.这可以说更简洁,更符合函数式编程范式(我们在这里创建一个闭包而不是实例化一个类).

The advantage of this construct with respect to a saved variable (with rules or whatever) is that here we encapsulate behavior rather than state. This is arguably cleaner and more along the functional programming paradigm (we create here a closure rather than instantiate a class).

编辑

显然,演示的规则非常严格,不允许使用建议的代码.希望这个版本没问题:

Apparently, the rules for demonstrations are pretty strict, and won't allow the sugested code. This version will hopefully be ok:

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

但是您必须记住输入参数的格式(由于使用了暗示模式,这对于初始解决方案来说很清楚).

But you'll have to remember the format of input arguments (which was clear for the initial solution due to the use of suggestive pattern).

这篇关于如何在一处定义与 With[] 一起使用的常量,然后再应用它们?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆