Scala学习笔记3:函数
前言
一等公民!
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)) 结果:14mulBy函数有一个类型为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 = 0x0D:\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 -10x06 参数简化与类型推导
最后一种形式是最简单的,但是,这其实是一个逐渐简化的过程。
- 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 = 30x08 柯里化 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 = 30x09 常用函数举例
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 = 1552016-07-01 16:53:00
作者:dantezhao | 简书 | CSDN | GITHUB 文章推荐:http://dantezhao.com/readme 个人主页:http://dantezhao.com 文章可以转载, 但必须以超链接形式标明文章原始出处和作者信息