加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala相当于Haskell的where-clauses?

发布时间:2020-12-16 09:30:23 所属栏目:安全 来源:网络整理
导读:有可能使用类似于Scala中的where-clause的东西吗?也许有一些我没想到的技巧? 编辑: 感谢您的所有答案,他们非常感激。总结一下: 本地的vars,vals和defs可以用来实现几乎相同的事情。对于懒惰评估,可以使用惰性val(带隐式缓存)或函数定义。确保功能纯度
有可能使用类似于Scala中的where-clause的东西吗?也许有一些我没想到的技巧?

编辑:

感谢您的所有答案,他们非常感激。总结一下:
本地的vars,vals和defs可以用来实现几乎相同的事情。对于懒惰评估,可以使用惰性val(带隐式缓存)或函数定义。确保功能纯度留给程序员。

现在只剩下一个问题:有没有办法将值或函数定义放在使用表达式之后?有时看起来很清楚。这可以通过类或对象的fields / methods来实现,但在方法中似乎不起作用。

迄今为止,答案中还没有提到另外一件事。 where-clause也限制了它们中定义的表达式的范围。我还没有找到一种在Scala中实现这一点的方法。

解决方法

在Hakell中,其中的子句将本地定义保存到函数中。 Scala没有明确的where子句,但通过使用本地的var,val和def可以实现相同的功能。

本地`var`和`val`

在斯卡拉:

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

在哈斯克尔

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)
}

在哈斯克尔

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

如果我在Haskell示例中出现任何语法错误,请更正我,因为我目前没有在此计算机上安装Haskell编译器:)。

可以以类似的方式实现更复杂的示例(例如使用两种语言支持的模式匹配)。本地函数具有与其他函数完全相同的语法,只是它们的范围是它们所在的块。

编辑:另见Daniel的这个例子的答案和一些关于这个问题的阐述。

编辑2:增加了关于惰性变量和值的讨论。

懒惰`var`和`val`

Edward Kmett的答案正确地指出,Haskell的where子句有懒惰和纯洁。您可以使用延迟变量在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,lazy2和lazy3都是懒惰的变量。 lazy3从来没有被实例化(因此这个代码永远不会进入无限循环),并且lazy1和lazy2的实例化顺序取决于函数的参数。例如,当您调用foo(1,2)时,将在lazy2之前实例化lazy1,当您调用foo(2,1)时,将会得到相反的结果。尝试在scala解释器中的代码,看看打印输出! (我不会把它放在这里,因为这个答案已经很久了)。

您可以获得类似的结果,而不是使用非参数函数的延迟变量。在上面的示例中,您可以用def替换每个惰性值,并获得类似的结果。不同之处在于缓存变量(也称为一次评估),但是每次调用def时都会进行评估。

编辑3:增加了关于范围的讨论,看问题。

本地定义的范围

本地定义具有它们被声明的块的范围,如预期的那样(在大多数情况下,在很少的情况下,它们可以逃避块,就像在for循环中使用mid-stream变量绑定一样)。因此,可以使用局部var,val和def来限制表达式的范围。举个例子:

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()都按预期方式运行。 innerFun()中的bar的定义隐藏了包围范围中定义的条。此外,函数fun是其封闭块的本地,因此不能以其他方式使用。方法doesNotCompile()…不编译。有趣的是注意到来自smthDifferent()方法的println()调用打印内部范围。因此,是的,您可以在方法中使用定义之后!我不会推荐,因为我认为这是不好的做法(至少在我看来)。在类文件中,您可以根据需要安排方法定义,但是在使用之前,我会将所有的defs保留在函数中。和vals和vars …好…我发现它使用后把它们尴尬。

还要注意,每个块必须以不带定义的表达式结束,因此您不能在块的末尾具有所有定义。我可能把所有的定义放在一个块的开始处,然后写出所有的逻辑,在该块的末尾产生一个结果。这似乎更自然的方式,而不是:

{
// some logic

// some defs

// some other logic,returning the result
}

正如我以前所说的,你不能只是//一些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)
}

你给的例子是工作,虽然vals是懒惰或他们def def。

def foo(): Int = {
  println(1)
  lazy val a = { println("a"); b }
  println(2)
  lazy val b = { println("b"); 1 }
  println(3)
  a + a
}

这个例子也很好地显示了工作中的缓存(尝试改变懒惰val来看看发生什么事情:)

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

– Flaviu Cipcigan

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读