当前位置: 首页 > Scala, 随记 > 正文

Scala语言备忘拾遗 – 1

Scala语言备忘拾遗 – 1

Scala 语法十分灵活,为了追求极致简洁,把各种符号玩的出神入化,一段时间不复习就会遗忘.
以下总结之以备忘.

1. 关于大括号(花括号)

1.1. 首先,大括号括住的是代码块

代码块有值,代码块的值是代码块中最后一个表达式的值.

println({
 val x = 1 + 1
 x + 1
}) // 3

1.2. call-by-name(传名调用)

传名调用用来设置函数处理传入参数时的处理方式.

正常情况下,函数F(x:Int)接收一个Int型的参数,但在调用F时可以传入一个函数调用B(参数列表),只要B的返回值是Int即可.
于是,调用F的写法可以为F(B(参数列表)),这里在F执行之前是需要首先计算下B(参数列表)的返回值,然后将返回值再传递给F,不管这个返回值在F内部被使用了多少次,返回值都是固定不变的,这种方式即是传值调用(call-by-value).

现在将函数F的定义稍作修改,变成F(x: => Int),注意在参数类型Int之前加上了 => 表示这个函数的使用参数的方式为传名调用(call-by-name).
假如现在传递的参数还是一个函数调用 x=B(参数列表), 那么在F函数体内,每次使用x时函数调用B(参数列表)都会被重新执行,产生最新鲜的返回值,然后再赋值给x使用.

和函数类型参数的区别

函数类型参数只是定义函数参数类型,而在函数参数类型之前加上 =>改变的是函数参数的处理方式,两者可以说是不相干的.

假如A(f: () => Int), 则函数A接受一个函数类型参数f,这个f不接受参数,返回一个Int,但,即使f不接受参数, ()也不能省略(如果省略,就表示接受一个Int参数的传名调用方式参数了),且()表示f不能接受任何参数(区别于传名调用时的函数调用可以是任意函数调用,只要返回值是Int).
同时,在A函数体内,f是函数引用,是可以用f()来调用的.

F(x: => Int)接受参数只能是Int,这个Int当然可以是其他函数调用的返回值,这个返回值不会在调用F之前计算,而是在F中用到时才(就)计算.
在F函数体内,不能进行x()这样的调用,因为x只是个Int.

再比如有M(g: (x:Int) => Int ),这个函数M接受的是函数类型的参数g,g接受一个Int返回一个Int,并且不是传名调用,
意味着,g如果是某个函数调用的返回值,则在调用M之前先计算这函数调用,返回g,只计算一次.

更变态一点
比如有N(h: => (x:Int) => Int ) 表示N接受一个函数类型的参数,函数类型签名为(x:Int) => Int ), N使用传名调用方式,意味着,如果
h是某个函数的调用,则在N函数体内每次用到h值时都会重新计算函数调用获得新的h.

一个例子, 参考了这个

package com.jack.yin.learn.concurrency.ch02

object TestCallByName {
  var money = 20
  val price = 1

  def drink(drinkTimes:Int): Unit = {
    money -= drinkTimes  * price
  }

  def count_money_left_after_drink_by_drink_time(drinkTimes:Int): Int = {
    drink(drinkTimes)
    money
  }

  def printByName(x: => Int): Unit = {
    for (i <- 0 until 5)
      println(x)
  }

  def printByValue(x: Int): Unit = {
    for (i <- 0 until 5)
      println(x)
  }

  def main(args: Array[String]) = {
    printByName(count_money_left_after_drink_by_drink_time(1))
    // 传名调用,传入函数调用,可以时任意函数的调用,带的参数也就可以是任意的, count_money_left_after_drink_by_drink_time(1) 在println(x)时被计算
    println("~~~~~~~~~~~~~~~~~~~~~~~")
    printByName(1) // 传名调用,传入常量,相当与常量表达式每次都会被求值,结果其实不变
    println("~~~~~~~~~~~~~~~~~~~~~~~")
    printByValue(count_money_left_after_drink_by_drink_time(1))
    // 传值调用, 传入函数调用时,先计算函数调用的返回值,count_money_left_after_drink_by_drink_time(1)只计算一次,在调用printByValue()之前计算
    println("~~~~~~~~~~~~~~~~~~~~~~~")
    printByValue(1) // 传值调用,传入常量,没啥特别的
  }

}
赞 赏

   微信赞赏  支付宝赞赏


本文固定链接: https://www.jack-yin.com/coding/essay/3150.html | 边城网事

该日志由 边城网事 于2020年04月09日发表在 Scala, 随记 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: Scala语言备忘拾遗 – 1 | 边城网事

Scala语言备忘拾遗 – 1:目前有1 条留言

发表评论

快捷键:Ctrl+Enter