Go coding in go way

素数是一个自然数,它具有两个截然不同的自然数除数:1和它本身。 要找到小于或等于给定整数n的素数,我们可以使用Eratosthenes' sieve(埃拉托斯特尼)算法(https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)。 算法描述:先用最小的素数2去筛,把2的倍数剔除掉;下一个未筛除的数就是素数(这里是3)。再用这个素数3去筛,筛除掉3的倍数... 这样不断重复下去,直到筛完为止 思考 面对同一个问题,来自不同编程语言的程序员给出了思维方式截然不同的解决方法 一定程度上印证了前面的假说:编程语言影响编程思维 避免Go coding in c way/in java way/in python way... 目标:Go coding in go way
展开查看详情

1.Go coding in go way GopherChina 2017 15 April 2017 Tony Bai Neusoft

2.人类语言与思维:萨丕尔-沃夫假说 "语言影响/决定思维" - 萨丕尔-沃夫假说 ("Language in uences/determines thought" - Sapir-Whorf hypothesis)

3.编程语言与思维:图灵奖得主的认知 "A language that doesn't a ect the way you think about programming is not worth knowing." - Alan J. Perlis(艾伦·佩利),首届图灵奖得主

4.问题: 素数筛 素数是一个自然数,它具有两个截然不同的自然数除数:1和它本身。 要找到小于或等于给定 整数n的素数,我们可以使用Eratosthenes' sieve(埃拉托斯特尼)算法 (https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) 。 算法描述:先用最小的素数2去筛,把2的倍数剔除掉;下一个未筛除的数就是素数(这里是3)。 再用这个素数3去筛,筛除掉3的倍数... 这样不断重复下去,直到筛完为止。

5.sieve.c //基于数组的内存操作 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; /*sieve the non-primes*/ } } 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]); }

6.sieve.hs //函数递归 sieve [] = [] sieve (x:xs) = x : sieve (filter (\a -> not $ a `mod` x == 0) xs) n = 100 main = print $ sieve [2..n] [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]

7.sieve.go //并发组合 (borrowed from Rob Pike's slide) func generate(ch chan<- int) { for i := 2; ; i++ { ch <- i // Send 'i' to channel 'ch'. } } func filter(src <-chan int, dst chan<- int, prime int) { for i := range src { // Loop over values received from 'src'. if i%prime != 0 { dst <- i // Send 'i' to channel 'dst'. } } } func sieve() { ch := make(chan int) // Create a new channel. go generate(ch) // Start generate() as a subprocess. for { prime := <-ch fmt.Print(prime, "\n") ch1 := make(chan int) go filter(ch, ch1, prime) ch = ch1 } }

8.sieve.go (cont.) From divan's blog (http://divan.github.io/posts/go_concurrency_visualize/)

9.思考 面对同一个问题,来自不同编程语言的程序员给出了思维方式截然不同的解决方法 一定程度上印证了前面的假说:编程语言影响编程思维 避免Go coding in c way/in java way/in python way... 目标:Go coding in go way

10.编程语言思维的形成 根本:价值观决定思维和语言结构 核心:思维和语言结构影响语言的应用行为 反馈:语言的应用行为反过来持续影响/优化语言结构

11.Go语言价值观 Go编程思维的形成深受Go语言价值观的影响 Go语言价值观的形成和内容 Go语言价值观主导下的Go编程思维

12.Go语言价值观形成 - 语言设计者&Unix文化(主导) (从左到右) Robert Griesemer, Rob Pike和Ken Thompson

13.Go语言价值观形成 - Go语言的遗传基因 The task of the programming language designer " is consolidation not innovation ". (Tony Hoare, 1973, Go并发模型最初CSP的提出者).

14.Go语言价值观形成 - 面向新的基础设施环境和大规模软件开发的诸多问题(初 衷) 新的基础设施环境: 大规模云计算数据中心 多核以及多处理器硬件体系 大规模软件开发的难题(Google内部): slow builds uncontrolled dependencies each programmer using a di erent subset of the language poor program understanding (documentation, etc.) duplication of e ort cost of updates cross-language builds

15.Go语言的价值观 Overall Simplicity Orthogonal Composition Preference in Concurrency 一句话概括Go的价值观:" orthogonal composition of simple concepts with preference in concurrency ".

16.Go语言的价值观(cont.) 价值观 (价值观下的)语言设计 编程思维 该编程思维在语言设计、标准库实现以及主流Go开源项目中的应用体现

17.Overall Simplicity

18.语言设计 "Simplicity & elegance are unpopular because they require hard work & discipline to achieve & education to be appreciated." - 图灵奖获得者Dijkstra(迪杰斯特拉) "Overall Simplicity"价值观更多地体现在了Go语言设计层面: clean and regular(mostly) syntax only 25 keywords one way to write a piece of code and minimize programmer's e ort garbage collection goroutines constants interfaces packages

19.short naming thought 短小 一致 在上下文环境中用最短的名字携带足够的信息 java vs. go "index" vs. "i" "value" vs. "v"

20.变量命名统计 cat $(find $GOROOT -name '*.go') | indents | sort | uniq -c | sort -nr | sed 30q 60224 v 42444 err 38012 t 33386 x 33302 i 33277 b 27943 p 25121 s 21228 n 20831 r 20634 _ 19236 c 17056 y 12850 f 12834 a ... ...

21.常见的变量短命名含义 [v, k, i] // loop varible for i, v := range s { for k, v := range m { for v := range r { // channel // if、switch/case clause varible if v := mimeTypes[ext]; v != "" { switch v := ptr.Elem(); v.Kind() { case v := <-c: v := reflect.ValueOf(x) // result of reflect.Value() [t] t := time.Now() // time t := &Timer{ // timer if t := md.typemap[off]; t != nil { // type [b] b := make([]byte, n) // bytes slice b := new(bytes.Buffer) // bytes.Buffer

22.minimal thought "一种"代码写法,或提供最少的写法、更简单的写法 Go is not a “TMTOWTDI—There’s More Than One Way To Do It” 显而易见的代码,而不是"聪明"的代码 minimize programmer's e ort Least concern about coding style: - Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.

23."一种"循环: for 常规 for i := 0; i < count; i++ {} "while" for condition { } "do-while" for { // use "for-break" instead doSomething() if condition { break } } iterator loop for k, v := range f.Value {} dead loop for {}

24."一种"constants constants只是数字,无需显式赋予类型 const incomingQueueLength = 25 const ( http2minMaxFrameSize = 1 << 14 http2maxFrameSize = 1<<24 - 1 ) const PI = 3.1415928 const e = 1E6 无需算术转换 const a = 10080 var c int32 = 99 d := c + a fmt.Printf("%T\n", d) //int32 fmt.Printf("%T\n", a) //int

25."一种"错误处理方法 There are only two kinds of language: the ones people complain about, and the ones nobody uses. - Bjarne Stroustrup 基于比较的错误处理, 没有针对exception的"try-catch"的控制结构 让使用者更聚焦于错误本身,显式地处理每一个error,让你对代码更加自信 不增加语言复杂性:错误值和其他类型的值地位是一样的,错误处理代码也是普通代码, 并无特殊之处。 "We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code.(go faq)"

26.错误处理模式 使用error类型作为错误码返回类型,而不是其他类型(比如int) 最常见的 callee: return errors.New("something error") or return fmt.Errorf("something error: %s", "error reason") caller: if err != nil {... } 导出的error变量 // io/io.go var ErrShortWrite = errors.New("short write") var ErrShortBuffer = errors.New("short buffer") if err := doSomeIO(); err == io.ErrShortWrite { ... }

27.错误处理模式(cont.) 定义新的错误类型实现定制化错误上下文 // encoding/json/decode.go type UnmarshalTypeError struct { Value string // description of JSON value - "bool", "array", "number -5" Type reflect.Type // type of Go value it could not be assigned to Offset int64 // error occurred after reading Offset bytes Struct string // name of the struct type containing the field Field string // name of the field holding the Go value } func (e *UnmarshalTypeError) Error() string { ... ... return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() } if serr, ok := err.(*UnmarshalTypeError); ok { //use serr to access context in UnmarshalTypeError ... ... }

28.错误处理模式(cont.) 将某个包中的Error Type归类,统一提取出一些公共行为特征 将这些公共行为特征(behaviour)放入一个公开的interface中 以stdlib的net package为例: //net/net.go type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary? } net/http/server.go中的使用举例: rw, e := l.Accept() if e != nil { if ne, ok := e.(net.Error); ok && ne.Temporary() { ... .. } ... ... }

29.错误处理模式(cont.) 一个实现了net.Error的Error type的实现 //net/net.go type OpError struct { ... ... // Err is the error that occurred during the operation. Err error } type temporary interface { Temporary() bool } func (e *OpError) Temporary() bool { if ne, ok := e.Err.(*os.SyscallError); ok { t, ok := ne.Err.(temporary) return ok && t.Temporary() } t, ok := e.Err.(temporary) return ok && t.Temporary() }