现代化程序开发笔记(8)——通俗易懂的函数式编程入门

本系列文章以我的个人博客的搭建为线索(GitHub 仓库:Evian-Zhang/evian-blog),记录我在现代化程序设计中的一些笔记。在这篇文章中,我将尽量用通俗易懂的方法,介绍函数式编程的入门知识。

什么是函数式编程

作为一个开发者,面对一个需求,我们掌握的知识常常是一定能完成这个需求,但是完成的方法多种多样,我们需要选择更好的技术来完成这些需求。就比如说我们是做汉堡的厨师,那么只要具有这方面的知识,这汉堡总能做成,无非是将制作好的肉、酱、蔬菜放在两片面包之间。但是,完成这个汉堡的手法有多种,比如说先加蔬菜,再加酱,最后加肉,再比如说加烤制的肉,速冻的蔬菜,在这些方法间权衡,才能做出最优的汉堡。

函数式编程就是一种方法,它并非一种特定的语言,而是一种理念,一种技术,让我们在编程开发的过程中,能够多一种挑选权衡的思路。函数式编程能让我们更清晰地完成我们的代码,在学习函数式编程的时候对于我们的编码思维也是一种提升,很多人往往学了函数式编程就离不开了。那么,我就介绍一下函数式编程的入门级知识。

假设我们使用Rust语言,在完成我们汉堡的制作:

struct Hamburger { }
impl Hamburger {
    fn make_hamburger(meat: Meat, vegetable: Vegetable, sauce: Sauce) -> Hamburger { /* hide */ }
}

fn main() {
    let mut hamburger_count = 0;
    let mut make = |meat, vegetable, sauce| {
        hamburger_count += 1;
        Hamburger::make_hamburger(meat, vegetable, sauce)
    }
    let hamburger1 = make_hamburger(Meat::new(), Vegetable::new(), Sauce::new());
    let hamburger2 = make_hamburger(Meat::new(), Vegetable::new(), Sauce::new());
}

这是我们最初的设计思路。

基本思想

函数式编程有一个最基本的思想,函数是一等公民,我们可以像传递别的变量一样,在函数的参数、返回值中传递函数。这一点绝大多数的编程语言都已经支持了,而且十分符合逻辑(事实上,函数不是一等公民才不符合逻辑),所以这一点就不再赘述了。

纯函数

在函数式编程中,函数作为它的招牌,必然有很多理念。首先,我们介绍的是纯函数。

我们在数学中遇到过函数,在编程中也遇到过函数,这两个函数有什么不同点呢?我们有一个数学里的函数 f ( x ) = x + 1 f(x)=x+1 f(x)=x+1, 在编程中,我们有这个函数:

int f1(int x) { return x + 1; }

这和我们数学里的函数好像差不多噢。

那我们改写一下:

int y = 0;
int f2(int &x) {
    int adder;
    printf("Input a number: ");
    scanf("%d", &adder);
    int z = x + adder;
    x -= 1;
    y++;
    return z;
}

f1函数和我们的数学里的函数是完全一致的,而f2则不同,它有两个重要的不同点:

  • f2改变了xy的值。
  • 对于相同的x, f2的输出结果是不确定的。

这样的函数十分不利于我们的编程,因为它在内部改变了状态,也会改变外部的状态,而如果我们并不知道,那么擅自调用可能会导致几百行之外的某个地方出问题。

因此,我们有纯函数的概念,它要求:

  • 函数无副作用,即不改变全局变量,捕获的变量,或传入的可变引用
  • 如果输入相同,那么输出相同

这样的函数就可以比较清晰地反应我们的思路。

观察上面制作汉堡的流程,我们的make函数似乎并不是纯函数。它改变了外部捕获的变量hamburger_count。因此,我们可以这样将它改变成纯函数:

struct Hamburger { }
impl Hamburger {
    fn make_hamburger(meat: Meat, vegetable: Vegetable, sauce: Sauce) -> Hamburger { /* hide */ }
}

fn main() {
    let hamburger_count = 0;
    let make = |meat, vegetable, sauce, hamburger_count| {
        (Hamburger::make_hamburger(meat, vegetable, sauce), hamburger_count + 1)
    };
    let (hamburger1, hamburger_count) = make_hamburger(Meat::new(), Vegetable::new(), Sauce::new(), hamburger_count);
    let (hamburger2, hamburger_count) = make_hamburger(Meat::new(), Vegetable::new(), Sauce::new(), hamburger_count);
}

变量不可变

在刚刚解决纯函数的问题时,我们发现改完代码以后,原来代码里的mut都没有了,所有的变量都成了不可变的变量。这自然也符合函数式编程的思想——变量不可变。在如Haskell等纯函数式编程语言中,这是强制的,但在现在流行的一些编程语言中,也提供了这样的支持,无论是Rust的letlet mut, Swift的letvar,Kotlin的valvar,JavaScript的letconst

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值