简体   繁体   English

“参数化”在DrScheme中做了什么?

[英]What does 'parametrize' do in 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? 它有什么作用?

parameterize is used to have values that are "dynamically scoped". parameterize用于具有“动态范围”的值。 You get a parameter with make-parameter . 你得到一个带make-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: 例如:

> (define p (make-parameter "blah"))
> (p)
"blah"
> (p "meh")
> (p)
"meh"

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 the current-output-port parameter. 例如, printf将使用作为current-output-port参数值的端口打印内容。 Now, say that you have some function that prints something: 现在,假设你有一些打印东西的功能:

> (define (foo x) (printf "the value of x is ~s\n"))

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: 你可以这样做:

(define (bar)
  (let ([old-stdout (current-output-port)])
    (current-output-port my-own-port)
    (foo some-value)
    (current-output-port old-stdout)))

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 to foo results in a runtime error? (事实上​​,PLT仍然有一个构造在某些语言中执行: fluid-let 。)但是这里有更多的问题:如果对foo的调用导致运行时错误会发生什么? 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 (which fluid-let uses too) is to protect the saving/restoring of the parameter with dynamic-wind , which makes sure that if there's an error (and more, if you know about continuations) then the value is still restored. 对此的解决方案(也fluid-let使用fluid-let )是使用dynamic-wind保护参数的保存/恢复,这样可以确保如果出现错误(如果你知道延续的话,还有更多),那么值仍然是恢复。

So the question is what's the point of having parameters instead of just using globals and fluid-let ? 所以问题是如何使用参数而不是仅使用全局变量和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: 假设您有一个带有数值的参数,并且您想要执行以下操作:

(define (foo)
  (parameterize ([p ...whatever...])
    (foo)))

In Scheme, "tail calls" are important -- they are the basic tool for creating loops and much more. 在Scheme中,“尾调用”很重要 - 它们是创建循环的基本工具等等。 parameterize does some magic that allows it to change the parameter value temporarily but still preserve these tail calls. parameterize化做了一些魔术,允许它暂时更改参数值,但仍保留这些尾调用。 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 these parameterize expressions can somehow detect when there's an earlier parameterize that no longer needs to do its cleanup. 例如,在上面的例子中,你得到一个无限循环,而不是得到一个堆栈溢出错误 - 会发生什么是这些parameterize表达式中的每一个都能以某种方式检测何时有一个不再需要进行清理的早期parameterize

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. 最后, parameterize实际上使用PLT的两个重要部分来完成它的工作:它使用线程单元来实现每线程值,并使用连续标记来保留尾调用。 Each of these features is useful in itself. 这些功能中的每一个本身都是有用的。

parameterize将特定parameterize设置为块的持续时间的指定值,而不影响它们之外的值。

Parameterize is a means by which you can dynamically re-bind values within an existing function, without using lambda to do so. 参数化是一种可以动态重新绑定现有函数中的值的方法,而不使用lambda来实现。 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. 在实践中,有时使用参数化来重新绑定函数中的值而不是传递参数并使用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. 例如,假设您使用的库将HTML发送到stdout,但为了方便起见,您希望将该值捕获到字符串并对其执行进一步操作。 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)接受输出端口作为函数的参数或2)参数化current-output-port值。 1 is ugly and a hassle. 1是丑陋和麻烦。 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. 2更好,因为最可能的行为是打印到stdout,但是如果你想打印到字符串端口,你可以参数化对该函数的调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM