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

时间:2009-12-08 22:31:55

标签: functional-programming scheme racket

我正在尝试理解示例代码here(下面的示例)。我不明白 parametrize 构造。它的文档是here,但它们没有帮助。它做了什么?

3 个答案:

答案 0 :(得分:13)

parameterize用于具有“动态范围”的值。你得到一个make-parameter的参数。参数本身就像一个函数:在没有输入的情况下调用它并获得它的值,用一个值调用它并设置值。例如:

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

许多函数(包括许多原始函数)使用参数作为自定义行为的方法。例如,printf将使用作为current-output-port参数值的端口打印内容。现在,假设你有一些打印东西的功能:

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

您通常会调用此功能并在屏幕上看到打印的内容 - 但在某些情况下,您希望使用它来将某些内容打印到文件或其他内容。你可以这样做:

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

这样做的一个问题是做起来很繁琐 - 但用宏来解决这个问题很容易。 (事实上​​,PLT仍然有一个构造在某些语言中执行:fluid-let。)但是这里有更多问题:如果对foo的调用导致运行时错误会发生什么?这可能会使系统处于不良状态,所有输出都会进入您的端口(您甚至不会看到问题,因为它不会打印任何内容)。针对该问题的解决方案(fluid-let也使用)是使用dynamic-wind保护参数的保存/恢复,这可确保如果出现错误(如果您了解延续,则更多)价值仍然恢复。

所以问题是如何使用参数而不仅仅使用全局变量和fluid-let?只有全局变量还有两个问题无法解决。一个是当你有多个线程时会发生什么 - 在这种情况下,临时设置值将影响其他线程,这可能仍然需要打印到标准输出。参数通过为每个线程设置特定值来解决此问题。会发生什么是每个线程从创建它的线程“继承”该值,并且一个线程中的更改仅在该线程中可见。

另一个问题更微妙。假设您有一个带有数值的参数,并且您想要执行以下操作:

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

在Scheme中,“尾调用”很重要 - 它们是创建循环的基本工具等等。 parameterize做了一些魔术,允许它暂时更改参数值,但仍保留这些尾调用。例如,在上面的例子中,你获得一个无限循环,而不是获得一个堆栈溢出错误 - 会发生什么是这些parameterize表达式中的每一个都可以以某种方式检测何时存在早期parameterize不再需要进行清理。

最后,parameterize实际上使用PLT的两个重要部分来完成它的工作:它使用线程单元来实现每线程值,并使用连续标记来保留尾调用。每个功能本身都很有用。

答案 1 :(得分:3)

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

答案 2 :(得分:3)

参数化是一种可以动态重新绑定现有函数中的值的方法,而不使用lambda来实现。在实践中,有时使用参数化来重新绑定函数中的值而不是传递参数并使用lambda绑定它们要容易得多。

例如,假设您使用的库将HTML发送到stdout,但为了方便起见,您希望将该值捕获到字符串并对其执行进一步操作。库设计者至少有两种选择可以让您轻松:1)接受输出端口作为函数的参数或2)参数化current-output-port值。 1是丑陋和麻烦。 2更好,因为最可能的行为是打印到stdout,但是如果你想打印到字符串端口,你可以参数化对该函数的调用。

相关问题