by name parameter & _的用法

关于byname byvalue何时计算的问题

A by-name parameter acts like a def.

Scala has a solution to this problem called by-name parameters. By declaring a parameter as a: => A (note that the space after the : is necessary) we are telling Scala to evaluate a only when it is used (which may be never). So let’s fix our method.

 

def when[A](test: Boolean, whenTrue: A, whenFalse: A): A = 
  test match {
    case true  => whenTrue
    case false => whenFalse
  }

scala> when(1 == 2, "foo", "bar")
res13: String = bar

scala> when(1 == 1, "foo", "bar")
res14: String = foo

// Ok so far, but...

scala> when(1 == 1, println("foo"), println("bar"))
foo
bar

def when[A](test: Boolean, whenTrue: => A, whenFalse: => A): A = 
  test match {
    case true  => whenTrue
    case false => whenFalse
  }

// Try that again...

scala> when(1 == 1, println("foo"), println("bar"))
foo

scala> when(1 == 2, println("foo"), println("bar"))
bar

When we talk about laziness we mean that an expression is reduced by need, as is the case with lazy val in Scala.

val a = { println("computing a"); util.Random.nextInt(10) }
def a = { println("computing a"); util.Random.nextInt(10) }
List(a, a, a, a, a)//及时计算

def five[A](a: => A): List[A] = List(a, a, a, a, a)
five { println("computing a"); util.Random.nextInt(10) }//by name 也及时计算

lazy val a = { println("computing a"); util.Random.nextInt(10) }
def five[A](a: => A): List[A] = { lazy val b = a; List(b, b, b, b, b) }
five { println("computing a"); util.Random.nextInt(10) }
//这里lazy后不计算
//lazy val is computed once like any other val, but not until its value is needed.

 

So although we used by-name parameters to implement lazy behavior for our when function above, this is dependent on only referencing each parameter once.

A parameter is strict if its argument is always evaluated, regardless of calling convention; in our five function above our parameter is both by-name and strict.

  reference a by-name parameter without forcing its evaluation:can apply η-expansion (put an underscore after the identifier) to turn it into a function value.

 

所以说,when中实现了lazy是因为对每个parameter只引用一次。

若需要引用by-name parameter,但又不想计算,则可以在identifier后加上_转为function value。_作用是we construct a Function1 instance that delegates to our method. (val f = add1 _)

scala> def foo(a: => Unit): () => Unit = a _
foo: (a: => Unit)() => Unit

scala> val z = foo(println("hi"))
z: () => Unit = <function0>

scala> z()
hi

 

 

以下几个情景使用 _ :

函数的类的实例赋给val(不用_则需要声明类型信息, 调用新名称时需要()),map以函数作参对每一个元素操作,

多个参数时:如果有默认arguments则不能partial application

 Values in scala cannot have type parameters; when η-expanding a parameterized method all type arguments must be specified (or they will be inferred as non-useful types): 即所有类型参数必须指明,否则为无用类型

val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }

scala> List(1,2,3).map(add1 _)
res5: List[Int] = List(2, 3, 4)

scala> List(1,2,3).map(add1)
res6: List[Int] = List(2, 3, 4)


scala> val z = add1//需要赋予类型信息
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
       val z = add1
               ^

scala> val z: Int => Int = add1
z: Int => Int = <function1>

scala> val z = add1 : Int => Int
z: Int => Int = <function1>

scala> "foo".substring _
<console>:8: error: ambiguous reference to overloaded definition,
both method substring in class String of type (x$1: Int, x$2: Int)String
and  method substring in class String of type (x$1: Int)String
match expected type ?
              "foo".substring _
                    ^

scala> "foo".substring _ : (Int => String)
res14: Int => String = <function1>

scala> def x = println("hi")
x: Unit

scala> val z = x // ok
hi
z: Unit = ()

scala> val z = x _
z: () => Unit = <function0>

scala> z()
hi
----------- 
scala> def plus(a: Int, b: Int): Int = a + b
plus: (a: Int, b: Int)Int

scala> plus _
res8: (Int, Int) => Int = <function2>


scala> def plus(a: Int)(b: Int): Int = a + b
plus: (a: Int)(b: Int)Int

scala> plus _
res11: Int => (Int => Int) = <function1>
 
scala> plus(1)
<console>:9: error: missing arguments for method plus;
follow this method with `_' if you want to treat it as a partially applied function
              plus(1)
                  ^

scala> plus(1) _
res13: Int => Int = <function1>

scala> val x = plus _
x: Int => (Int => Int) = <function1>

scala> x(1) // no underscore needed
res0: Int => Int = <function1>

-------------#implicit members
scala> def foo[N:Numeric](n:N):N = n
foo: [N](n: N)(implicit evidence$1: Numeric[N])N

scala> foo[String] _
<console>:9: error: could not find implicit value for evidence parameter of type Numeric[String]
              foo[String] _
                 ^

scala> foo[Int] _
res3: Int => Int = <function1>

scala> def bar[N](n:N)(implicit ev: Numeric[N]):N = n
bar: [N](n: N)(implicit ev: Numeric[N])N

scala> bar[Int] _
res4: Int => Int = <function1>
-------------
scala> def foo(n: Int = 3, s: String) = s * n
foo: (n: Int, s: String)String

scala> foo _
res19: (Int, String) => String = <function2>

scala> foo(42) _
<console>:9: error: not enough arguments for method foo: (n: Int, s: String)String.
Unspecified value parameter s.
              foo(42) _
 

 

posted on 2017-10-07 16:17  satyrs  阅读(353)  评论(0编辑  收藏  举报

导航