• Scala 中的函数式编程基础(三)


    主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 《Functional Programming Principles in Scala》


    3. Data and Abstraction

    3.1 Class Hierarchies

    这一集字幕不同步-,-,听得有点费力!

    类的概念和其他语言里面很相似,基类,子类,父类啥的叫法差不多。在 Scala 中,所有用户自定义的类都是另外一个类的子类,如果没有显式给定父类,java 里面默认继承 java.lang,scala 里面是 Object。无论基类中的方法有没有具体实现,子类都可以用 override 重新定义,回想起之前强大的 toString 了吗?

    举一个二叉树的例子:

    package week3
    
    object insets {
      val t1 = new NonEmpty(3, Empty, Empty)          //> t1  : week3.NonEmpty = {.3.}
      val t2 = t1 incl 4                              //> t2  : week3.IntSet = {.3{.4.}}
      val t3 = new NonEmpty(5, Empty, Empty)          //> t3  : week3.NonEmpty = {.5.}
      t2 union t3                                     //> res0: week3.IntSet = {{{.3.}4.}5.}
    }
    
    abstract class IntSet {  // 抽象类作为基类,无法实例化,定义了三个接口
      def contains(x: Int): Boolean // 查找是否包含 x
      def incl(x: Int): IntSet // 如果 x 不存在,将 x 放入二叉树中
      def union(x: IntSet): IntSet  // 两棵树融合
      }
    
    object Empty extends IntSet { // Empty 是 IntSet 的 subclass,`object` 表示单例模式,所有空节点都可以用一个对象来表示
      def contains(x: Int): Boolean = false
      def incl(x: Int): IntSet = new NonEmpty(x, Empty, Empty)
      def union(other: IntSet): IntSet = other
      override def toString = "." // 空节点打印"."
    }
    
    class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet {
      def contains(x: Int): Boolean =
        if (x < elem) left contains x
        else if (x > elem) right contains x
        else true
    
      def incl(x: Int): IntSet =
        // 实际上创建了一个新树,新树和旧树共用未改变的子树
        // 这个叫 persistent data structure,是把函数式编程扩展到 collections 的关键之一
        // 反正他是这么说的 `-,-`
        if (x < elem) new NonEmpty(elem, left incl x, right) // 一重一重地复制节点
        else if (x > elem) new NonEmpty(elem, left, right incl x)
        else this
    
      def union(other: IntSet): IntSet =
        ((left union right) union other) incl elem  // 
    
      override def toString = "{" + left + elem + right + "}" //强大的递归啊
    }
    

    3.2 How Classes Are Organized

    没学过 java,估计和 java 中 package 管理一样。

    在源码最顶端写上 package week3 表示这个文件的 object 或者 class 属于这个包。要使用某一个类,可以在源码中用全名 week3.classA,也可以像 python 一样在最开始 import,源码中间用类名:

    • import week3.classA:导入类 classA
    • import week3.{classA, classB}:导入两个类
    • import week3._ :导入包所有(通配符导入方法)

    除了从包导入,还可以从 object 导入。所有 Scala 程序默认导入一些 entities,比如 scala 中的 Int,java.lang 中的 Object,scala.Predef 中的断言等。更多信息可以查看 scala 的标准库

    在 java 和 scala 中,一个类只能有一个父类(单继承),如何实现多继承,scala 中采用 traits。trait 像 java 里面的接口,偏抽象,但是更强大,可以包含 field 和具体方法,但是不能有value参数。子类可只能继承一个父类,但是可以继承任意多个 traits,例如:class Square extends Shape with Planar with Moveble

    scala 类型结构如下,实线表示继承,虚线表示隐式转化。

    • Any是所有类型的基本类,包含的方法有:‘==’,‘!=’,‘equals’,‘hashCode’,‘toString’
    • AnyVal是数值类型的基本类。
    • AnyRef是所有引用类型的基本类,也是 java.lang.Object 的别名。
    • Nothing是所有类的子类型。主要作用是异常类与collection中的一个空元素。
    • Null 是所有类的子类型。但是与 AnyVal 的子类型不兼容。

    Q:if (true) 1 else False 的返回类型是什么?
    A:int 和 boolean 类型,返回父类 AnyVal。

    3.3 Polymorphism

    Polymorphism 意味着函数可以以多种类型出现。一张 PPT 总结 Polymorphism:

    假设我们要建立一个 list 类,它可能包含了不同的数据类型(整数,布尔,list自身类型等),例子如下:

    这时需要用泛型来表示。新建一个package叫week4,在其中新建一个 trait。它的两个‘子类’分别为 Cons 和 Nil,分别表示含有元素的节点和空节点。

    package week4
    
    // [T] 是类型参数,比如int,double之类。是泛型编程的基础
    trait List[T] {
      def isEmpty: Boolean
      def head: T
      def tail: List[T]
    }
    
    class Cons[T](val head: T, val tail: List[T]) extends List[T] {
      def isEmpty = false
      // head 和 tail 已经在初始化中实现
    }
    
    class Nil[T] extends List[T] {
      def isEmpty = true
      def head: Nothing = throw new NoSuchElementException("Nil.head")
      // Nothing 是任何类型的子类,所以也是 T 的子类
      def tail: Nothing = throw new NoSuchElementException("Nil.tail")
    }
    

    在 week4 中新建一个 scala worksheet,测试一下上述代码:

    package week4
    
    import week4._
    
    object nth {
      // 创建一个含有一个元素的 list
      def singleton[T](elem: T) = new Cons(elem, new Nil)
            //> singleton: [T](elem: T)week4.Cons[T]
      singleton[Int](3)         //> res0: week4.Cons[Int] = week4.Cons@71be98f5
      singleton(3) // 编译器可以从 3 推到出 T 是 Int 
    
      // 寻找 list 中的第 n 个元素
      def nth[T](n: Int, xs: List[T]): T =
        if (xs.isEmpty) throw new IndexOutOfBoundsException
        else if (n == 0) xs.head
        else nth(n - 1, xs.tail)        //> nth: [T](n: Int, xs: week4.List[T])T
    
      // 创建一个 list  = [1, 2, 3]
      val list = new Cons(1, new Cons(2, new Cons(3, new Nil)))
      nth(2, list)         //> res2: Int = 3
    }
    

    小记

    这里是课程前四次的大概内容,因为第一次课是教你怎么安装,所以实际内容只有三次课,后面还有四次课。总体来说,函数式编程给人很多启发,但是如果不是真正需要用,也不宜占用太多时间去学习。暑假要去实习了,等下学期再学吧。

    by:daniel-D
    from:http://www.cnblogs.com/daniel-D/ 
  • 相关阅读:
    蚂蚁森林自动收能量
    え速记
    い 速记
    あ速记
    五十音图
    Leetcode 215. 数组中的第K个最大元素 排序 优先队列
    动手学深度学习 | 第二部分完结竞赛:图片分类 | 29
    动手学深度学习 | 残差网络 ResNet | 27
    动手学深度学习 | 批量归一化 | 26
    动手学深度学习 | 含并行连结的网络GoogLeNet/Inception V3 | 25
  • 原文地址:https://www.cnblogs.com/GarfieldEr007/p/5334094.html
Copyright © 2020-2023  润新知