Scala 随笔

Posted by danner on March 9, 2018

https://docs.scala-lang.org/zh-cn/tour/higher-order-functions.html

case class

case class 样例类,必须要有参数列表case object 样例对象,必须不能加参数列表。案例类非常适合用于不可变的数据,Spark 中常用 case class 来存储信息。case class 相比于 class 有什么不同(以下这些都是编译器实现):

  • case class 重写了 toStringequalshashCode
  • case class 默认就实现了序列化
  • case class 不用 new,有一个默认的 apply 方法来负责对象的创建
  • case classcopy 内部实现是 new 对象


object CaseClassApp {
	def main(args: Array[String]): Unit = {
	    println(Dog("john").name)
	}
	
	case class Dog(name:String)
}

case class 按值比较

object CaseClassApp {
	def main(args: Array[String]): Unit = {
	    val dog1 = Dog("john")
	    val dog2 = Dog("john")
	    println(dog1 == dog2) // true
	}
	
	case class Dog(name:String)
}

柯里化

一个接受多个参数的函数分解成一系列的函数,每个函数只接受一个参数,这个过程叫做柯里化。

currying 最大的意义在于把多个参数的函数等价转化成多个单参数函数的级联,这样所有的函数就都统一了,方便做 lambda 演算。

object Currying {
	def main(args: Array[String]): Unit = {
	
	    // 参数必须分开写
	    def sum(a:Int)(b:Int) = a + b
	
	    // 柯里化产生新的函数 sum9
	    // 此函数功能:形参 + 9
	    val sum9 = sum(9)_  //还有参数必须加 "_"
	    
	    println(sum9(5))    // 14
	    println(sum9(10))   // 19
	}
}

下面来看看 currying 的演化,省得下次看到不认识

// 以下四个函数的作用都是两者之和
scala> def sum(x:Int,y:Int) = x + y
sum: (x: Int, y: Int)Int

scala> def sum = (x:Int,y:Int) => x + y
sum: (Int, Int) => Int

scala> def sum(x:Int)(y:Int) = x + y
sum: (x: Int)(y: Int)Int

scala> def sum = (x:Int) => (y:Int) => x + y
sum: Int => (Int => Int)

偏函数

PartialFunction:包在花括号内没有 matchcase 语句;重点理解

偏函数接收一个类型为A的参数,返回类型为B的值

object PartialFunctionApp {
    def main(args: Array[String]): Unit = {

        val names = Array("li","wang","zhang")
        val name = names(new Random().nextInt(names.length))

        def say:PartialFunction[String,String] = {
            case "li" => "name is  li"
            case "wang" => "name is wang"
            case "zhang" => "name is zhang"
            case _ => "no"
        }
        println(say(name))
    }
}

隐式转换

增强现有代码:悄无声息

隐式参数

可以提供相同的默认值

def say(implicit word:String) = println(s"hello $word")

implicit val word = "danner"
// 输出 hello danner
say

当方法/函数的参数用 implicit 修饰时,会寻找上下文中用implicit 修饰的相同类型的变量。在本例中,say的隐式参数是String没有带参数,那么此时会寻找类型为 String且被implicit修饰的变量直接代入 say 函数。

隐式类型转换

为已存在的类添加新方法

object ImplicitApp {
    def main(args: Array[String]): Unit = {   
   // 为现有 Man 类添加 fly 函数
    implicit def man2superman(man: Man) = new Superman(man.name)
    val man = new Man("danner")
    man.fly()

    implicit def str2Int(string: String):Int = new StringOps(string).toInt
    // 无声息地将字符串转化为 Int
    println(math.max("2",3))

	}
}
class Man(val name:String)

class Superman(val name:String) {
    def fly(): Unit = {
        println(s"$name fly ...")
    }
}

隐式类

实现与隐式类型转换类似功能

implicit class EnMan(man:Man){
    def fly(): Unit ={
        println(s"EnMan ${man.name} can fly ...")
    }
}

val john = new Man("john")
john.fly // EnMan john can fly ...

Man类增加 fly 方法

弊端

代码执行流程不清晰,你可能都看不明白

泛型

对类型的约束

object GenericApp {
    def main(args: Array[String]): Unit = {
        // 只能是子类
        testUpperBounds(new Child)
        testUpperBounds(new User)
        // scala 2.11.8 是可以传 Child
        testLowerBounds(new Person)
        testLowerBounds(new Child)
        testLowerBounds(new User)
    }
    // 上界:约束 T 是 User 或子类
    def testUpperBounds[T <: User](t:T): Unit ={
        println(t)
    }
    // 下界:约束 T 是 User 或父类?不同版本表现不同,
    def testLowerBounds[T >: User](t:T): Unit ={
        println(t)
    }
}
class Person
class User extends Person
class Child extends User

逆变

子类 ==> 父类,减少功能

// 传递子类
val person = new Test[Person]
println(person)

class Person
class User extends Person
class Child extends User
// 逆变
class Test[-User]

协变

父类 ==> 子类,增强功能

val child = new Test[Child]
println(child)

class Person
class User extends Person
class Child extends User
// 协变
class Test[+User]

ScalikeJDBC

官网 ScalikeJDBC 是 Scala 开发人员基于 SQL 的简洁数据库访问库

application.conf

数据库连接配置:src/main/resources/application.conf

# MySQL example
db.default.driver="com.mysql.jdbc.Driver"
db.default.url="jdbc:mysql://192.168.22.147:3306/rz_db"
db.default.user="root"
db.default.password="123456"

# Connection Pool settings
db.default.poolInitialSize=10
db.default.poolMaxSize=20
db.default.connectionTimeoutMillis=1000


code

查询 platform_stat

object ScalikeJDBCApp {
    def main(args: Array[String]): Unit = {
        // 初始化 JDBC 配置,加载 application.conf
        DBs.setupAll()
	// 查询示例
        query()
    }
    def query()={
        val offsets = DB.readOnly({
            implicit session => {
                SQL("select * from platform_stat").map(rs => {
                    platform(
                        rs.string("platform"),
                        rs.int("cnt"),
                        rs.string("d")
                    )
                }).list().apply()
            }
        })
        // 输出
        offsets.foreach(println)
    }
}
case class platform(platform:String,cnt:Int,time:String)

参考资料

深入理解scala的柯里化( currying or curry )以及其用处
偏函数 vs 部分应用函数 vs 柯里化