gpt4 book ai didi

Scala 相当于 Haskell 的 where 子句?

转载 作者:行者123 更新时间:2023-12-03 14:36:29 24 4
gpt4 key购买 nike

是否可以使用类似于 Scala 中的 where-clauses 的东西?也许有一些我没有想到的技巧?

编辑:

感谢您的所有回答,非常感谢。总结:
本地 vars、vals 和 defs 可用于实现几乎相同的功能。对于惰性求值,可以使用惰性 val(带有隐式缓存)或函数定义。确保功能纯度留给程序员。

现在只剩下一个问题:有没有办法将值或函数定义放在它们被使用的表达式之后?有时这似乎更清楚。这对于类或对象的字段/方法是可能的,但它似乎不适用于方法。

到目前为止,答案中没有提到的另一件事。 where-clauses 还限制了其中定义的表达式的范围。我也没有找到在 Scala 中实现这一目标的方法。

最佳答案

在 Hakell 中,where 子句保存函数的局部定义。 Scala 没有明确的 where 子句,但可以通过本地 var 实现相同的功能。 , valdef .

本地`var`和`val`

在斯卡拉:

def foo(x: Int, y: Int): Int = {
val a = x + y
var b = x * y
a - b
}

在 haskell :
foo :: Integer -> Integer -> Integer 
foo x y = a - b
where
a = x + y
b = x * y

本地`def`

在斯卡拉
def foo(x: Int, y: Int): Int = {
def bar(x: Int) = x * x
y + bar(x)
}

在 haskell
foo :: Integer -> Integer -> Integer 
foo x y = y + bar x
where
bar x = x * x

如果我在 Haskell 示例中犯了任何语法错误,请纠正我,因为我目前在这台计算机上没有安装 Haskell 编译器 :)。

更复杂的例子可以用类似的方式实现(例如使用模式匹配,两种语言都支持)。局部函数与任何其他函数具有完全相同的语法,只是它们的作用域是它们所在的块。

编辑:另见 Daniel对此类示例的回答以及对该主题的一些详细说明。

编辑 2:添加了关于 lazy 的讨论 var s 和 val s。

懒惰的“var”和“val”

Edward Kmett的回答正确地指出了 Haskell 的 where 子句具有懒惰和纯洁性。您可以使用 lazy 在 Scala 中做一些非常相似的事情。变量。这些仅在需要时实例化。考虑以下示例:
def foo(x: Int, y: Int) = { 
print("--- Line 1: ");
lazy val lazy1: Int = { print("-- lazy1 evaluated "); x^2}
println();

print("--- Line 2: ");
lazy val lazy2: Int = { print("-- lazy2 evaluated "); y^2}
println();

print("--- Line 3: ");
lazy val lazy3: Int = { print("-- lazy3 evaluated ")
while(true) {} // infinite loop!
x^2 + y^2 }
println();

print("--- Line 4 (if clause): ");
if (x < y) lazy1 + lazy2
else lazy2 + lazy1
}

这里 lazy1 , lazy2lazy3都是惰性变量。 lazy3永远不会被实例化(因此这段代码永远不会进入无限循环)和 lazy1 的实例化顺序和 lazy2取决于函数的参数。例如,当您拨打 foo(1,2) 时你会得到 lazy1之前实例化 lazy2当您调用 foo(2,1) 时你会得到相反的结果。在 Scala 解释器中尝试代码并查看打印输出! (我不会把它放在这里,因为这个答案已经很长了)。

如果您使用无参数函数代替惰性变量,您可以获得类似的结果。在上面的示例中,您可以替换每个 lazy valdef并取得类似的结果。不同之处在于惰性变量被缓存(也就是只计算一次),但 def每次调用时都会对其进行评估。

编辑 3:添加了关于范围界定的讨论,请参阅问题。

本地定义的范围

正如预期的那样,本地定义具有声明它们的块的范围(好吧,大多数情况下,在极少数情况下,它们可以转义块,例如在 for 循环中使用中间流变量绑定(bind)时)。因此本地 var , valdef可用于限制表达式的范围。以下面的例子为例:
object Obj {
def bar = "outer scope"

def innerFun() {
def bar = "inner scope"
println(bar) // prints inner scope
}

def outerFun() {
println(bar) // prints outer scope
}

def smthDifferent() {
println(bar) // prints inner scope ! :)
def bar = "inner scope"
println(bar) // prints inner scope
}

def doesNotCompile() {
{
def fun = "fun" // local to this block
42 // blocks must not end with a definition...
}
println(fun)
}

}

两者 innerFun()outerFun()按预期行事。 bar的定义在 innerFun()隐藏 bar在封闭范围内定义。另外,函数 fun是其封闭块的局部,因此不能以其他方式使用。方法 doesNotCompile() ...不编译。有趣的是, println()来自 smthDifferent() 的电话方法打印 inner scope .因此,是的,您可以将定义放在方法内部使用之后!不过我不推荐,因为我认为这是不好的做法(至少在我看来)。在类文件中,您可以随意安排方法定义,但我会保留所有 def s 内部函数,然后再使用。和 val s 和 var s ... 嗯... 我觉得用完后把它们放起来很尴尬。

另请注意,每个块必须以表达式而不是定义结尾,因此您不能在块的末尾拥有所有定义。我可能会将所有定义放在一个块的开头,然后在该块的末尾编写所有产生结果的逻辑。这样看起来确实更自然,而不是:
{
// some logic

// some defs

// some other logic, returning the result
}

正如我之前所说,你不能只用 // some defs 结束一个块。 .这就是 Scala 与 Haskell 略有不同的地方:)。

编辑 4:详细说明使用后定义内容,由 Kim 提示的评论。

使用它们后定义“东西”

用一种有副作用的语言来实现这是一件棘手的事情。在一个纯无副作用的世界中,顺序并不重要(方法不依赖于任何副作用)。但是,由于 Scala 允许副作用,因此定义函数的位置很重要。此外,当您定义 val 时或 var ,右侧必须被评估到位以实例化 val .考虑以下示例:
// does not compile :)
def foo(x: Int) = {

// println *has* to execute now, but
// cannot call f(10) as the closure
// that you call has not been created yet!
// it's similar to calling a variable that is null
println(f(10))

var aVar = 1

// the closure has to be created here,
// as it cannot capture aVar otherwise
def f(i: Int) = i + aVar

aVar = aVar + 1

f(10)
}

如果 val,您给出的示例确实有效s 是 lazy或者他们是 def s。
def foo(): Int = {
println(1)
lazy val a = { println("a"); b }
println(2)
lazy val b = { println("b"); 1 }
println(3)
a + a
}

这个例子也很好地展示了工作中的缓存(尝试将 lazy val 更改为 def 看看会发生什么:)

我仍然在一个有副作用的世界里,最好在使用它们之前坚持定义。这样更容易阅读源代码。
-- Flaviu Cipcigan

关于Scala 相当于 Haskell 的 where 子句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1284325/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com