Scala学习笔记3:函数

JerryXia 发表于 , 阅读 (24)

前言

一等公民!

0x01 值函数

仍然是普通的函数调用语法,唯一的区别是fun是一个包含函数的变量,而不是一个固定的函数。

这是值函数的调用形式:val valFunName = funName _

import scala.math._class Test {}object Test extends  App {  val num = 3.14  //在这里,ceil后面的_的意思是,将ceil方法转换成了函数,只是忘了传参数。  //在scala无法直接操纵方法,只能直接操纵函数。  //可以看出,这里仍然是普通的函数调用语法,唯一的区别是fun是一个包含函数的变量,而不是一个固定的函数  val fun = ceil _  //ceil函数的作用是求不小于给定实数的最小整数。  println(fun(num))}

0x02 嵌套函数

Scala允许在一个函数内部定义其他函数定义的函数,并可被局部函数调用。

  def sum3 (a : Int, b : Int , c : Int) : Int = {    def sum2 (a : Int, b : Int) : Int = {      a+b    }    sum2(a,b) + c  }  println(sum3(1,2,3))

ps:目前尚无用到的机会。

0x03 匿名函数

可以直接写函数体,而不用给函数命名。

(x : Int ) => x + 1

当然也可以赋值给val或者var

目前,个人理解,如上例,单纯的写一个匿名函数应该没什么用,下面这种直接将一个函数传入到另一个函数中的时候,会帮你省去了命名函数然后再调用的操作。

object OptionTest extends  App {  //map接受一个函数参数,将它应用到数组中的所有值,然后返回结果的数组  val a = Array(3.14, 1.42, 2.0) map {(x : Double) => 3*x}  for(i <- a)    println(i)}

当然也可以赋值给val或者var,不过这种行为,和使用def效果差不多,至少在表面上看起来其实区别不大。

  var triple1 = (x : Double) => 3 * x  def triple2(x : Double) = 3 * x  println(triple1(3))  println(triple2(3))

0x04 函数作为参数

fun(函数)

以一个快学scala上的例子为主。

注意,这里的参数可以是任何接受Double并返回Double的函数,valueAtOneQuarter函数将计算那个函数在0.25位置的值

valueAtOneQuarter是一个带有单个参数的函数,它的参数类型是((Double) => Double) => Double,也就是说它接受的参数是一个函数,输出的结果是一个Double值。

object FunctionTest extends App{  def valueAtOneQuarter (f: (Double) => Double) = f(0.25)  //ceil函数的作用是求不小于给定实数的最小整数。  println(valueAtOneQuarter(ceil _))  //sqrt是平方根函数  println(valueAtOneQuarter(sqrt _))}

高阶函数也可以产出另一个函数,具体如下:

 def mulBy(factor : Double) = (x : Double) => factor * x - 1 val quintuple = mulBy(5) println(quintuple(3)) 结果:14

mulBy函数有一个类型为Double的参数,返回一个类型为(Double) => Double的函数。因此整个mulBy函数的类型是(Double) => ((Double) => Double)

分析:

既然自己分析,那么就不再用scala的App运行了,自己写个Main可能更容易理解。

class FunctionTest{}object FunctionTest {  def mulBy(factor : Double) = (x : Double) => factor * x -1  def main(args : Array[String]) {    val quintuple = mulBy(5)	println(quintuple(3))  }}
D:\workspace\idea\test>scalac FunctionTest.scalaD:\workspace\idea\test>dir······2016/03/13  10:56             1,070 FunctionTest$$anonfun$mulBy$1.class2016/03/13  10:56             1,095 FunctionTest$.class2016/03/13  10:48             1,172 FunctionTest$delayedInit$body.class2016/03/13  10:56             1,077 FunctionTest.class2016/03/13  10:56               206 FunctionTest.scala

用自己写的main函数,就会发现比继承App的时候少了很多很多很多自己不知道东东。感兴趣的可以自己试一下。

太囧,本想通过class文件看一下scala实现高阶函数内部的原理是什么,毕竟还是知识储备不够哎,结果也没怎么看明白,下面的内容暂时作废,mark一下,自己以后回头再看。

update:

下面开始分析一下具体内容。

可以看出,FunctionTest.class应该就是从代码中的class FunctionTest编译而成的,FunctionTest$.class是object FunctionTest编译而成的,FunctionTest$$anonfun$mulBy$1.class是def mulBy编译而成的,FunctionTest$delayedInit$body.class暂时不太清楚它的含义。

FunctionTest.class和FunctionTest$.class暂时不关注,他们其实是类的一些信息。

FunctionTest$$anonfun$mulBy$1.class这个文件让我很迷惑,mulBy是一个函数,但是却生成了一个.class文件,是不是说在scala中的函数其实是类似java中类?或者是高阶的函数功能类似于java中的类?

D:\workspace\idea\test>javap -p FunctionTest.classCompiled from "FunctionTest.scala"public class FunctionTest {  public static void main(java.lang.String[]);  public static scala.Function1<java.lang.Object, java.lang.Object> mulBy(double);  public FunctionTest();}}
D:\workspace\idea\test>javap -p FunctionTest$.classCompiled from "FunctionTest.scala"public final class FunctionTest$ {  public static final FunctionTest$ MODULE$;  public static {};  public scala.Function1<java.lang.Object, java.lang.Object> mulBy(double);  public void main(java.lang.String[]);  private FunctionTest$();}}
D:\workspace\idea\test>javap -p FunctionTest$$anonfun$mulBy$1.classCompiled from "FunctionTest.scala"public final class FunctionTest$$anonfun$mulBy$1 extends scala.runtime.AbstractFunction1$mcDD$sp implements scala.Serializable {  public static final long serialVersionUID;  private final double factor$1;  public final double apply(double);  public double apply$mcDD$sp(double);  public final java.lang.Object apply(java.lang.Object);  public FunctionTest$$anonfun$mulBy$1(double);}
D:\workspace\idea\test>javap -p FunctionTest$delayedInit$body.classCompiled from "FunctionTest.scala"public final class FunctionTest$delayedInit$body extends scala.runtime.AbstractFunction0 {  private final FunctionTest$ $outer;  public final java.lang.Object apply();  public FunctionTest$delayedInit$body(FunctionTest$);}

通过上面的结果并没有看出来太多的东东,加一个-verbos瞅瞅。

在文件的最后有一句”InnerClasses: public static #16; //class FunctionTestanonfun$mulBy$1.class文件,会发现,它居然还有自己的apply方法……

D:\workspace\idea\test>javap -p -verbose FunctionTest$.classClassfile /D:/workspace/idea/test/FunctionTest$.class  Last modified 2016-3-13; size 1095 bytes  MD5 checksum 1c71889d90ec14e11f7b5c51b85aa701  Compiled from "FunctionTest.scala"public final class FunctionTest$  minor version: 0  major version: 50  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPERConstant pool:   #1 = Utf8               FunctionTest$   #2 = Class              #1             // FunctionTest$   #3 = Utf8               java/lang/Object   #4 = Class              #3             // java/lang/Object   #5 = Utf8               FunctionTest.scala   #6 = Utf8               MODULE$   #7 = Utf8               LFunctionTest$;   #8 = Utf8               <clinit>   #9 = Utf8               ()V  #10 = Utf8               <init>  #11 = NameAndType        #10:#9         // "<init>":()V  #12 = Methodref          #2.#11         // FunctionTest$."<init>":()V  #13 = Utf8               mulBy  #14 = Utf8               (D)Lscala/Function1;  #15 = Utf8               FunctionTest$$anonfun$mulBy$1  #16 = Class              #15            // FunctionTest$$anonfun$mulBy$1  #17 = Utf8               (D)V  #18 = NameAndType        #10:#17        // "<init>":(D)V  #19 = Methodref          #16.#18        // FunctionTest$$anonfun$mulBy$1."<init>":(D)V  #20 = Utf8               this  #21 = Utf8               factor  #22 = Utf8               D  #23 = Utf8               main  #24 = Utf8               ([Ljava/lang/String;)V  #25 = Double             5.0d  #27 = NameAndType        #13:#14        // mulBy:(D)Lscala/Function1;  #28 = Methodref          #2.#27         // FunctionTest$.mulBy:(D)Lscala/Function1;  #29 = Utf8               scala/Predef$  #30 = Class              #29            // scala/Predef$  #31 = Utf8               Lscala/Predef$;  #32 = NameAndType        #6:#31         // MODULE$:Lscala/Predef$;  #33 = Fieldref           #30.#32        // scala/Predef$.MODULE$:Lscala/Predef$;  #34 = Double             3.0d  #36 = Utf8               scala/Function1  #37 = Class              #36            // scala/Function1  #38 = Utf8               apply$mcDD$sp  #39 = Utf8               (D)D  #40 = NameAndType        #38:#39        // apply$mcDD$sp:(D)D  #41 = InterfaceMethodref #37.#40        // scala/Function1.apply$mcDD$sp:(D)D  #42 = Utf8               scala/runtime/BoxesRunTime  #43 = Class              #42            // scala/runtime/BoxesRunTime  #44 = Utf8               boxToDouble  #45 = Utf8               (D)Ljava/lang/Double;  #46 = NameAndType        #44:#45        // boxToDouble:(D)Ljava/lang/Double;  #47 = Methodref          #43.#46        // scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;  #48 = Utf8               println  #49 = Utf8               (Ljava/lang/Object;)V  #50 = NameAndType        #48:#49        // println:(Ljava/lang/Object;)V  #51 = Methodref          #30.#50        // scala/Predef$.println:(Ljava/lang/Object;)V  #52 = Utf8               args  #53 = Utf8               [Ljava/lang/String;  #54 = Utf8               quintuple  #55 = Utf8               Lscala/Function1;  #56 = Methodref          #4.#11         // java/lang/Object."<init>":()V  #57 = NameAndType        #6:#7          // MODULE$:LFunctionTest$;  #58 = Fieldref           #2.#57         // FunctionTest$.MODULE$:LFunctionTest$;  #59 = Utf8               Code  #60 = Utf8               LocalVariableTable  #61 = Utf8               LineNumberTable  #62 = Utf8               Signature  #63 = Utf8               (D)Lscala/Function1<Ljava/lang/Object;Ljava/lang/Object;>;  #64 = Utf8               SourceFile  #65 = Utf8               InnerClasses  #66 = Utf8               Scala{  public static final FunctionTest$ MODULE$;    descriptor: LFunctionTest$;    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL  public static {};    descriptor: ()V    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=1, locals=0, args_size=0         0: new           #2                  // class FunctionTest$         3: invokespecial #12                 // Method "<init>":()V         6: return  public scala.Function1<java.lang.Object, java.lang.Object> mulBy(double);    descriptor: (D)Lscala/Function1;    flags: ACC_PUBLIC    Code:      stack=4, locals=3, args_size=2         0: new           #16                 // class FunctionTest$$anonfun$mulBy$1         3: dup         4: dload_1         5: invokespecial #19                 // Method FunctionTest$$anonfun$mulBy$1."<init>":(D)V         8: areturn      LocalVariableTable:        Start  Length  Slot  Name   Signature            0       9     0  this   LFunctionTest$;            0       9     1 factor   D      LineNumberTable:        line 4: 0    Signature: #63                          // (D)Lscala/Function1<Ljava/lang/Object;Ljava/lang/Object;>;  public void main(java.lang.String[]);    descriptor: ([Ljava/lang/String;)V    flags: ACC_PUBLIC    Code:      stack=4, locals=3, args_size=2         0: aload_0         1: ldc2_w        #25                 // double 5.0d         4: invokevirtual #28                 // Method mulBy:(D)Lscala/Function1;         7: astore_2         8: getstatic     #33                 // Field scala/Predef$.MODULE$:Lscala/Predef$;        11: aload_2        12: ldc2_w        #34                 // double 3.0d        15: invokeinterface #41,  3           // InterfaceMethod scala/Function1.apply$mcDD$sp:(D)D        20: invokestatic  #47                 // Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;        23: invokevirtual #51                 // Method scala/Predef$.println:(Ljava/lang/Object;)V        26: return      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      27     0  this   LFunctionTest$;            0      27     1  args   [Ljava/lang/String;            8      18     2 quintuple   Lscala/Function1;      LineNumberTable:        line 6: 0        line 7: 8  private FunctionTest$();    descriptor: ()V    flags: ACC_PRIVATE    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: invokespecial #56                 // Method java/lang/Object."<init>":()V         4: aload_0         5: putstatic     #58                 // Field MODULE$:LFunctionTest$;         8: return      LocalVariableTable:        Start  Length  Slot  Name   Signature            0       9     0  this   LFunctionTest$;      LineNumberTable:        line 3: 0}SourceFile: "FunctionTest.scala"InnerClasses:     public static #16; //class FunctionTest$$anonfun$mulBy$1Error: unknown attribute  Scala: length = 0x0
D:\workspace\idea\test>javap -p -verbose FunctionTest$$anonfun$mulBy$1.classClassfile /D:/workspace/idea/test/FunctionTest$$anonfun$mulBy$1.class  Last modified 2016-3-13; size 1070 bytes  MD5 checksum 1d5bd1ea9a2779423e8ad63104c8f759  Compiled from "FunctionTest.scala"public final class FunctionTest$$anonfun$mulBy$1 extends scala.runtime.AbstractFunction1$mcDD$sp implements scala.Serializable  minor version: 0  major version: 50  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPERConstant pool:   #1 = Utf8               FunctionTest$$anonfun$mulBy$1   #2 = Class              #1             // FunctionTest$$anonfun$mulBy$1   #3 = Utf8               scala/runtime/AbstractFunction1$mcDD$sp   #4 = Class              #3             // scala/runtime/AbstractFunction1$mcDD$sp   #5 = Utf8               scala/Serializable   #6 = Class              #5             // scala/Serializable   #7 = Utf8               FunctionTest.scala   #8 = Utf8               FunctionTest$   #9 = Class              #8             // FunctionTest$  #10 = Utf8               mulBy  #11 = Utf8               (D)Lscala/Function1;  #12 = NameAndType        #10:#11        // mulBy:(D)Lscala/Function1;  #13 = Utf8               serialVersionUID  #14 = Utf8               J  #15 = Long               0l  #17 = Utf8               factor$1  #18 = Utf8               D  #19 = Utf8               apply  #20 = Utf8               (D)D  #21 = Utf8               apply$mcDD$sp  #22 = NameAndType        #21:#20        // apply$mcDD$sp:(D)D  #23 = Methodref          #2.#22         // FunctionTest$$anonfun$mulBy$1.apply$mcDD$sp:(D)D  #24 = Utf8               this  #25 = Utf8               LFunctionTest$$anonfun$mulBy$1;  #26 = Utf8               x  #27 = NameAndType        #17:#18        // factor$1:D  #28 = Fieldref           #2.#27         // FunctionTest$$anonfun$mulBy$1.factor$1:D  #29 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;  #30 = Utf8               scala/runtime/BoxesRunTime  #31 = Class              #30            // scala/runtime/BoxesRunTime  #32 = Utf8               unboxToDouble  #33 = Utf8               (Ljava/lang/Object;)D  #34 = NameAndType        #32:#33        // unboxToDouble:(Ljava/lang/Object;)D  #35 = Methodref          #31.#34        // scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D  #36 = NameAndType        #19:#20        // apply:(D)D  #37 = Methodref          #2.#36         // FunctionTest$$anonfun$mulBy$1.apply:(D)D  #38 = Utf8               boxToDouble  #39 = Utf8               (D)Ljava/lang/Double;  #40 = NameAndType        #38:#39        // boxToDouble:(D)Ljava/lang/Double;  #41 = Methodref          #31.#40        // scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;  #42 = Utf8               v1  #43 = Utf8               Ljava/lang/Object;  #44 = Utf8               <init>  #45 = Utf8               (D)V  #46 = Utf8               ()V  #47 = NameAndType        #44:#46        // "<init>":()V  #48 = Methodref          #4.#47         // scala/runtime/AbstractFunction1$mcDD$sp."<init>":()V  #49 = Utf8               ConstantValue  #50 = Utf8               Code  #51 = Utf8               LocalVariableTable  #52 = Utf8               LineNumberTable  #53 = Utf8               SourceFile  #54 = Utf8               EnclosingMethod  #55 = Utf8               InnerClasses  #56 = Utf8               Scala{  public static final long serialVersionUID;    descriptor: J    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL    ConstantValue: long 0l  private final double factor$1;    descriptor: D    flags: ACC_PRIVATE, ACC_FINAL  public final double apply(double);    descriptor: (D)D    flags: ACC_PUBLIC, ACC_FINAL    Code:      stack=3, locals=3, args_size=2         0: aload_0         1: dload_1         2: invokevirtual #23                 // Method apply$mcDD$sp:(D)D         5: dreturn      LocalVariableTable:        Start  Length  Slot  Name   Signature            0       6     0  this   LFunctionTest$$anonfun$mulBy$1;            0       6     1     x   D      LineNumberTable:        line 4: 0  public double apply$mcDD$sp(double);    descriptor: (D)D    flags: ACC_PUBLIC    Code:      stack=4, locals=3, args_size=2         0: aload_0         1: getfield      #28                 // Field factor$1:D         4: dload_1         5: dmul         6: iconst_1         7: i2d         8: dsub         9: dreturn      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      10     0  this   LFunctionTest$$anonfun$mulBy$1;            0      10     1     x   D      LineNumberTable:        line 4: 0  public final java.lang.Object apply(java.lang.Object);    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;    flags: ACC_PUBLIC, ACC_FINAL, ACC_BRIDGE, ACC_SYNTHETIC    Code:      stack=3, locals=2, args_size=2         0: aload_0         1: aload_1         2: invokestatic  #35                 // Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D         5: invokevirtual #37                 // Method apply:(D)D         8: invokestatic  #41                 // Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;        11: areturn      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      12     0  this   LFunctionTest$$anonfun$mulBy$1;            0      12     1    v1   Ljava/lang/Object;      LineNumberTable:        line 4: 0  public FunctionTest$$anonfun$mulBy$1(double);    descriptor: (D)V    flags: ACC_PUBLIC    Code:      stack=3, locals=3, args_size=2         0: aload_0         1: dload_1         2: putfield      #28                 // Field factor$1:D         5: aload_0         6: invokespecial #48                 // Method scala/runtime/AbstractFunction1$mcDD$sp."<init>":()V         9: return      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      10     0  this   LFunctionTest$$anonfun$mulBy$1;            0      10     1 factor$1   D      LineNumberTable:        line 4: 0}SourceFile: "FunctionTest.scala"EnclosingMethod: #9.#12                 // FunctionTest$.mulByInnerClasses:     public static #2; //class FunctionTest$$anonfun$mulBy$1Error: unknown attribute  Scala: length = 0x0

好吧,其实还是没有怎么深入地看。

0x05 闭包

闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。

话说,现在还不能理解什么是闭包。不过下面的代码貌似就可以理解为一个闭包。

def mulBy(factor : Double) = (x : Double) => factor * x -1

0x06 参数简化与类型推导

最后一种形式是最简单的,但是,这其实是一个逐渐简化的过程。

  • list.map((x : Int) => x + 1)
  • list.map((x) => x + 1)
  • list.map(x => x + 1)
  • list.map(_ + 1)
scala> val list = List(1,2,3,4,5)list: List[Int] = List(1, 2, 3, 4, 5)//最初的使用习惯scala> list.map((x : Int) => x+1)res0: List[Int] = List(2, 3, 4, 5, 6)//因为list中只有一种类型,因此就不用写Int了,让它自己推断scala> list.map((x) => x+1)res1: List[Int] = List(2, 3, 4, 5, 6)//再少一个括号scala> list.map(x => x+1)res2: List[Int] = List(2, 3, 4, 5, 6)//用_来代替所有scala> list.map(_ + 1)res3: List[Int] = List(2, 3, 4, 5, 6)

0x07 部分函数(partial function)

部分应用函数(Partial Applied Function)是缺少部分参数的函数,是一个逻辑上的概念。

下例,可以在输入值得时候先省略个别的参数,待以后处理。

scala> def add(x : Int, y : Int, z : Int) = x + y + zadd: (x: Int, y: Int, z: Int)Intscala> add(1,2,_)<console>:9: error: missing parameter type for expanded function ((x$1) => add(1, 2, x$1))              add(1,2,_)                      ^scala> add(1,2,_ : Integer)res5: Integer => Int = <function1>scala> val a = add(1,2,_ : Integer)a: Integer => Int = <function1>scala> a(3)res6: Int = 6

也可以将部分函数付给一个新的def

scala> def add(x : Int, y : Int, z : Int) = x + y + zadd: (x: Int, y: Int, z: Int)Intscala> def _add = add(_ : Int ,_ : Int , 0)_add: (Int, Int) => Intscala> _add(1,2)res7: Int = 3

0x08 柯里化 Currying

柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

scala> def add (a : Int, b : Int) = a + badd: (a: Int, b: Int)Int//把参数分开写scala> def add(a : Int)(b : Int) = a + badd: (a: Int)(b: Int)Intscala> add(1,2)<console>:12: error: too many arguments for method add: (a: Int)(b: Int)Int              add(1,2)                 ^scala> add(1)(2)res9: Int = 3//可以只写部分参数scala> add(1)_res10: Int => Int = <function1>scala> res10(2)res11: Int = 3

0x09 常用函数举例

scala> val l = List(1,2,3,4,5,6,7,8,9,10)l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)//对list每个元素执行操作scala> l.map( _ * 3)res12: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)//过滤scala> l.filter( _ > 4)res13: List[Int] = List(5, 6, 7, 8, 9, 10)//每两个元素进行相加scala> l.reduce(_ + _)res17: Int = 55//从左向右减scala> l.reduceLeft(_ - _)res18: Int = -53//从右向左减scala> l.reduceRight(_ - _)res19: Int = -5//可以给一个初始化值scala> l.fold(100)(_ + _)res20: Int = 155

2016-07-01 16:53:00

作者:dantezhao | 简书 | CSDN | GITHUB 文章推荐:http://dantezhao.com/readme 个人主页:http://dantezhao.com 文章可以转载, 但必须以超链接形式标明文章原始出处和作者信息