Scala学习笔记(四)(函数相关)

Scala函数问题:

函数是一组一起执行任务的语句。可以将代码放到独立的功能。如何划分你的代码不同功能?在逻辑上,通常是让每个函数执行特定的任务。


Scala有函数和方法,我们术语说的方法和函数互换用微小的差别。Scala方法是其中有一个名字,签名,任选一些注释,有的字节码,

其中如在Scala中函数是可被分配给一个变量的完整对象类的一部分。换句话说,函数,其被定义为某些对象的一个成员,被称为方法。


函数定义可以出现在在源文件的任何地方,Scala允许嵌套函数的定义,那就是其他函数定义的内部函数定义。需要注意的最重要的一点是,

Scala的函数名称可以类似+, ++, ~, &,-, -- , , /, : 等字符。


函数定义:


Scala函数定义有如下形式:


def functionName ([list of parameters]) : [return type] = {
   function body
   return [expr]
}

在这里,返回类型可以是任何有效的scala数据类型,参数列表将是用逗号和参数,返回值类型列表分离变量是可选的。非常类似于Java,一个返回语句可以在函数表达式可用情况下返回一个值。以下是这将增加两个整数并返回的函数:


object add{
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}

函数,它不返回任何东西,可以返回这相当于在Java中void,并表示该函数不返回任何单元。Scala中不返回任何东西函数被称为过程。以下是语法


object Hello{
   def printMe( ) : Unit = {
      println("Hello, Scala!")
   }
}

调用函数:


Scala提供了一些语法的变化来调用方法。以下是调用一个方法的标准方法:


如果函数被对象的一个实例调用使用,那么使用类似于Java点标记如下:


[instance.]functionName( list of parameters )

下面是一个例子用来定义,然后调用函数:


 

 

object Test {
   def main(args: Array[String]) {
        println( "Returned Value : " + addInt(5,7) );
   }
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}

让我们编译和运行上面的程序,这将产生以下结果:


C:/>scalac Test.scala
C:/>scala Test
Returned Value : 12

C:/>

Scala函数是Scala编程的核心,这就是为什么Scala被假定为一个函数式编程语言.


Scala函数按名称调用

通常情况下,函数的参数是传值参数;即,参数的值在它被传递给函数之前被确定。但是,如果我们需要编写一个接收参数不希望马上计算,直到调用函数内的表达式。对于这种情况,Scala提供按名称参数调用函数。

按名称调用机制传递一个代码块给被调用者并且每次被调用方传接入参数,代码块被执行,值被计算。

object Test {
   def main(args: Array[String]) {
        delayed(time());
   }

   def time() = {
      println("Getting time in nano seconds")
      System.nanoTime
   }
   def delayed( t: => Long ) = {
      println("In delayed method")
      println("Param: " + t)
      t
   }
}

在这里,我们声明delayed方法,它通过=>符号变量的名称和类型,需要一个按名称调用参数。当上述代码被编译和执行时,它产生了以下结果:


C:/>scalac Test.scala
C:/>scala Test
In delayed method
Getting time in nano seconds
Param: 81303808765843
Getting time in nano seconds
这里,delayed打印的消息声明,该方法已被输入。接下来,delayed打印一个与其消息的值。最后delayed方法返回 t。

Scala函数使用命名参数

在一个正常的函数调用,调用参数在调用函数的参数的顺序一一匹配。命名参数允许将参数以不同的顺序传递给的函数。语法很简单,每个参数前面有一个参数名和等号。下面是一个简单的例子来说明这一概念:

object Test {
   def main(args: Array[String]) {
        printInt(b=5, a=7);
   }
   def printInt( a:Int, b:Int ) = {
      println("Value of a : " + a );
      println("Value of b : " + b );
   }
}

当上述代码被编译和执行时,它产生了以下结果:


C:/>scalac Test.scala
C:/>scala Test
Value of a :  7
Value of b :  5

 

Scala函数使用可变参数


Scala允许指出的最后一个参数的函数可以被重复。能够通过可变长度参数列表传递到函数。下面是一个简单的例子来说明这个概念。


object Test {
   def main(args: Array[String]) {
        printStrings("Hello", "Scala", "Python");
   }
   def printStrings( args:String* ) = {
      var i : Int = 0;
      for( arg <- args ){
         println("Arg value[" + i + "] = " + arg );
         i = i + 1;
      }
   }
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
Arg value[0] = Hello
Arg value[1] = Scala
Arg value[2] = Python

 在这里,printStrings函数,这被声明为类型为“String*”里的args类型实际上是数组[字符串]。


Scala函数默认参数值


Scala可以指定默认值函数的参数。对于这样的一个参数,可以任选地从一个函数调用,在这种情况下对应的参数将被填充使用默认参数值。下面是指定默认参数的一个例子:


object Test {
   def main(args: Array[String]) {
        println( "Returned Value : " + addInt() );
   }
   def addInt( a:Int=5, b:Int=7 ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}

当上述代码被编译和执行时,它产生了以下结果:


C:/>scalac Test.scala
C:/>scala Test
Returned Value : 12
 

Scala高阶函数

Scala允许高阶函数的定义。这些都是采取其他函数参数,或它的结果是一个功能的函数。例如在下面的代码,适用于apply()函数将另一个函数f和v值并应用函数f到v:

object Test {
   def main(args: Array[String]) {

      println( apply( layout, 10) )

   }

   def apply(f: Int => String, v: Int) = f(v)

   def layout[A](x: A) = "[" + x.toString() + "]"
   
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
[10]

 

Scala匿名函数

Scala中提供相对轻便的语法定义匿名函数。在源代码中的匿名函数被调用函数文本,并在运行时,函数文本被实例化为调用的函数值的对象。

Scala支持一流的功能,这意味着可以表达功能文本语法功能,即,(x: Int) => x + 1,函数都可以通过对象,这是所谓的函数值来表示。下面的表达式创建一个后继函数的整数:

var inc = (x:Int) => x+1

变量inc现在可以使用以通常的方式的函数:

var x = inc(7)-1

另外,也可以用多个参数定义的函数如下:

var mul = (x: Int, y: Int) => x*y

变量mul现在可以使用以通常的方式的函数:

println(mul(3, 4))

另外,也可以用无参数定义函数如下:

var userDir = () => { System.getProperty("user.dir") }

变量userDir现在可以使用以通常的方式的函数:

println( userDir )

Scala柯里函数

柯里转换函数接受多个参数成一条链的函数,每次取一个参数。柯里函数是具有多个参数列表定义,如下:

def strcat(s1: String)(s2: String) = s1 + s2

另外,还可以使用以下语法定义柯里函数:

def strcat(s1: String) = (s2: String) => s1 + s2

以下是语法来调用一个柯里函数:

strcat("foo")("bar")

可以根据柯里函数需求定义两个以上的参数。让我们以一个完整的例子来说明柯里的概念:

object Test {
   def main(args: Array[String]) {
      val str1:String = "Hello, "
      val str2:String = "Scala!"
      println( "str1 + str2 = " +  strcat(str1)(str2) )
   }

   def strcat(s1: String)(s2: String) = {
      s1 + s2
   }
}

当上述代码被编译和执行时,它产生了以下结果:
C:/>scalac Test.scala
C:/>scala Test
str1 + str2 = Hello, Scala!

Scala嵌套函数

Scala允许在一个函数内部定义其他函数定义的函数,并可被局部函数调用。这里是一个阶乘计算器,在这里使用调用第二个函数,嵌套方式是传统技术的一个实现:

object Test {
   def main(args: Array[String]) {
      println( factorial(0) )
      println( factorial(1) )
      println( factorial(2) )
      println( factorial(3) )
   }

   def factorial(i: Int): Int = {
      def fact(i: Int, accumulator: Int): Int = {
         if (i <= 1)
            accumulator
         else
            fact(i - 1, i * accumulator)
      }
      fact(i, 1)
   }
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
1
1
2
6

C:/>

像在许多语言中的局部变量声明,嵌套方法只封闭方法内可见。如果试图调用 fact() 在factorial()之外,将会得到一个编译器错误。

Scala部分应用函数

当调用函数,可以认为是将参数传给函数。通过所有希望的参数并完全把它传到应用。如果只发送了几个参数,会得到一个部分函数应用。也可以绑定一些参数,剩下其余的稍后再填补上。下面是一个简单的例子来说明这一概念:

import java.util.Date

object Test {
   def main(args: Array[String]) {
      val date = new Date
      log(date, "message1" )
      Thread.sleep(1000)
      log(date, "message2" )
      Thread.sleep(1000)
      log(date, "message3" )
   }

   def log(date: Date, message: String)  = {
     println(date + "----" + message)
   }
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
Mon Dec 02 12:52:41 CST 2013----message1
Mon Dec 02 12:52:41 CST 2013----message2
Mon Dec 02 12:52:41 CST 2013----message3

C:/>

在这里,log()方法有两个参数:date和message。如果想多次调用这个方法,如:date 使用不同的值,而 message 的值相同。我们可以消除部分参数传递给 log() 方法,因为传递 date 在每个调用都可能会有干扰。要做到这一点,我们首先绑定一个值到 date 参数,并把下划线放在其位置第二个参数之后。现在我们已经存储一个变量的部分应用到函数。可以与未结合的 mesage 参数调用这个新方法如下:

import java.util.Date

object Test {
   def main(args: Array[String]) {
      val date = new Date
      val logWithDateBound = log(date, _ : String)

      logWithDateBound("message1" )
      Thread.sleep(1000)
      logWithDateBound("message2" )
      Thread.sleep(1000)
      logWithDateBound("message3" )
   }

   def log(date: Date, message: String)  = {
     println(date + "----" + message)
   }
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
Mon Dec 02 12:53:56 CST 2013----message1
Mon Dec 02 12:53:56 CST 2013----message2
Mon Dec 02 12:53:56 CST 2013----message3

C:/>

Scala闭包

闭包是函数,它的返回值取决于此函数之外声明一个或多个变量的值。考虑下面的一块使用匿名函数的代码:

val multiplier = (i:Int) => i * 10

在这里,在函数体中使用的唯一变量, i * 0, 为i,其被定义为一个参数的函数。现在,让我们来看另一块代码:

val multiplier = (i:Int) => i * factor

有两个自由变量的乘数:i和factor。其中一个i是一个正式函数参数。因此,它被绑定到一个新的值在乘数每次调用。然而,factor不是一个正式的参数,那么这是什么?让我们增加一行代码:

var factor = 3
val multiplier = (i:Int) => i * factor

现在,factor具有参考变量在函数之外,但为封闭范围的变量。让我们试试下面的例子:

object Test {
   def main(args: Array[String]) {
      println( "muliplier(1) value = " +  multiplier(1) )
      println( "muliplier(2) value = " +  multiplier(2) )
   }
   var factor = 3
   val multiplier = (i:Int) => i * factor
}
让我们编译和运行上面的程序,这将产生以下结果:
C:/>scalac Test.scala
C:/>scala Test
muliplier(1) value = 3
muliplier(2) value = 6
上面的函数引用factor并读取每个时间的当前值。如果函数没有外部引用,那么它就是封闭了自己。无需外部环境是必需的。
 
posted @ 2017-06-05 15:57  dade丶  阅读(443)  评论(3编辑  收藏  举报