泛函编程(2)-初次体验泛函编程

    泛函编程和数学方程式解题相似;用某种方式找出问题的答案。泛函编程通用的方式包括了模式匹配(pattern matching)以及递归思维(Recursive thinking)。我们先体验一下:(在阅读本系列博客文章之前,相信读者已经对Scala语言及REPL用法有所了解了。在这就不去解释Scala的语法语意了。)

先来个简单的:

复制代码
1 def reportError(msgId: Int): String = msgId match {
2      | case 1 => "Error number 1."
3      | case 2 => "Error number 2."
4      | case 3 => "Error number 3."
5      | case _ => "Unknown error!"
6      | }
7 reportError: (msgId: Int)String
复制代码

很明显,这个函数的是一个纯函数,也是一个完整函数。因为函数主体涵盖了所有输入值(注意: case _ =>)。我们可以预知任何输入msgId值所产生的结果。还有,函数中没有使用任何中间变量。看看引用情况:

1 reportError(2)
2 res3: String = Error number 2.
3 
4 scala> reportError(-1)
5 res4: String = Unknown error!

恰如我们预测的结果。

再来看看一个递归(Recursion)例子:阶乘(Factorial)是一个经典样例:

1 def factorial(n: Int): Int = {
2       if ( n == 1) n
3       else n * factorial(n-1)
4   }                                               //> factorial: (n: Int)Int
5   factorial(4)                                    //> res48: Int = 24

也可以用模式匹配方式:

1 def factorial_1(n: Int): Int = n match {
2       case 1 => 1
3       case k => k * factorial(n-1)
4   }                                               //> factorial_1: (n: Int)Int
5   factorial_1(4)                                  //> res49: Int = 24

用模式匹配方式使函数意思表达更简洁、明了。

我们试着用“等量替换”方式逐步进行约化(reduce)

1 factorial(4)
2   4 * factorial(3)
3   4 * (3 * factorial(2))
4   4 * (3 * (2 * factorial(1)))
5   4 * (3 * (2 * 1)) = 24

可以得出预料的答案。

递归程序可以用 loop来实现。主要目的是防止堆栈溢出(stack overflow)。不过这并不妨碍我们用递归思维去解决问题。 阶乘用while loop来写:

复制代码
1 def factorial_2(n: Int): Int = {
2          var k: Int = n
3          var acc: Int = 1
4          while (k > 1) { acc = acc * k; k = k -1}
5          acc
6   }                                               //> factorial_2: (n: Int)Int
7   factorial_2(4)                                  //> res50: Int = 24
复制代码

注意factorial_2使用了本地变量k,acc。虽然从表达形式上失去了泛函编程的优雅,但除了可以解决堆栈溢出问题外,运行效率也比递归方式优化。但这并不意味着完全违背了“不可改变性”(Immutability)。因为变量是锁定在函数内部的。

最后,也可用tail recursion方式编写阶乘。让编译器(compiler)把程序优化成改变成 loop 款式:

复制代码
1 def factorial_3(n: Int): Int = {
2     @annotation.tailrec
3       def go(n: Int, acc: Int): Int = n match {
4           case 1 => acc
5           case k => go(n-1,acc * k)
6       }
7       go(n,1)
8   }                                               //> factorial_3: (n: Int)Int
9   factorial_3(4)                                  //> res51: Int = 24
复制代码

得出的同样是正确的答案。这段程序中使用了@annotation.tailrec。如果被标准的函数不符合tail recusion的要求,compiler会提示。

Python泛函编程是一种编程范式,它主要关注于使用高阶函数和不可变数据来构建程序。泛函编程的核心思想是将计算过程看作是函数之间的转换,通过组合和应用函数来解决问题。 在Python中,泛函编程可以通过以下几个方面来实现: 1. 高阶函数:Python中的函数是一等公民,可以作为参数传递给其他函数,也可以作为返回值。高阶函数可以接受一个或多个函数作为参数,并返回一个新的函数。常见的高阶函数包括map、filter和reduce等。 2. 匿名函数:Python中的lambda表达式可以用来创建匿名函数,这些函数通常用于简单的计算或作为其他函数的参数。 3. 不可变数据:在泛函编程中,数据被视为不可变的,即不能被修改。这样可以避免副作用,使得程序更加可靠和易于理解。 4. 列表推导式:列表推导式是一种简洁的语法,可以通过对一个列表进行转换或筛选来创建一个新的列表。它可以替代循环语句,使代码更加简洁和易读。 5. 函数组合:泛函编程鼓励将多个函数组合在一起,形成一个新的函数。这样可以将复杂的问题分解为多个简单的函数,提高代码的可读性和可维护性。 6. 惰性求值:泛函编程中的惰性求值指的是只在需要的时候才进行计算,可以提高程序的效率。Python中的生成器和迭代器就是惰性求值的一种实现方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值