第十四章 使用递归的方式去思考,去编程14.1 基本介绍14.2 Scala 提倡函数式编程(递归思想)14.3 应用案例1-求和14.4 应用案例2-求最大值14.5 应用案例3-翻转字符串14.6 应用案例4-求阶乘14.7 应用案例5-求x的n次方14.8 应用案例6-求斐波那契数14.9 作业07、作业08和作业0914.9.1 作业0714.9.2 作业0814.9.2 作业09
第十四章 使用递归的方式去思考,去编程
14.1 基本介绍
14.2 Scala 提倡函数式编程(递归思想)
编程范式
14.3 应用案例1-求和
scala 中循环不建议使用 while 和 do…while,而建议使用递归。
计算1-50的和
示例代码如下:
package com.atguigu.chapter14
import java.text.SimpleDateFormat
import java.util.Date
/**
* 1、计算1-50的和。使用常规的方式解决。
*/
object RecursiveDemo01 {
def main(args: Array[String]): Unit = {
// 方式一:使用常规的方式解决。
val now1: Date = new Date()
val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date1 = dateFormat.format(now1)
println("date1=" + date1) // 输出时间
var res = BigInt(0) // 存放计算的结果
var num = BigInt(1) // 变化的数
val maxVal = BigInt(99999999l) // 测试效率使用大数
while (num <= maxVal) {
res += num // 累加
num += 1 // 变量的叠加
}
println("res=" + res)
val now2: Date = new Date()
val date2 = dateFormat.format(now2)
println("date2=" + date2) // 输出时间
}
}
输出结果如下:
date1=2019-04-03 10:11:44
res=4999999950000000
date2=2019-04-03 10:11:49
示例代码如下:
package com.atguigu.chapter14
import java.text.SimpleDateFormat
import java.util.Date
/**
* 1、计算1-50的和。递归的方式来解决
*/
object RecursiveDemo02 {
def main(args: Array[String]): Unit = {
val now1: Date = new Date()
val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date1 = dateFormat.format(now1)
println("date1=" + date1) // 输出时间
// 测试
var num = BigInt(1)
var sum = BigInt(0)
var res = mx(num, sum)
println("res=" + res)
val now2: Date = new Date()
val date2 = dateFormat.format(now2)
println("date2=" + date2) // 输出时间
}
// 使用递归的方式来解决
def mx(num: BigInt, sum: BigInt): BigInt = {
if (num <= 99999999l) return mx(num + 1, sum + num)
else return sum
}
}
输出结果如下:
date1=2019-04-03 10:11:59
res=4999999950000000
date2=2019-04-03 10:12:05
14.4 应用案例2-求最大值
求最大值
示例代码如下:
package com.atguigu.chapter14
/**
* 2、求最大值
*/
object RecursiveDemo03 {
def main(args: Array[String]): Unit = {
def mymax(xs: List[Int]): Int = {
if (xs.isEmpty)
throw new java.util.NoSuchElementException
if (xs.size == 1)
xs.head
else if (xs.head > mymax(xs.tail)) xs.head else mymax(xs.tail)
}
println(mymax(List(1, 2, 3, 4, 5)))
}
}
输出结果如下:
5
14.5 应用案例3-翻转字符串
翻转字符串
示例代码如下:
package com.atguigu.chapter14
/**
* 3、翻转字符串
*/
object RecursiveDemo04 {
def main(args: Array[String]): Unit = {
def myreverse(xs: String): String =
if (xs.length == 1) xs else myreverse(xs.tail) + xs.head
println(myreverse("abc"))
}
}
输出结果如下:
cba
14.6 应用案例4-求阶乘
求阶乘
示例代码如下:
package com.atguigu.chapter14
/**
* 4、求阶乘
*/
object RecursiveDemo05 {
def main(args: Array[String]): Unit = {
def myfactorial(n: BigInt): BigInt =
if (n == 0) 1 else n * myfactorial(n - 1)
println(myfactorial(5))
}
}
输出结果如下:
120
14.7 应用案例5-求x的n次方
示例代码如下:
package com.atguigu.chapter14
/**
* 5、求x的n次方
*/
object RecursiveDemo06 {
def main(args: Array[String]): Unit = {
println(mi(2.5, 3))
}
// 递归的妙用:求 x 的 n 次方,厉害啊!!!
def mi(x: Double, n: Int): Double = {
if (n == 0) 1 // x 的 0 次方等于 1
else if (n > 0) x * mi(x, n - 1)
else 1 / mi(x, -n)
}
}
输出结果如下:
15.625
14.8 应用案例6-求斐波那契数
示例代码如下:
package com.atguigu.chapter14
import scala.io.StdIn
/**
* 6、给你一个整数n,求出它的斐波那契数(1,1,2,3,5,8,13...)
*/
object RecursiveDemo07 {
def main(args: Array[String]): Unit = {
var count = BigInt(0)
// 研究下递归求斐波那契数的递归次数的增长情况:指数爆炸式增长
def fbn(n: Int): Int = {
count += 1
if (n == 1 || n == 2) {
1
} else {
fbn(n - 1) + fbn(n - 2) // 因为我们这里调用了2次递归,出现了重复计算,需要考虑优化:方案一:改递归为迭代,方案二:减少递归次数。
}
}
println("请输入一个正整数:")
val n = StdIn.readInt()
printf("%d的斐波那契数是:%d", n, fbn(n))
println()
println("调用的次数=" + count)
}
}
关于斐波那契数列的优化:https://blog.csdn.net/dadai_/article/details/50209511
作为算法工程师/科学家,名校公开课程或者考研课程《数值分析》需要学习。
14.9 作业07、作业08和作业09
14.9.1 作业07
数据结构(集合)
1、编写一段代码,将 a 设置为一个 n 个随机整数的数组,要求随机数介于 0 和 n 之间。
2、编写一个循环,将整数数组中相邻的元素置换。比如 Array(1, 2, 3, 4, 5) 置换后为 Array(2, 1, 4, 3, 5)。
3、给定一个整数数组,产出一个新的数组,包含原数组中的所有正值,以原有顺序排列,之后的元素是所有零或负值,以原有顺序排列。
4、设置一个映射,其中包含你想要的一些装备,以及它们的价格。然后根据这个映射构建另一个新映射,采用同一组键,但是价格上打9折。
5、编写一个函数 minmax(values: Array[Int]), 返回数组中最小值和最大值的对偶。
6、编写一个函数,从一个整型链表中去除所有的零值。
7、编写一个函数,接受一个字符串的集合,以及一个从字符串到整数值的映射。返回整形的集合,其值为能和集合中某个字符串相对应的映射的值。
举例来说,给定 Array("Tom", "Fred", "Harry") 和 Map("Tom"->3, "Dick"->4, "Harry"->5),返回 Array(3, 5)。提示:用 flatMap 将 get 返回的 Option 值组合在一起。
8、实现一个函数,作用与 mkStirng 相同,提示:使用 reduceLeft 实现。
9、给定整型列表lst,(lst : List[Int]())(_ :: _)
得到什么?(List[Int]() /: lst)(_ :+ _)
又得到什么?如何修改他们中的一个,以对原列表进行反向排列?
10、编写一个函数,将 Double 数组转换成二维数组。传入列数作为参数。具体来说,传入 Array(1, 2, 3, 4, 5, 6) 和 3 列,返回 Array(Array(1, 2, 3), Array(4, 5, 6))。
示例代码链接:xxx
14.9.2 作业08
类型参数
1、定义一个不可变类 Pair1[T, S],带一个 swap 方法,返回组件交换过位置的新对偶。
2、定义一个可变类 Pair2[T],带一个 swap 方法,交换对偶中组件的位置。
3、给定类 Pair3[T, S],编写一个泛型方法 swap,接受对偶作为参数并返回组件交换过位置的新对偶。
4、给定可变类 Pair4[S, T],使用类型约束定义一个 swap 方法,当类型参数相同时可以被调用。
示例代码如下:
package com.atguigu.chapter14.homework.hw02
/**
* 类型参数
* 1、定义一个不可变类 Pair1[T, S],带一个 swap 方法,返回组件交换过位置的新对偶。
* 2、定义一个可变类 Pair2[T],带一个 swap 方法,交换对偶中组件的位置。
* 3、给定类 Pair3[T, S],编写一个泛型方法 swap,接受对偶作为参数并返回组件交换过位置的新对偶。
* 4、给定可变类 Pair4[S, T],使用类型约束定义一个 swap 方法,当类型参数相同时可以被调用。
*/
object Exercise01 {
def main(args: Array[String]): Unit = {
}
}
// 1、定义一个不可变类 Pair1[T, S],带一个 swap 方法,返回组件交换过位置的新对偶。
final class Pair1[T, S](val t: T, val s: S) {
def swap() = {
new Pair1(s, t)
}
}
// 2、定义一个可变类 Pair2[T],带一个 swap 方法,交换对偶中组件的位置。
class Pair2[T](val t: T, val s: T) {
def swap() = {
new Pair2(s, t)
}
}
// 3、给定类 Pair3[T, S] ,编写一个泛型方法 swap,接受对偶作为参数并返回组件交换过位置的新对偶。
class Pair3[T, S](val t: T, val s: S) {
def swap[T, S](t: T, s: S) = {
new Pair3(s, t)
}
}
// 4、给定可变类 Pair4[S, T],使用类型约束定义一个 swap 方法,当类型参数相同时可以被调用。
class Pair4[S, T](val s: S, val t: T) {
def swap(implicit env2: S =:= T) = {
new Pair4(t, s)
}
}
14.9.2 作业09
模式匹配
1、利用模式匹配,编写一个 swap 函数,接受一个整数的对偶,返回对偶的两个组成部件互换位置的新对偶。
2、利用模式匹配,编写一个 swap 函数,交换数组中的前两个元素的位置,前提条件是数组长度至少为 2。
3、编写一个函数,计算 List[Option[Int]] 中所有非 None 值之和。不得使用 match 语句。
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 模式匹配
* 1、利用模式匹配,编写一个 swap 函数,接受一个整数的对偶,返回对偶的两个组成部件互换位置的新对偶。
* 2、利用模式匹配,编写一个 swap 函数,交换数组中的前两个元素的位置,前提条件是数组长度至少为 2。
* 3、编写一个函数,计算 List[Option[Int]] 中所有非 None 值之和。不得使用 match 语句。
*/
object Exercise01 {
def main(args: Array[String]): Unit = {
println(swap1(100, "hello"))
val arr = swap2(Array(1, 2, 4, 5))
for (item <- arr) {
println(item + " ")
}
val x = List(Some(1), None, Some(2), None, Some(3))
println(sum(x))
}
// 1、利用模式匹配,编写一个 swap1 函数,接受一个整数的对偶,返回对偶的两个组成部件互换位置的新对偶。
def swap1[S, T](tup: (S, T)) = {
tup match {
case (a, b) => (b, a)
}
}
// 2、利用模式匹配,编写一个 swap2 函数,交换数组中的前两个元素的位置,前提条件是数组长度至少为 2。
def swap2(array: Array[Any]) = {
array match { // @ 表示法将嵌套的值绑定到变量。*_ 绑定剩余元素到 rest。
case Array(first, second, rest @ _*) => Array(second, first) ++ rest // ++ 表示拼接
case _ => array
}
}
// 3、编写一个函数,计算 List[Option[Int]] 中所有非 None 值之和。不得使用 match 语句。
def sum(lst: List[Option[Int]]) = lst.map(_.getOrElse(0)).sum
}
输出结果如下:
(hello,100)
2
1
4
5
6
高阶函数
1、编写一个 compose 函数,将两个类型为 Double => Option[Double] 的函数组合在一起,产生另一个同样类型的函数。如果其中一个函数返回 None,则组合函数也应返回 None。例如:
def f(x: Double) = if (x > 0) Some(sqrt(x)) else None
def g(x: Double) = if (x != 1) Some(1 / ( x - 1)) else None
val h = compose(f, g)
h(2) 将得到 Some(1),而 h(1) 和 h(0) 将得到 None。
示例代码如下:
package com.atguigu.chapter14.homework.hw03
import scala.math.sqrt
/**
* 高阶函数
* 1、编写一个 compose 函数,将两个类型为 Double => Option[Double] 的函数组合在一起,产生另一个同样类型的函数。如果其中一个函数返回 None,则组合函数也应返回 None。例如:
* def f(x: Double) = if (x > 0) Some(sqrt(x)) else None
* def g(x: Double) = if (x != 1) Some(1 / ( x - 1)) else None
* val h = compose(f, g)
* h(2) 将得到 Some(1.0),而 h(1) 和 h(0) 将得到 None。
*
*/
object Exercise02 {
def main(args: Array[String]): Unit = {
val h = compose(f, g)
println(h(2))
println(h(1))
println(h(0))
}
def f(x: Double) = if (x > 0) Some(sqrt(x)) else None
def g(x: Double) = if (x != 1) Some(1 / (x - 1)) else None
def compose(f: Double => Option[Double], g: Double => Option[Double]) = {
// 返回一个匿名函数
(x: Double) =>
// 如果其中一个函数返回 None,则组合函数也应返回 None
if (f(x) == None || g(x) == None) None else g(x)
}
}
输出结果如下:
Some(1.0)
None
None
2、编写函数 values(fun: (Int) => Int, low: Int, high: Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。比如,values(x => x * x, -5, 5) 应该产出一个对偶的集合 (5,25)(4,16)(3,9),…,(-4,16)(-5,25)
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 2、编写函数 values(fun: (Int) => Int, low: Int, high: Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。
* 比如,values(x => x * x, -5, 5) 应该产出一个对偶的集合 (5,25)(4,16)(3,9),...,(-4,16)(-5,25)
*/
object Exercise03 {
def main(args: Array[String]): Unit = {
println(values(x => x * x, -5, 5).mkString)
}
def values(fun: (Int) => Int, low: Int, high: Int) = {
var array = List[(Int, Int)]()
low to high foreach {
x =>
array = (x, fun(x)) :: array
}
array
}
}
输出结果如下:
(5,25)(4,16)(3,9)(2,4)(1,1)(0,0)(-1,1)(-2,4)(-3,9)(-4,16)(-5,25)
3、如何用 reduceLeft 得到数组 Array(1, 333, 4, 6, 4, 4, 9, 32, 6, 9, 0, 2) 中的最大元素?
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 3、如何用 reduceLeft 得到数组 Array(1, 333, 4, 6, 4, 4, 9, 32, 6, 9, 0, 2) 中的最大元素?
*/
object Exercise04 {
def main(args: Array[String]): Unit = {
val arr = Array(1, 333, 4, 6, 4, 4, 9, 32, 6, 9, 0, 2)
println(arr.reduceLeft(f1))
// 简写形式
// println(arr.reduceLeft((l, r) => if (l > r) l else r))
}
def f1(l: Int, r: Int): Int = {
if (l > r) l else r
}
}
输出结果如下:
333
4、用 to 和 reduceLeft 实现阶乘函数,不得使用循环或递归。
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 4、用 to 和 reduceLeft 实现阶乘函数,不得使用循环或递归。
*/
object Exercise05 {
def main(args: Array[String]): Unit = {
println(factorial(5))
}
def factorial(n: Int): Int = {
// 1 to n reduceLeft ((n1: Int, n2: Int) => n1 * n2)
// 1 to n reduceLeft ((n1, n2) => n1 * n2)
1 to n reduceLeft (_ * _)
}
}
输出结果如下:
120
5、编写函数 largest(fun: (Int) => Int, inputs: Seq[Int]),输出在给定输入序列中给定函数的最大值。举例来说,largest(x => 10 * x - x * x, 1 to 10) 应该返回 25。不得使用循环或递归。
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 5、编写函数 largest(fun: (Int) => Int, inputs: Seq[Int]),输出在给定输入序列中给定函数的最大值。
* 举例来说,largest(x => 10 * x - x * x, 1 to 10) 应该返回 25。不得使用循环或递归。
*/
object Exercise06 {
def main(args: Array[String]): Unit = {
println(largest1(x => 10 * x - x * x, 1 to 10))
println(largest2(x => 10 * x - x * x, 1 to 10))
}
def largest1(fun: (Int) => Int, inputs: Seq[Int]) = {
inputs.foldLeft(1)((a, b) => if (fun(b) > a) fun(b) else a)
}
def largest2(fun: (Int) => Int, inputs: Seq[Int]) = {
inputs.map(fun(_)).max
}
}
输出结果如下:
25
25
6、要得到一个序列的对偶很容易,比如:
val pairs = (1 to 10) zip (11 to 20)
编写函数 adjustToPair,该函数接受一个类型为 (Int, Int) => Int 的函数作为参数,并返回一个等效的,可以以对偶作为参数的函数。
举例来说就是:adjustToPair(_ * _)((6, 7)) 应得到 42。然后用这个函数通过 map 计算出各个对偶的元素之和。
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 6、要得到一个序列的对偶很容易,比如:
* val pairs = (1 to 10) zip (11 to 20)
* 编写函数 adjustToPair,该函数接受一个类型为 (Int, Int) => Int 的函数作为参数,并返回一个等效的,可以以对偶作为参数的函数。
* 举例来说就是:adjustToPair(_ * _)((6, 7)) 应得到 42。然后用这个函数通过 map 计算出各个对偶的元素之和。
*/
object Exercise07 {
def main(args: Array[String]): Unit = {
def adjustToPair(fun: (Int, Int) => Int) = {
(x: (Int, Int)) => fun(x._1, x._2)
}
val x = adjustToPair(_ * _)((6, 7))
println(x)
val pairs = (1 to 10) zip (11 to 20)
println(pairs)
val y = pairs.map(adjustToPair(_ + _))
println(y)
}
}
输出结果如下:
42
Vector((1,11), (2,12), (3,13), (4,14), (5,15), (6,16), (7,17), (8,18), (9,19), (10,20))
Vector(12, 14, 16, 18, 20, 22, 24, 26, 28, 30)
7、实现一个 unless 控制抽象,工作机制类似 if,但条件是反过来的。
示例代码如下:
package com.atguigu.chapter14.homework.hw03
/**
* 7、实现一个 unless 控制抽象,工作机制类似 if,但条件是反过来的。
*/
object Exercise08 {
def main(args: Array[String]): Unit = {
}
def unless(conditon: => Boolean)(block: => Unit) = {
if (!conditon) {
block
}
}
unless(0 > 1) {
println("Unless!")
}
}