“参数化”是什么意思?在 DrScheme 中做什么?
I'm trying to make sense of the example code here (below Examples). I don't understand that parametrize construct. The docs for it are here, but they don't help. What does it do?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
parameterize
用于具有“动态范围”的值。您可以使用make-parameter
获取参数。参数本身就像一个函数:在没有输入的情况下调用它,您将获得它的值,使用一个值调用它,它将设置该值。例如:许多函数(包括许多原始函数)使用参数作为自定义其行为的方式。例如,
printf
将使用作为current-output-port
参数值的端口来打印内容。现在,假设您有一些打印某些内容的函数:您通常调用此函数并看到屏幕上打印的内容 - 但在某些情况下,您想使用它来将某些内容打印到文件或其他内容中。你可以这样做:
这样做的一个问题是做起来很乏味——但这很容易用宏解决。 (事实上,PLT 仍然有一个在某些语言中执行此操作的构造:
fluid-let
。)但是这里还有更多问题:如果对foo
的调用结果会发生什么运行时错误?这可能会使系统处于不良状态,所有输出都会发送到您的端口(您甚至不会看到问题,因为它不会打印任何内容)。解决方案(fluid-let
也使用)是使用dynamic-wind
保护参数的保存/恢复,这确保如果出现错误(以及更多,如果您了解延续),那么该值仍然会恢复。所以问题是使用参数而不是仅仅使用全局变量和 Fluid-let 的意义何在?还有两个问题是仅用全局变量无法解决的。一种是当您有多个线程时会发生的情况 - 在这种情况下,临时设置该值将影响其他线程,这些线程可能仍希望打印到标准输出。参数通过为每个线程指定一个特定值来解决这个问题。所发生的情况是,每个线程“继承”创建它的线程的值,并且一个线程中的更改仅在该线程中可见。
另一个问题则更加微妙。假设您有一个带有数值的参数,并且您想要执行以下操作:
在Scheme 中,“尾部调用”很重要——它们是创建循环等的基本工具。
parameterize
做了一些魔法,允许它暂时更改参数值,但仍然保留这些尾部调用。例如,在上述情况下,您将得到一个无限循环,而不是得到堆栈溢出错误 - 发生的情况是这些参数化
表达式中的每一个都可以以某种方式检测到当早期的parameterize
不再需要进行清理时。最后,parameterize 实际上使用 PLT 的两个重要部分来完成其工作:它使用线程单元来实现每个线程的值,并使用连续标记来保留尾部调用。这些功能中的每一个本身都是有用的。
parameterize
is used to have values that are "dynamically scoped". You get a parameter withmake-parameter
. The parameter itself behaves as a function: call it with no inputs and you get its value, call it with one value and it will set the value. For example:Many functions (including many primitive ones) use parameters as a way to customize their behavior. For example
printf
will print stuff using the port that is the value of thecurrent-output-port
parameter. Now, say that you have some function that prints something:You usually call this function and see something printed on the screen -- but in some cases you want to use it to print something to a file or whatever. You could do this:
One problem with this is that it is tedious to do -- but that's easily solved with a macro. (In fact, PLT still has a construct that does that in some languages:
fluid-let
.) But there are more problems here: what happens if the call tofoo
results in a runtime error? This might leave the system in a bad state, where all output goes to your port (and you won't even see a problem, since it won't print anything). A solution for that (whichfluid-let
uses too) is to protect the saving/restoring of the parameter withdynamic-wind
, which makes sure that if there's an error (and more, if you know about continuations) then the value is still restored.So the question is what's the point of having parameters instead of just using globals and
fluid-let
? There are two more problems that you cannot solve with just globals. One is what happens when you have multiple threads -- in this case, setting the value temporarily will affect other threads, which may still want to print to the standard output. Parameters solve this by having a specific value per-thread. What happens is that each thread "inherits" the value from the thread that created it, and changes in one thread are visible only in that thread.The other problem is more subtle. Say that you have a parameter with a numeric value, and you want to do the following:
In Scheme, "tail calls" are important -- they are the basic tool for creating loops and much more.
parameterize
does some magic that allows it to change the parameter value temporarily but still preserve these tail calls. For example, in the above case, you will get an infinite loop, rather than get a stack overflow error -- what happens is that each of theseparameterize
expressions can somehow detect when there's an earlierparameterize
that no longer needs to do its cleanup.Finally,
parameterize
actually uses two important parts of PLT to do its job: it uses thread cells to implement per-thread values, and it uses continuation marks to be able to preserve tail-calls. Each of these features is useful in itself.parameterize
在块的持续时间内将特定参数设置为指定值,而不影响块之外的值。parameterize
sets particular parameters to specified values for the duration of the block, without affecting their values outside of it.参数化是一种可以在现有函数中动态重新绑定值的方法,而无需使用 lambda 来执行此操作。在实践中,有时使用参数化在函数内重新绑定值比需要传递参数并使用 lambda 绑定它们要容易得多。
例如,假设您使用的库将 HTML 发送到标准输出,但为了方便起见,您希望将该值捕获到字符串中并对其执行进一步的操作。库设计者至少有两种选择可以让您轻松实现这一点:1)接受输出端口作为函数的参数,或 2)参数化当前输出端口值。 1 又丑又麻烦。 2 更好,因为最可能的行为是打印到标准输出,但如果您想打印到字符串端口,您只需参数化对该函数的调用即可。
Parameterize is a means by which you can dynamically re-bind values within an existing function, without using lambda to do so. In practice sometimes it is a lot easier to use parameterize to re-bind values within a function rather than being required to pass arguments and bind them using lambda.
For example, say that a library that you use emits HTML to stdout but for sake of convenience you want to capture that value to a string and perform further operations on it. The library designer has at least two choices to make that easy for you: 1) accept an output port as a argument to the function or 2) parameterize the current-output-port value. 1 is ugly and a hassle. 2 is nicer since the most likely behavior is to print to stdout, but in case you want to print to a string-port you can just parameterize the call to that function.