zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Go语言精进自学系列 | 使用Go语言原生编码思维来写Go代码

Go编码语言代码 系列 原生 思维 自学
2023-09-14 09:09:06 时间

书籍来源:《Go语言精进之路:从新手到高手的编程思想、方法和技巧》

一边学习一边整理读书笔记,并与大家分享,侵权即删,谢谢支持!

附上汇总贴:Go语言精进自学系列 | 汇总_COCOgsta的博客-CSDN博客


在编写一些Go代码之后,很多人感觉自己写的Go代码很别扭,并且总是尝试在Go语言中寻找自己熟悉的上一门语言中的语法元素。出现这种情况的主要原因是大脑中上一门编程语言的思维方式在“作祟”。那么思维与语言之间究竟有什么联系呢?

4.1 语言与思维——来自大师的观点

在编程语言界,首届图灵奖得主、著名计算机科学家艾伦·佩利(Alan J.Perlis)提出:“不能影响到你的编程思维方式的编程语言不值得学习和使用。”

4.2 现实中的“投影”

问题描述:素数是一个自然数,它具有两个截然不同的自然数除数:1和它本身。这里的问题是如何找到小于或等于给定整数n的素数。针对这个问题,我们可以采用埃拉托斯特尼素数筛算法。

算法描述:先用最小的素数2去筛,把2的倍数筛除;下一个未筛除的数就是素数(这里是3)。再用这个素数3去筛,筛除3的倍数……这样不断重复下去,直到筛完为止(算法图示见图4-1)。

图4-1 素数筛算法图示

下面是该素数筛算法的不同编程语言的实现版本。

(1)C语言版本

// chapter1/sources/sieve.c

#include <stdio.h>

#define LIMIT  50
#define PRIMES 10

void sieve() {
    int c, i,j,numbers[LIMIT], primes[PRIMES];

    for (i=0;i<LIMIT;i++){
        numbers[i]=i+2; /*fill the array with natural numbers*/
    }
    for (i=0;i<LIMIT;i++){
        if (numbers[i]!=-1){
            for (j=2*numbers[i]-2;j<LIMIT;j+=numbers[i])
                numbers[j]=-1; /* 筛除非素数 */
        }
    }
    c = j = 0;
    for (i=0;i<LIMIT&&j<PRIMES;i++) {
        if (numbers[i]!=-1) {
            primes[j++] = numbers[i]; /*transfer the primes to their own array*/
            c++;
        }
    }
    for (i=0;i<c;i++) printf("%d\n",primes[i]);
}
复制代码

(2)Haskell版本

// chapter1/sources/sieve.hs

sieve [] = []
sieve (x:xs) = x : sieve (filter (\a -> not $ a `mod` x == 0) xs)

n = 100
main = print $ sieve [2..n]
复制代码

(3)Go语言版本

// chapter1/sources/sieve.go

func Generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i
    }
}

func Filter(in <-chan int, out chan<- int, prime int) {
    for {
        i := <-in
        if i%prime != 0 {
            out <- i
        }
    }
}

func main() {
    ch := make(chan int)
    go Generate(ch)
    for i := 0; i < 10; i++ {
        prime := <-ch
        print(prime, "\n")
        ch1 := make(chan int)
        go Filter(ch, ch1, prime)
        ch = ch1
    }
}
复制代码

对比上述三个语言版本的素数筛算法的实现,我们看到:

C版本的素数筛程序是一个常规实现。它定义了两个数组numbers和primes,“筛”的过程在numbers这个数组中进行(基于纯内存修改),非素数的数组元素被设置为-1,便于后续提取。

Haskell版本采用了函数递归的思路,通过“filter操作集合”,用谓词(过滤条件)\a -> not $ a mod x == 0筛除素数的倍数,将未筛除的数的集合作为参数传递归递给下去。

Go版本程序实现了一个并发素数筛,它采用的是goroutine的并发组合。程序从素数2开始,依次为每个素数建立一个goroutine,用于作为筛除该素数的倍数。

4.3 Go语言原生编程思维

面对同一个问题,来自不同编程语言的程序员给出了截然不同的方法:C的命令式思维、Haskell的函数式思维和Go的并发思维。我们可以得到一个未经理论证实但又确实对现实有影响的推论:编程语言影响编程思维,或者说每种编程语言都有属于自己的原生编程思维。

经验告诉我们,但凡属于某个编程语言的高质量范畴的代码,其必定是在这种编程语言原生思维下编写的代码。

那么Go原生编程思维究竟是什么呢?一门编程语言的编程思维是由语言设计者、语言实现团队、语言社区、语言使用者在长期的演进和实践中形成的一种统一的思维习惯、行为方式、代码惯用法和风格。

我们的目标是编写出高质量的Go代码,这就需要我们在学习语言的同时,不断学习Go语言原生的编程思维,时刻用Go编程思维考虑Go代码的设计和实现,这是通往高质量Go代码的必经之路。