从"Functional programming in scala"一书中,我看到了表达式的"引用透明"的定义:
我如果对于所有程序 p,p 中 e 的所有出现都可以替换为计算 e 的结果而不影响 p 的含义,则表达式 e 是引用透明的。
有一些代码示例,我不确定它们是否在引用上是透明的。
我将在示例中使用scala.collection.mutable.StringBuilder
,这是一个可变类
1.
val x = new StringBuilder("Hello")
println(x.length)
println(x.length)
假设这里的代码是使用 x
的完整代码。
我可以说表达式x
是一个引用性透明表达式吗?
如果我用其值new StringBuilder("Hello")
更改所有x
,则程序的可观察行为不会更改:
val x = new StringBuilder("Hello")
println(new StringBuilder("Hello").length)
println(new StringBuilder("Hello").length)
阿拉伯数字。
val x = new StringBuilder("Hello")
val y = x.append("aaa")
假设这里的代码是使用 x
和 y
的完整代码。
我可以说y
是引用透明的,因为它根本没有在程序中使用吗?
3.
def getTheClassName(n:Int):String = {
val x = new StringBuilder("hello")
for(int i=0;i<n;i++) {
x.append("world")
}
return x.getClass.getName
}
我可以说x
是参考透明的吗?因为无论我如何用它的值替换它,返回值都不会改变。
PS:也许主要问题是我不明白for all programs p
是什么意思,它是指现有的完整代码吗?或者可以添加任何可能的代码?
这意味着对于您可能编写的任何可能的程序p
包含该表达式。正确地,这应该针对一种语言或一组可能的程序进行定义。因此,您的x
在唯一有效的程序是您编写的程序的语言中是可引用的透明的。您的y
在 Scala 的子集中是引用透明的,其中您不允许在StringBuilder
上调用.append
。但这些并不是特别有趣的语言。
大多数时候,当人们谈论"引用透明"的表达式时,他们(隐含地)指的是像Scala的Scalazzi安全子集这样的东西,它足够通用,可以表达(所有?)有用的Scala程序,但足够安全,可以推理。因为当然,如果您被允许打电话,例如 System.identityHashCode()
,大多数所谓的"指代透明"表达实际上并非如此。
当然,最重要的定义是操作定义;什么是"引用透明"最终取决于你想使用它做什么。一个重要的用例是编译器/库优化:我们不希望编译器执行您在示例 1 中给出的替换,因为对于大多数程序来说,这种"优化"会改变程序的含义。但是我们很高兴编译器通过内联不可变常量来优化我们的程序,因为我们的程序"不应该"取决于特定常量的identityHashCode
是什么。
据我了解,表达式"e"在引用上是透明的,对于所有可能的程序"p",它是否在引用上是透明的。
对于具体的程序"p",它可以是"引用透明"的,但是如果你可以编写另一个违反"替换规则"的程序"px",这意味着表达式不是引用透明的。
import scala.collection.mutable
val b = new mutable.StringBuilder("Hello")
def e = b.length
def p0 = {
val l1 = e
val l2 = e
l1 + l2
}
def p1 = {
val l1 = e
b.append("World")
val l2 = e
l1 + l2
}
可以构建违反"p 中的 e 可以被评估 e 的结果替换"的程序 'p' - 这意味着 'e' 不是引用透明的。使用可变状态,构建此程序非常容易。
在 p0 中,我们可以说 e 是引用透明的,但 p1 很容易破坏它。