Scala学习笔记02_函数入门

作者: padluo | 来源:发表于2018-03-24 16:22 被阅读24次

    函数入门

    函数的定义与调用,在Scala中定义函数时,需要定义函数的函数名、参数、函数体。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sayHello(name:String, age:Int) = {
      if(age >= 18) {
        printf("Hi, %s, you are a big boy!\n", name)
        age
      } else {
          printf("Hi, %s, you are a children!\n", name)
          age
        }
    }
    
    // Exiting paste mode, now interpreting.
    
    sayHello: (name: String, age: Int)Int
    
    scala> sayHello("padluo", 30)
    Hi, padluo, you are a big boy!
    res1: Int = 30
    

    Scala要求必须给出所有参数的类型,但是不一定给出函数返回值的类型,只要右侧的函数体不包含递归的语句,Scala就可以自己根据右侧的表达式推断出返回类型。

    在代码块中定义包含多行语句的函数体

    单行的函数,

    scala> def sayHello(name:String) = printf("Hello, " + name)
    sayHello: (name: String)Unit
    
    scala> sayHello("padluo")
    Hello, padluo
    

    如果函数体中有多行代码,则可以用代码块的方式包裹多行代码,代码块中最后一行的返回值就是整个函数的返回值,与Java中不同,不是使用return返回值的。

    # 函数定义没有写=
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sum(n:Int) {
      var result = 0
      for(i <- 1 to n) {
        result += i
      }
    }
    
    // Exiting paste mode, now interpreting.
    
    sum: (n: Int)Unit
    
    # 函数定义写了=,但是最后一行是赋值语句
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sum(n:Int) = {
      var result = 0
      for(i <- 1 to n) {
        result += i
      }
    }
    
    // Exiting paste mode, now interpreting.
    
    sum: (n: Int)Unit
    
    # 正确定义
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sum(n:Int) = {
      var result = 0
      for(i <- 1 to n) {
        result += i
      }
      result
    }
    
    // Exiting paste mode, now interpreting.
    
    sum: (n: Int)Int
    

    递归函数与返回类型,如果在函数体内递归调用函数自身,则必须手动给出函数的返回类型。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def fab(n:Int):Int = {
      if(n<=0) 1
      else fab(n-1) + fab(n-2)
    }
    
    // Exiting paste mode, now interpreting.
    
    fab: (n: Int)Int
    
    scala> fab(10)
    res5: Int = 144
    

    默认参数和带名参数

    默认参数,在Scala中,有时我们调用某些函数时,不希望给出具体的参数值,而希望使用参数自身默认的值,此时就在定义函数时使用默认参数。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sayHello(name:String, age:Int=20) {
      print("Hello, " + name + ", your age is " + age)
    }
    
    // Exiting paste mode, now interpreting.
    
    sayHello: (name: String, age: Int)Unit
    
    scala> sayHello("leo")
    Hello, leo, your age is 20
    scala> sayHello("leo", 30)
    Hello, leo, your age is 30
    

    如果给出的参数不够,则会从左到右依次应用参数。

    带名参数,在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。

    scala> sayHello(age=30, name="leo")
    Hello, leo, your age is 30
    

    还可以混合使用未命名参数和带名参数,但是未命名参数必须排在带名参数前面。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sayHello(firstName:String, middleName:String="Willian",lastName:String="Croft") = print(firstName + " " + middleName + " " + lastName)
    
    // Exiting paste mode, now interpreting.
    
    sayHello: (firstName: String, middleName: String, lastName: String)Unit
    
    scala> sayHello("leo")
    leo Willian Croft
    scala> sayHello("leo", lastName="Jack", middleName="Tom")
    leo Tom Jack
    

    变长参数

    如果函数体包含在花括号中,但没有前面的=号,那么返回类型就是Unit,这样的函数被称为过程,过程不返回值,我们调用它仅仅是为了它的副作用,有人不喜欢这种简明写法定义过程,并建议大家总是显式声明Unit返回类型。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sum(nums:Int*) {
      var result = 0
      for(num <- nums) {
        result += num
      }
      result
    }
    
    // Exiting paste mode, now interpreting.
    
    sum: (nums: Int*)Unit
    
    scala> sum(1,2,3,4,5)
    
    scala>
    

    在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sum(nums:Int*) = {
      var result = 0
      for(num <- nums) {
        result += num
      }
      result
    }
    
    // Exiting paste mode, now interpreting.
    
    sum: (nums: Int*)Int
    
    scala> sum(1,2,3,4,5)
    res0: Int = 15
    

    使用序列调用变长参数,如果想要将一个已有的序列直接调用变长参数函数,是不对的,如val s = sum(1 to 5)。此时需要使用Scala特殊的语法将参数定义为序列sum(1 to 5: _*),让Scala解释器能够识别。

    scala> 1 to 5
    res6: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
    
    scala> sum(1 to 5)
    <console>:13: error: type mismatch;
     found   : scala.collection.immutable.Range.Inclusive
     required: Int
           sum(1 to 5)
    
    scala> sum(1 to 5:_*)
    res3: Int = 15
    
    scala> sum(1 to 5: _*)
    res4: Int = 15
    
    scala> sum(1 to 5 : _*)
    res5: Int = 15
    

    使用递归函数实现累加,

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    def sum2(nums:Int*): Int = {
      if (nums.length == 0) 0
      else nums.head + sum2(nums.tail: _*)
    }
    
    // Exiting paste mode, now interpreting.
    
    sum2: (nums: Int*)Int
    
    scala> sum(1,2,3,4,5)
    res8: Int = 15
    

    过程、lazy值和异常

    过程,在Scala中,定义函数时,如果函数体直接包裹在了花括号里面,而没有使用=连接,则函数的返回值类型就是Unit。这样的函数称为过程。过程通常用于不需要返回值的函数。

    过程还有一种写法,就是将函数的返回值类型定义为Unit。

    # 函数,没有显式声明返回值类型,自动推断返回值类型
    scala> def sayHello(name:String) = "Hello, " + name
    sayHello: (name: String)String
    
    scala> sayHello("padluo")
    res1: String = Hello, padluo
    
    # 过程,没有使用=连接,调用它仅仅是为了它的副作用(print ...),即使块最后的表达式有值,整个函数最终是没有值返回的
    scala> def sayHello(name:String) {print("Hello, " + name); "Hello, " + name}
    sayHello: (name: String)Unit
    
    scala> sayHello("padluo")
    Hello, padluo
    
    # 显式声明返回值类型为Unit,即使块最后的表达式有值,函数最终也是没有值返回
    scala> def sayHello(name:String): Unit = "Hello," + name
    sayHello: (name: String)Unit
    
    scala> sayHello("padluo")
    
    scala>
    

    lazy值,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算,这种特性对于特别耗时的操作特别有用,比如打开文件进行IO,进行网络IO等。

    scala> import scala.io.Source._
    import scala.io.Source._
    
    scala> lazy val lines = fromFile("/home/hadoop/test.txt").mkString
    lines: String = <lazy>
    
    scala> print(lines)
    Hello World
    
    scala> lazy val lines = fromFile("/home/hadoop/test1.txt").mkString
    lines: String = <lazy>
    
    scala> print(lines)
    java.io.FileNotFoundException: /home/hadoop/test1.txt (No such file or directory)
      at java.io.FileInputStream.open(Native Method)
      at java.io.FileInputStream.<init>(FileInputStream.java:146)
      at scala.io.Source$.fromFile(Source.scala:91)
      at scala.io.Source$.fromFile(Source.scala:76)
      at scala.io.Source$.fromFile(Source.scala:54)
      at .lines$lzycompute(<console>:14)
      at .lines(<console>:14)
      ... 32 elided
      
    scala> val lines = fromFile("/home/hadoop/test1.txt").mkString
    java.io.FileNotFoundException: /home/hadoop/test1.txt (No such file or directory)
      at java.io.FileInputStream.open(Native Method)
      at java.io.FileInputStream.<init>(FileInputStream.java:146)
      at scala.io.Source$.fromFile(Source.scala:91)
      at scala.io.Source$.fromFile(Source.scala:76)
      at scala.io.Source$.fromFile(Source.scala:54)
      ... 32 elided
    
    scala> def getLines = fromFile("/home/hadoop/test1.txt").mkString
    getLines: String
    
    scala> getLines
    java.io.FileNotFoundException: /home/hadoop/test1.txt (No such file or directory)
      at java.io.FileInputStream.open(Native Method)
      at java.io.FileInputStream.<init>(FileInputStream.java:146)
      at scala.io.Source$.fromFile(Source.scala:91)
      at scala.io.Source$.fromFile(Source.scala:76)
      at scala.io.Source$.fromFile(Source.scala:54)
      at .getLines(<console>:14)
      ... 32 elided
    

    val lines = fromFile("/home/hadoop/test1.txt").mkString,即使文件不存在也不会报错,只有第一个使用变量时会报错,证明了表达式计算的lazy特性。

    异常

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    try {
      throw new IllegalArgumentException("illegal argument!")
    } catch {
      case _: IllegalArgumentException => print("sorry, error!")
    } finally {
      print("\nrelease io resources!")
    }
    
    // Exiting paste mode, now interpreting.
    
    sorry, error!
    release io resources!
    
    scala> import java.io._
    import java.io._
    
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    try {
      throw new IOException("user defined exception")
    } catch {
      case e1: IllegalArgumentException => println("illegal argument")
      case e2: IOException => print("io exception")
    }
    
    // Exiting paste mode, now interpreting.
    
    io exception
    

    本文首发于steem,感谢阅读,转载请注明。

    https://steemit.com/@padluo


    微信公众号「数据分析」,分享数据科学家的自我修养,既然遇见,不如一起成长。

    数据分析

    读者交流电报群

    https://t.me/sspadluo


    知识星球交流群

    知识星球读者交流群

    相关文章

      网友评论

        本文标题:Scala学习笔记02_函数入门

        本文链接:https://www.haomeiwen.com/subject/lvgqcftx.html