238

有什么区别:

def even: Int => Boolean = _ % 2 == 0

val even: Int => Boolean = _ % 2 == 0

两者都可以称为 like even(10)

4

9 回答 9

347

方法def even在调用时评估并每次创建新函数(的新实例Function1)。

def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false

val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

您可以在def每次通话时获得新功能:

val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1049057402
test()
// Int = -1049057402 - same result

def test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -240885810
test()
// Int = -1002157461 - new result

val定义时评估,def- 调用时:

scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing

scala> def even: Int => Boolean = ???
even: Int => Boolean

scala> even
scala.NotImplementedError: an implementation is missing

请注意,还有第三个选项:lazy val.

它在第一次调用时评估:

scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>

scala> even
scala.NotImplementedError: an implementation is missing

但每次都返回相同的结果(在本例中为 的相同实例FunctionN):

lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

lazy val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1068569869
test()
// Int = -1068569869 - same result

表现

val定义时进行评估。

def评估每次调用,因此性能可能比val多次调用更差。一次调用即可获得相同的性能。并且没有调用,您将不会从 中获得任何开销def,因此即使您不会在某些分支中使用它,您也可以定义它。

使用 alazy val你会得到一个惰性评估:即使你不会在某些分支中使用它,你也可以定义它,并且它评估一次或从不评估,但是你会从每次访问你的lazy val.

正如@SargeBorsch 所说,您可以定义方法,这是最快的选择:

def even(i: Int): Boolean = i % 2 == 0

但是,如果您需要一个函数(而不是方法)用于函数组合或更高阶函数(如filter(even)),每次您将其用作函数时,编译器都会从您的方法中生成一个函数,因此性能可能比 with 稍差val

于 2013-09-19T06:15:17.337 回答
24

考虑一下:

scala> def even: (Int => Boolean) = {
             println("def"); 
             (x => x % 2 == 0)
       }
even: Int => Boolean

scala> val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>

scala> even(1)
def
res9: Boolean = false

scala> even2(1)
res10: Boolean = false

你看得到差别吗?简而言之:

def:对于每次调用even,它都会even再次调用方法体。但是使用even2val,该函数在声明时仅初始化一次(因此它val在第 4 行打印并且不再打印),并且每次访问时都使用相同的输出。例如尝试这样做:

scala> import scala.util.Random
import scala.util.Random

scala> val x = { Random.nextInt }
x: Int = -1307706866

scala> x
res0: Int = -1307706866

scala> x
res1: Int = -1307706866

x初始化时,将返回的值设置Random.nextInt为 的最终值x。下次x再次使用,它总是返回相同的值。

你也可以懒惰地初始化x. 即第一次使用它时,它被初始化而不是在声明时。例如:

scala> lazy val y = { Random.nextInt }
y: Int = <lazy>

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
于 2013-09-19T06:15:15.847 回答
5

看到这个:

  var x = 2 // using var as I need to change it to 3 later
  val sq = x*x // evaluates right now
  x = 3 // no effect! sq is already evaluated
  println(sq)

令人惊讶的是,这将打印 4 而不是 9!val (even var) 被立即评估并赋值。
现在将 val 更改为 def.. 它将打印 9!Def 是一个函数调用.. 每次调用它都会评估。

于 2017-06-07T20:26:15.837 回答
1

val ie "sq" 是由 Scala 定义固定的。它在声明时立即评估,您以后无法更改。在其他示例中,even2 也是 val,但它使用函数签名即“(Int => Boolean)”声明,因此它不是 Int 类型。它是一个函数,它的值由以下表达式设置

   {
         println("val");
         (x => x % 2 == 0)
   }

根据 Scala val 属性,您不能将另一个函数分配给 even2,与 sq 相同的规则。

关于为什么调用 eval2 val 函数而不是一次又一次地打印“val”?

原始代码:

val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }

我们知道,在 Scala 中,上述表达式的最后一条语句(在 { .. } 内)实际上是返回到左侧。所以你最终将 even2 设置为“x => x % 2 == 0”函数,它与你为 even2 val 类型声明的类型匹配,即(Int => Boolean),所以编译器很高兴。现在 even2 只指向 "(x => x % 2 == 0)" 函数(不是之前的任何其他语句,即 println("val") 等。用不同的参数调用 event2 实际上会调用 "(x => x % 2 == 0)" 代码,因为只有它与 event2 一起保存。

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

只是为了更清楚地说明这一点,以下是不同版本的代码。

scala> val even2: (Int => Boolean) = {
     |              println("val");
     |              (x => { 
     |               println("inside final fn")
     |               x % 2 == 0
     |             })
     |        }

会发生什么 ?在这里,当您调用 even2() 时,我们会一次又一次地看到“inside final fn”。

scala> even2(3)
inside final fn
res9: Boolean = false

scala> even2(2)
inside final fn
res10: Boolean = true

scala> 
于 2018-01-14T04:21:55.193 回答
1

执行诸如def x = e不会计算表达式 e 的定义。相反,每当调用 x 时都会评估 e。

或者,Scala 提供了一个值定义 val x = e,它确实评估右侧作为定义评估的一部分。如果随后使用 x,它会立即被预先计算的 e 值替换,因此不需要再次计算表达式。

于 2018-01-26T13:08:39.887 回答
0

此外,Val 是按值评估。这意味着在定义期间评估右侧表达式。其中 Def 是按名称评估。在使用之前不会进行评估。

于 2017-09-20T09:18:57.810 回答
0

除了上述有用的回复,我的发现是:

def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int

def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int

def test3(): Int = 4
--test3: test3[]() => Int

上面显示“def”是一个方法(具有零参数参数),它在调用时返回另一个函数“Int => Int”。

这里很好地解释了方法到函数的转换:https ://tpolecat.github.io/2014/06/09/methods-functions.html

于 2018-07-11T08:15:13.000 回答
0

在 REPL 中,

scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean

scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8

def 均值call-by-name,按需评估

val 表示call-by-value,在初始化时评估

于 2019-01-11T23:36:34.980 回答
0

注意:Scala 中有不同类型的函数:抽象的、具体的、匿名的、高阶的、纯的、不纯的等...

解释 val 函数:

Scala 中的 val 函数是一个完整的对象。Scala 中有一些特征来表示具有不同数量参数的函数:Function0、Function1、Function2 等。作为实现这些特征之一的类的实例,函数对象具有方法。其中一种方法是 apply 方法,它包含实现函数体的代码。

当我们创建一个其值为函数对象的变量,然后我们引用该变量后跟括号时,这将转换为对函数对象的 apply 方法的调用。

解释方法即定义:

Scala 中的方法不是值,而是函数。

Scala 方法,就像在 Java 中一样,是类的一部分。它有一个名称、一个签名、可选的一些注释和一些字节码。

方法的实现是一个有序的语句序列,它产生一个必须与其返回类型兼容的值。

于 2021-02-16T11:49:00.840 回答