简体   繁体   English

定义函数的“def”和“val”有什么区别

[英]What is the difference between “def” and “val” to define a function

What is the difference between:有什么区别:

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

and

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

Both can be called like even(10) .两者都可以像even(10)一样被调用。

Method def even evaluates on call and creates new function every time (new instance of Function1 ).方法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

With def you can get new function on every call:使用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 evaluates when defined, def - when called: 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

Note that there is a third option: lazy val .请注意,还有第三种选择: lazy val

It evaluates when called the first time:它在第一次调用时进行评估:

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

scala> even
scala.NotImplementedError: an implementation is missing

But returns the same result (in this case same instance of FunctionN ) every time:但是每次都返回相同的结果(在这种情况下是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

Performance表现

val evaluates when defined. val在定义时进行评估。

def evaluates on every call, so performance could be worse than val for multiple calls. def对每次调用进行评估,因此对于多次调用,性能可能比val差。 You'll get the same performance with a single call.您只需一次调用即可获得相同的性能。 And with no calls you'll get no overhead from def , so you can define it even if you will not use it in some branches.没有调用,您将不会从def获得任何开销,因此即使您不会在某些分支中使用它,您也可以定义它。

With a lazy val you'll get a lazy evaluation: you can define it even if you will not use it in some branches, and it evaluates once or never, but you'll get a little overhead from double check locking on every access to your lazy val .使用lazy val您将获得惰性求值:即使您不会在某些分支中使用它,您也可以定义它,并且它会求值一次或从不求值,但是您会从每次访问的双重检查锁定中获得一些开销你的lazy val

As @SargeBorsch noted you could define method, and this is the fastest option:正如@SargeBorsch 指出的,您可以定义方法,这是最快的选择:

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

But if you need a function (not method) for function composition or for higher order functions (like filter(even) ) compiler will generate a function from your method every time you are using it as function, so performance could be slightly worse than with val .但是,如果您需要一个函数(不是方法)用于函数组合或高阶函数(如filter(even) ),则每次将其用作函数时,编译器都会从您的方法中生成一个函数,因此性能可能会比val

Consider this:考虑一下:

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

Do you see the difference?你看得到差别吗? In short:简而言之:

def : For every call to even , it calls the body of the even method again. def :每次调用even ,它都会再次调用even方法的主体。 But with even2 ie val , the function is initialized only once while declaration (and hence it prints val at line 4 and never again) and the same output is used each time it accessed.但是对于even2val ,函数在声明时只初始化一次(因此它在第 4 行打印val并且不再打印)并且每次访问时都使用相同的输出。 For example try doing this:例如尝试这样做:

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

When x is initialized, the value returned by Random.nextInt is set as the final value of x .x被初始化时, Random.nextInt返回的值被设置为x的最终值。 Next time x is used again, it will always return the same value.下次再次使用x时,它将始终返回相同的值。

You can also lazily initialize x .你也可以懒惰地初始化x ie first time it is used it is initialized and not while declaration.即第一次使用它被初始化而不是在声明时。 For example:例如:

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

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673

See this:看到这个:

  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)

Surprisingly, this will print 4 and not 9!令人惊讶的是,这将打印 4 而不是 9! val (even var) is evaluated immediately and assigned. val(甚至var)被立即评估并赋值。
Now change val to def.. it will print 9!现在将 val 更改为 def.. 它会打印 9! Def is a function call.. it will evaluate each time it is called. Def 是一个函数调用。它会在每次被调用时求值。

val ie "sq" is by Scala definition is fixed. val 即“sq”是由 Scala 定义固定的。 It is evaluated right at the time of declaration, you can't change later.它在声明时进行评估,以后无法更改。 In other examples, where even2 also val, but it declared with function signature ie "(Int => Boolean)", so it is not Int type.在其他例子中,even2 也是 val,但它用函数签名声明,即“(Int => Boolean)”,所以它不是 Int 类型。 It is a function and it's value is set by following expression它是一个函数,它的值由以下表达式设置

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

As per Scala val property, you can't assign another function to even2, same rule as sq.根据 Scala val 属性,您不能将另一个函数分配给 even2,与 sq 相同的规则。

About why calling eval2 val function not printing "val" again and again ?关于为什么调用 eval2 val 函数不会一次又一次地打印“val”?

Orig code:原文代码:

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

We know, in Scala last statement of above kind of expression (inside { .. }) is actually return to the left hand side.我们知道,在 Scala 中,上述类型表达式的最后一条语句(inside { .. })实际上是返回到左侧。 So you end up setting even2 to "x => x % 2 == 0" function, which matches with the type you declared for even2 val type ie (Int => Boolean), so compiler is happy.所以你最终将 even2 设置为 "x => x % 2 == 0" 函数,它与你为 even2 val 类型声明的类型相匹配,即 (Int => Boolean),所以编译器很高兴。 Now even2 only points to "(x => x % 2 == 0)" function (not any other statement before ie println("val") etc. Invoking event2 with different parameters will actually invoke "(x => x % 2 == 0)" code, as only that is saved with event2.现在 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

Just to clarify this more, following is different version of the code.为了更清楚地说明这一点,以下是不同版本的代码。

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

What will happen ?会发生什么 ? here we see "inside final fn" printed again and again, when you call even2().在这里,当您调用 even2() 时,我们会看到一次又一次地打印“inside final fn”。

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

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

scala> 

Executing a definition such as def x = e will not evaluate the expression e.执行诸如def x = e的定义不会对表达式 e 求值。 In- stead e is evaluated whenever x is invoked.而是在调用 x 时评估 e。

Alternatively, Scala offers a value definition val x = e ,which does evaluate the right-hand-side as part of the evaluation of the definition.或者,Scala 提供了一个值定义val x = e ,它确实将右侧作为定义评估的一部分进行评估。 If x is then used subsequently, it is immediately replaced by the pre-computed value of e, so that the expression need not be evaluated again.如果随后使用 x,它会立即被 e 的预先计算值替换,因此不需要再次计算表达式。

also, Val is a by value evaluation.此外,Val 是按值评估。 Which means the right-hand side expression is evaluated during definition.这意味着在定义期间评估右侧表达式。 Where Def is by name evaluation.其中 Def 是按名称评估。 It will not evaluate until it's used.在使用之前不会对其进行评估。

In addition to the above helpful replies, my findings are:除了上述有用的回复外,我的发现是:

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

The above shows that “def” is a method (with zero argument parameters) that returns another function "Int => Int” when invoked.上面显示“def”是一个方法(带有零个参数),它在调用时返回另一个函数“Int => Int”。

The conversion of methods to functions is well explained here: https://tpolecat.github.io/2014/06/09/methods-functions.html这里很好地解释了方法到函数的转换: https : //tpolecat.github.io/2014/06/09/methods-functions.html

In REPL,在 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 means call-by-name , evaluated on demand def 表示call-by-name ,按需评估

val means call-by-value , evaluated while initialization val 表示call-by-value ,在初始化时评估

Note: There are different types of functions in Scala: abstract, concrete, anonymous, high order, pure, impure etc...注意:Scala 中有不同类型的函数:抽象的、具体的、匿名的、高阶的、纯的、不纯的等等......

Explaining val function:解释val函数:

A val function in Scala is a complete object. Scala 中的 val 函数是一个完整的对象。 There are traits in Scala to represent functions with various numbers of arguments: Function0, Function1, Function2, etc. As an instance of a class that implements one of these traits, a function object has methods. Scala 中有一些特征来表示具有不同数量参数的函数:Function0、Function1、Function2 等。作为实现这些特征之一的类的实例,函数对象具有方法。 One of these methods is the apply method, which contains the code that implements the body of the function.其中一种方法是apply 方法,它包含实现函数体的代码。

When we create a variable whose value is a function object and we then reference that variable followed by parentheses, that gets converted into a call to the apply method of the function object.当我们创建一个值是函数对象的变量,然后我们引用该变量后跟括号时,这将转换为对函数对象的 apply 方法的调用。

Explaining Method ie def:解释方法即定义:

Methods in Scala are not values, but functions are. Scala 中的方法不是值,但函数是。

A Scala method, as in Java, is a part of a class. Scala 方法,就像在 Java 中一样,是类的一部分。 It has a name, a signature, optionally some annotations, and some bytecode.它有一个名称、一个签名、一些可选的注释和一些字节码。

The implementation of a method is an ordered sequence of statements that produces a value that must be compatible with its return type.方法的实现是一个有序的语句序列,它产生一个必须与其返回类型兼容的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM