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] = [email protected]
  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
}

小记

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

时间: 2024-08-28 14:57:50

Scala 中的函数式编程基础(三)的相关文章

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

主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. 2. Higher Order Functions 把其他函数作为参数或者作为返回值,就是 higher order functions,python 里面也可以看到这样使用的情形.在酷壳上的博客有一个例子就是将函数作为返回值. 2.1 匿名函数 在 python 里边叫 lambda 函数,常常与 map(

SCALA中的函数式编程

演示了值函数,匿名函数,闭包... 其它具体的应用,还得在生产当中吧.. 这个告一段落..其它SAM,CURRY,高阶函数,集合,泛型,隐式类..这些,还是找专门的书去深入了解啦... C:\Users\hengheng>scala Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_4 3). Type in expressions to have them evaluated. Type

97. 在LotusScript中模拟函数式编程

本文将介绍96. 通用字段修改器用到的在LotusScript中模拟函数式编程的技巧. 函数式编程 函数式编程是一种优美而强大的编程范式.它源于Alonzo Church提出的λ演算(Lambda演算),而某个问题能表示成Lambda演算,按照Church–Turing论题,等价于该问题在数学上是可以有效计算的.粗略地说,用对应于Lambda演算的一门函数式程序语言可以写出任何理论上可计算问题的计算程序.因为与可计算理论的紧密关系和强大的表现力,函数式编程在学术界历来很受重视.但是在日常应用的软

JS函数式编程【译】4.在Javascript中实现函数式编程的技术

?? Functional Programming in Javascript 主目录上一章 建立函数式编程环境 第四章 在Javascript中实现函数式编程的技术 扶好你的帽子,我们现在要真正进入函数式的思想了. 这章我们继续下面的内容: 把所有的核心概念放到一个集中的范式里 探索函数式编程之美 一步步跟踪函数式模式相互交织的逻辑 我们将贯穿整章建立一个简单的应用做一些很酷的事情 你可能已经注意到,在上一章我们介绍Javascript的函数式库的时候引入了一些概念, 而不是在第二章<函数式编

JavaScript ES6函数式编程(三):函子

前面二篇学习了函数式编程的基本概念和常见用法.今天,我们来学习函数式编程的最后一个概念--函子(Functor). 相信有一部分同学对这个概念很陌生,毕竟现在已经有很多成熟的轮子,基本能满足我们日常的业务开发,所以没必须重复造轮子.但是,作为一名(未来)优秀的程序员,光会用怎么能行呢?必须要理解更深层的思想.下面就来学习函子部分的知识... 函子(Functor) 在正式学习函子之前,我会先抛出一个问题,先用普通的方式解决,然后转换为用函子解决,这能帮助我们更好的理解函子.同时,这也是我想说的,

[原创译书] JS函数式编程 2.函数式编程基础

2 函数式编程基础 ?? Functional Programming in Javascript 主目录上一章 Javascript函数式编程的力量——举个例子 现在,你已经稍稍领略了一点函数式编程能做的事情.但是到底什么是函数式编程呢? 如何来区分一个语言是否是函数式的?又如何来区分一段程序是否是函数式的呢? 在这章,我们先来看看下面的问题,这些问题覆盖了函数式编程的核心概念: 使用函数和数组实现控制流 编写纯函数.匿名函数.递归函数等等 像对象那样传递函数 利用map().filter()

python中的函数式编程与装饰器

2.1 python中的函数式编程 函数式编码的特点 把计算视为函数而非指令 纯函数式编程,不需要变量,没有副作用,测试简单 支持高阶函数,代码简洁 python支持的函数式编程 不是纯函数式编码:允许有变量 支持高阶函数:函数也可以作为变量传入 支持闭包:有了闭包就能返回函数 有限度地支持匿名函数 2.2 python中高阶函数 函数名可以作为变量,如 高阶函数:只能接收函数作为参数的函数 变量可以是指向函数 函数的参数可以接收变量 一个函数可以接收另一个函数作为参数 能接收函数作为参数的函数

Javascript 中的函数式编程

本文和大家分享的主要是javascript中函数式编程相关内容,一起来看看吧,希望对大家学习javascript有所帮助. 函数式编程(functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程. 函数式编程,近年来一直被炒得火热,国内外的开发者好像都在议论和提倡这种编程范式.在众多的函数式语言中,Jav

C#中的函数式编程:递归与纯函数(二)

在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential transparency)来定义的.如果一个表达式满足将它替换成它的值,而程序的行为不变,则称这个表达式是引用透明的. 现在,我们不妨进行一个尝试:我们来实现一些函数,但是这次有一个限制:只能用无副作用的表达式. 先以素数判定为例子,我们要写一个函数bool IsPrime(int n),它返回这个整数是不是素数.简单起见,我们采用最朴素的方