一、使用递归的方式去思考
1、一个简单问题:对数列 xs 进行求和(不使用函数)
Python 循环解法:
def my_sum(xs):
sums = 0
for i in length(xs):
sums += xs[i]
return sums
Scala 递归解法:
//xs.head 返回列表的第一个元素(头元素)
//xs.tail 返回除头元素外剩余元素组成的列表
def my_sum(xs: List[Int]): Int =
if (xs.isEmpty) 0 else xs.head + my_sum(xs.tail)
-
使用 Scala 递归,只需一行,更简洁。
2、常见数据结构:反转字符串
Python 解法:
def reverse(xs):
return xs[::-1]
Scala 解法:
def reverse(xs: String): String =
if (xs.length==1) xs else reverse(xs.tail) + xs.head
-
使用 scala 递归,仍然只需一行。
3、经典数据结构:快速排序
Python 解法:
def quickSort(xs):
pivot = xs[0]
left = [elem for elem in xs if elem < pivot]
right = [elem for elem in xs if elem >= pivot]
return quickSort(left) + pivot + quickSort(right)
Scala 解法:
def quickSort(xs: List[Int]): List[Int] = {
if (xs.isEmpty) xs
else
quickSort(xs.filter(x=>x<xs.head)):::xs.head::quickSort(xs.filter(x=>x>xs.head))
}
4、尾递归
- 问题:相比循环,递归存在效率问题。因为每一次递归调用,都会分配一个新的函数栈,如果递归嵌套很深,容易出现栈溢出的问题。
- 解决方案:尾递归(在函数调用的最后一步,只调用该递归函数本身。此时无需记住其他的变量,当前的函数栈可以被重复使用)?
递归求阶乘(当 n 很大时,函数栈将很快被耗尽)
def factorial(n: Int): Int =
if (n == 0) 1 else n * factorial(n - 1)
尾递归求阶乘
def factorial(n: Int): Int = {
@tailrec //该注释用来确保程序员写出来的是正确的尾递归程序,如果不是,则编译器会报错
def loop(acc: Int, n: Int): Int =
if n == 0 acc else loop(n * acc, n - 1)
loop(1, n)
}
Note:在上面的尾递归程序中,在阶乘函数内部又定义了一个新的递归函数,该函数最后一步要么返回结果,要么定义该递归函数本身(对比前面的递归程序,可以发现它们非常相似,不同在于前面的递归程序最后一步不能直接返回结果,还需要计算)。新递归函数多出一个变量 acc,每次递归调用都会更新该变量,直到递归边界条件满足时返回该值,即为最后的计算结果。
5、零钱兑换问题
- 问题描述:假设某国的货币有若干面值,现给一张大面值的货币要兑换成零钱,问有多少种兑换方式?
Python 动态规划求解需要的最小硬币个素
def countChange(money, coins):
coins.sort()
dp = {0:0}
for i in range(1, money + 1):
dp[i] = money + 1
for j in coins:
if j <= i:
dp[i] = min(dp[i], dp[i-j] + 1)
if dp[money] == money + 1: #当最小硬币个数为最小值时,代表不存在硬币组合能构成此金额
return -1
else:
return dp[money]
Python 非递归解法:动态规划
def change(money, coins):
dp = [0] * (money + 1)
dp[0] = 1
for coin in coins:
for x in range(coin, money + 1):
dp[x] += dp[x - coin]
return dp[money]
Scala 递归解法
def countChange(money: Int, coins: List[Int]): Int = {
if (money == 0)
0
else if (coins.size == 0 || money < 0)
0
else
countChange(money, coins.tail) + countChange(money - coins.head, coins) //找零的方法数 = 不使用第一种硬币进行找零的方法数 + 使用第一种硬币进行找零的方法数
}
二、函数式编程
1、高阶函数与匿名函数
- 问题:练习题:求1-10的和、求1-10的平方和、求1-10的立方和、求1-10的阶乘和,如何编程求解?
使用高阶函数定义求和函数
def id(n: Int) = n
def cube(n: Int) = n * n * n //定义函数求立方
def square(n : Int) = n * n //定义函数求平方
def fact(n: Int): Int =
if (n == 0) 1 else n * fact(n - 1) //定义函数求阶乘
// 高阶函数
def sum(f: Int => Int, a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sum(f, a + 1, b) //定义求和函数
// 使用高阶函数定义求和函数
def sumInt(a: Int, b: Int): Int = sum(id, a, b)
def sumCube(a: Int, b: Int): Int = sum(cube, a, b)
def sumSquare(a: Int, b: Int): Int = sum(square, a, b)
def sumFact(a: Int, b: Int): Int = sum(fact, a, b)
- 思考:多数情况下,我们关心的是高阶函数,而不是作为参数传入的函数,所以为其单独定义一个函数是没有必要的。
在高阶函数中使用匿名函数
def fact(n: Int): Int =
if (n == 0) 1 else n * fact(n - 1)
// 高阶函数
def sum(f: Int => Int, a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sum(f, a + 1, b)
// 使用高阶函数重新定义求和函数
def sumCube(a: Int, b: Int): Int = sum(x => x * x * x, a, b) //使用匿名函数 x => x * x * x
def sumSquare(a: Int, b: Int): Int = sum(x => x * x, a, b) //使用匿名函数 x => x * x
def sumFact(a: Int, b: Int): Int = sum(fact, a, b)
def sumInt(a: Int, b: Int): Int = sum(x => x, a, b) //使用匿名函数 x => x
2、柯里化
Don’t Repeat Yourself !
- 问题:上面几个求和函数的上下限变量 a、b 被重复传来传去,如何解决?
返回函数的高阶函数
def fact(n: Int): Int =
if (n == 0) 1 else n * fact(n - 1)
// 高阶函数
def sum(f: Int => Int): (Int, Int) => Int = {
def sumF(a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sumF(a + 1, b)
sumF
}
// 使用高阶函数重新定义求和函数
def sumCube: Int = sum(x => x * x * x)
def sumSquare: Int = sum(x => x * x)
def sumFact: Int = sum(fact)
def sumInt: Int = sum(x => x)
- 再简化:
直接调用高阶函数
def fact(n: Int): Int =
if (n == 0) 1 else n * fact(n - 1)
// 高阶函数
def sum(f: Int => Int): (Int, Int) => Int = {
def sumF(a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sumF(a + 1, b)
sumF
}
// 这些函数没有必要了
//def sumCube: Int = sum(x => x * x * x)
//def sumSquare: Int = sum(x => x * x)
//def sumFact: Int = sum(fact)
//def sumInt: Int = sum(x => x)
// 直接调用高阶函数 !
sum(x => x * x * x) (1, 10) //=> sumCube(1, 10)
sum(x => x) (1, 10) //=> sumInt(1, 10)
sum(x => x * x) (1, 10) //=> sumSquare(1, 10)
sum(fact) (1, 10) //=> sumFact(1, 10)
- 上面的sum函数可以简写为:
高阶函数的语法糖
// 没使用语法糖的 sum 函数
def sum(f: Int => Int): (Int, Int): Int = {
def sumF(a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sumF(a + 1, b)
sumF
}
// 使用语法糖后的 sum 函数
def sum(f: Int => Int)(a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sum(f)(a + 1, b)