go官网的7个例子分析

陆仁贾 · · 12961 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

go 语言或是 golang 的官网首页上有 7 个例子代码,赶脚灰常好,赶脚值得写篇文章简单分析一下 —— 虽然第 6 个例子不是太懂,貌似是个玩游戏的。

  1. hello,世界
    1. package main
    2.  
    3. import "fmt"
    4.  
    5. func main() {
    6.         fmt.Println("Hello, 世界")
    7. }

    这是hello,世界程序,要比hello,world其实要难一些,因为里面有中文!windows 下直接运行这个程序一般都有错误或是乱码出现,主要是编码的问题,倒不是程序的问题(请使用 utf-8 编码程序以及终端!)。关于安装以及其他细节可以看这篇文章—— 介绍 windows 下 go 环境的搭建,不仅仅是安装一下的问题。

    从这个例子中,可以看出来,go 是通过 package(包)来实现功能或是模块分割的;控制台/终端输出要借助于 fmt 包中的 Println 函数,或是 Printf 函数;使用包之前,先导入!虽然你也许可以直接使用 print/println 函数,但是不能保证永远可以用!

    go 支持单独的函数使用。所以,go 更像是 c !

    程序从 main 包的 main 函数开始执行;但是 main 函数不接受参数(参数怎么处理?),没有返回值,而且貌似语句没有分号!分号实际跟 c 语言类似,是语句的分隔符,不过 go 有自动分号插入规则,所以很多时候,偶们都可以省略分号。

  2. 处理命令行参数
    1. package main
    2.  
    3. import (
    4.         // "fmt"
    5.         "os"
    6.         "flag"    // command line option parser
    7. )
    8.  
    9. var nFlag = flag.Bool("n", true, "末尾是否换行")
    10.  
    11. func main() {
    12.         flag.Parse();          // 解析命令行选项
    13.         s := "";
    14.         for i := 0; i < flag.NArg(); i++ {
    15.                 if i > 0 {
    16.                         s += " "      // 单词之间用空格间隔开来
    17.                 }
    18.                 s += flag.Arg(i)
    19.         }
    20.         if *nFlag {         // 换行与否
    21.                 s += "\n"
    22.         }
    23.         // fmt.Println(flag.Args());
    24.         os.Stdout.WriteString(s);
    25. }
    26.  
    27. // results in cmd:
    28. > go run hello.go -n=false good morning
    29. good morning
    30. > go run hello.go -n=true good morning
    31. good moring
    32.  
    33. > go run hello.go good morning
    34. good moring
    35.  
    36. >

    这个是从上面的那个——如何处理命令行参数——问题衍生出来的程序。 main 函数不像 c/c++/java 等带有参数可以处理命令行参数,go 有自己的方法。

    flag 包负责处理命令行参数。这里实现的一个类似 echo 的命令,将命令行参数一个一个输出,对于最后是否换行,我们来个选项 -n=false 或是 -n=true 来决定;默认带有换行。

    flag.Bool 就是获取这个 -n 选项,true/false ?使用参数前使用 flag.Parse 获取这些选项和参数,然后可以使用 flag.Args() 这个片段来处理参数,或是使用 flag.NArg() 和 flag.Arg(i) —— 一个是长度,一个是具体的值,来进行处理。

    这里使用的是 os 包的 Stdout.WriteString 进行的输出,也可以使用 fmt 包中的函数进行输出。

  3. 斐波拉契闭包
    1. package main
    2.  
    3. // fib returns a function that returns
    4. // successive Fibonacci numbers.
    5. func fib() func() int {
    6.         a, b := 0, 1
    7.         return func() int {
    8.                 a, b = b, a+b
    9.                 return a
    10.         }
    11. }
    12.  
    13. func main() {
    14.         f := fib()
    15.         // Function calls are evaluated left-to-right.
    16.         println(f(), f(), f(), f(), f())
    17. }

    斐波拉契跟闭包其实嚒神马关系!斐波拉契数列是灰常出名的数列:1、1、2、3、5、8、13、...;从第三个数开始每个数均是前面两个数的和,而前两个数都是 1。习惯 c、c++ 等语言的话,e 们一般会用个循环来迭代求;go 当然也可以,不过这里使用的闭包的技术,鄙人赶脚是很巧妙的用法。

    fib 是个函数,它有两个局部变量 a、b,返回值是个函数,这个函数使用着 a、b;返回值是函数现在很多语言都支持或是准备支持了,javascript、F#等等。按 c 语言的传统,函数调用完,局部(自动)变量 a、b 销毁,不能使用;然而在 go 中当 fib 返回的函数中一直在使用 fib 中的两个局部/临时变量 —— 这实际就是闭包,然后会发生神马情况呢?会将这两个变量的生存期延长,也就是说只要返回的函数还有用,这两个变量就在使用;所以,第一次调用 f、第二次调用 f 都在使用这两个变量,这两个变量的值也一直在变化,每调用一次 f ,f 返回的 a 就是一个斐波拉契数,调用几次就返回第几个 Fibonacci 数。

    程序逻辑就是上面所说,不过这个例子还说明 go 的函数声明的问题,func 表明是个函数,然后写函数名,加上参数,而函数的返回值类型写在后面;函数名如果么有的话,那就是匿名函数!

  4. 皮亚诺整数
    1. // Peano integers are represented by a linked
    2. // list whose nodes contain no data
    3. // (the nodes are the data).
    4.  
    5. // This program demonstrates the power of Go's
    6. // segmented stacks when doing massively
    7. // recursive computations.
    8.  
    9. package main
    10.  
    11. import "fmt"
    12.  
    13. // Number is a pointer to a Number
    14. type Number *Number
    15.  
    16. // The arithmetic value of a Number is the
    17. // count of the nodes comprising the list.
    18. // (See the count function below.)
    19.  
    20. // -------------------------------------
    21. // Peano primitives
    22.  
    23. func zero() *Number {
    24.         return nil
    25. }
    26.  
    27. func isZero(x *Number) bool {
    28.         return x == nil
    29. }
    30.  
    31. func add1(x *Number) *Number {
    32.         e := new(Number)
    33.         *e = x
    34.         return e
    35. }
    36.  
    37. func sub1(x *Number) *Number {
    38.         return *x
    39. }
    40.  
    41. func add(x, y *Number) *Number {
    42.         if isZero(y) {
    43.                 return x
    44.         }
    45.         return add(add1(x), sub1(y))
    46. }
    47.  
    48. func mul(x, y *Number) *Number {
    49.         if isZero(x) || isZero(y) {
    50.                 return zero()
    51.         }
    52.         return add(mul(x, sub1(y)), x)
    53. }
    54.  
    55. func fact(n *Number) *Number {
    56.         if isZero(n) {
    57.                 return add1(zero())
    58.         }
    59.         return mul(fact(sub1(n)), n)
    60. }
    61.  
    62. // -------------------------------------
    63. // Helpers to generate/count Peano integers
    64.  
    65. func gen(n int) *Number {
    66.         if n > 0 {
    67.                 return add1(gen(n - 1))
    68.         }
    69.         return zero()
    70. }
    71.  
    72. func count(x *Number) int {
    73.         if isZero(x) {
    74.                 return 0
    75.         }
    76.         return count(sub1(x)) + 1
    77. }
    78.  
    79. // -------------------------------------
    80. // Print i! for i in [0,9]
    81.  
    82. func main() {
    83.         for i := 0; i <= 9; i++ {
    84.                 f := count(fact(gen(i)))
    85.                 fmt.Println(i, "! =", f)
    86.         }
    87. }

    这是一个 n!的例子,但又不是单单的一个求 n! 的例子;这个例子让 e 们赶脚到了 —— 自然数可以构造出来或是一一数出来!“皮亚诺自然数公理”:0 是一个自然数;每个自然数后面都跟有一个自然数;除 0 之外,每个自然数前面都有一个自然数;—— 0 很特殊,是第一个自然数,因为它前面没有自然数!同时例子中也反映了:阶乘依赖于自然数乘法;自然数乘法依赖于自然数加法,而自然数加法其实是个递归定义。

    自然数就是一个链表,链表的第一个元素代表 0 ,第二个元素代表 1,第三个 代表 2,一直下去。10 就是一个长度为 10 的链表!求 10!的思路不再是循环一下,乘一下!而是,构造出一个长度是 10! 的链表,然后 count 一下长度,就是 10!多此一举?!这里所有的操作只建立在“加一”、“减一”和“递归”的基础之上。

  5. 并发求圆周率 π
    1. // Concurrent computation of pi.
    2. //
    3. // This demonstrates Go's ability to handle
    4. // large numbers of concurrent processes.
    5. // It is an unreasonable way to calculate pi.
    6. package main
    7.  
    8. import (
    9.         "fmt"
    10.         "math"
    11. )
    12.  
    13. func main() {
    14.         fmt.Println(pi(5000))
    15. }
    16.  
    17. // pi launches n goroutines to compute an
    18. // approximation of pi.
    19. func pi(n int) float64 {
    20.         ch := make(chan float64)
    21.         for k := 0; k <= n; k++ {
    22.                 go term(ch, float64(k))
    23.         }
    24.         f := 0.0
    25.         for k := 0; k <= n; k++ {
    26.                 f += <-ch
    27.         }
    28.         return f
    29. }
    30.  
    31. func term(ch chan float64, k float64) {
    32.         ch <- 4 * math.Pow(-1, k) / (2*k + 1)
    33. }

    割圆术求圆周长或是圆周率,听的比较多,but,太不切实可行!不管公式怎么来的,总之有下面一个公式,本例子就是用这个公式求 π :

    例子中使用了并发求每单项的值,然后将每单项的值加起来,赶脚是,加的项数越多,便越接近与真值。

    go 中的管道(channel)提供了一种很方便的并发同步机制。管道是连接多个并发函数的通道,就像是水管,一端可以流进去,另一端可以流出去。这里多个函数/进程将每个单项的求值放到管道之后,谁先谁后是不确定的,然后另外一个函数/进程从管道中取值然后加起来,最后的结果便是就得的 π 的近似值。管道的同步操作已经内置,也就是不需要偶们自己去管来的。 go 是偶目前看到的写并发程序最容易的语言,么有之一!

  6. 并发通过筛选法求素数
    1. // A concurrent prime sieve
    2.  
    3. package main
    4.  
    5. // Send the sequence 2, 3, 4, ... to channel 'ch'.
    6. func Generate(ch chan<- int) {
    7.         for i := 2; ; i++ {
    8.                 ch <- i // Send 'i' to channel 'ch'.
    9.         }
    10. }
    11.  
    12. // Copy the values from channel 'in' to channel 'out',
    13. // removing those divisible by 'prime'.
    14. func Filter(in <-chan int, out chan<- int, prime int) {
    15.         for {
    16.                 i := <-in // Receive value from 'in'.
    17.                 if i%prime != 0 {
    18.                         out <- i // Send 'i' to 'out'.
    19.                 }
    20.         }
    21. }
    22.  
    23. // The prime sieve: Daisy-chain Filter processes.
    24. func main() {
    25.         ch := make(chan int) // Create a new channel.
    26.         go Generate(ch)      // Launch Generate goroutine.
    27.         for i := 0; i < 10; i++ {
    28.                 prime := <-ch
    29.                 print(prime, "\n")
    30.                 ch1 := make(chan int)
    31.                 go Filter(ch, ch1, prime)
    32.                 ch = ch1
    33.         }
    34. }
  7. 筛选法找素数是个不错的方法,2、3、4、5、6、7、8、9、10 ... 这些数中,第一个数 2 是素数,取出来,然后将 2 的倍数全部去掉;剩下的第一个数 3 还是素数,再去掉所有 3 的倍数,一直进行下去,就能找到很多素数。本例子也就是用的这个方法。

    具体逻辑是:第一个管道记录从2开始的自然数,取第一自然数/质数 2;然后第二个管道记录从第一个管道中过滤后的所有自然数,再取一个质数 3;第三个管道取第二个管道中过滤后的数,又得一个质数;所有的管道都是无限长的,只要程序嚒有终止,这些筛选/过滤便一直在进行着。

  8. 孔明棋
    1. // This program solves the (English) peg
    2. // solitaire board game.
    3.  
    4. package main
    5.  
    6. import "fmt"
    7.  
    8. const N = 11 + 1 // length of a row (+1 for \n)
    9.  
    10. // The board must be surrounded by 2 illegal
    11. // fields in each direction so that move()
    12. // doesn't need to check the board boundaries.
    13. // Periods represent illegal fields,
    14. // ● are pegs, and ○ are holes.
    15.  
    16. var board = []rune(
    17.         `...........
    18. ...........
    19. ....●●●....
    20. ....●●●....
    21. ..●●●●●●●..
    22. ..●●●○●●●..
    23. ..●●●●●●●..
    24. ....●●●....
    25. ....●●●....
    26. ...........
    27. ...........
    28. `)
    29.  
    30. // center is the position of the center hole if
    31. // there is a single one; otherwise it is -1.
    32. var center int
    33.  
    34. func init() {
    35.         n := 0
    36.         for pos, field := range board {
    37.                 if field == '○' {
    38.                         center = pos
    39.                         n++
    40.                 }
    41.         }
    42.         if n != 1 {
    43.                 center = -1 // no single hole
    44.         }
    45. }
    46.  
    47. var moves int // number of times move is called
    48.  
    49. // move tests if there is a peg at position pos that
    50. // can jump over another peg in direction dir. If the
    51. // move is valid, it is executed and move returns true.
    52. // Otherwise, move returns false.
    53. func move(pos, dir int) bool {
    54.         moves++
    55.         if board[pos] == '●' && board[pos+dir] == '●' && board[pos+2*dir] == '○' {
    56.                 board[pos] = '○'
    57.                 board[pos+dir] = '○'
    58.                 board[pos+2*dir] = '●'
    59.                 return true
    60.         }
    61.         return false
    62. }
    63.  
    64. // unmove reverts a previously executed valid move.
    65. func unmove(pos, dir int) {
    66.         board[pos] = '●'
    67.         board[pos+dir] = '●'
    68.         board[pos+2*dir] = '○'
    69. }
    70.  
    71. // solve tries to find a sequence of moves such that
    72. // there is only one peg left at the end; if center is
    73. // >= 0, that last peg must be in the center position.
    74. // If a solution is found, solve prints the board after
    75. // each move in a backward fashion (i.e., the last
    76. // board position is printed first, all the way back to
    77. // the starting board position).
    78. func solve() bool {
    79.         var last, n int
    80.         for pos, field := range board {
    81.                 // try each board position
    82.                 if field == '●' {
    83.                         // found a peg
    84.                         for _, dir := range [...]int{-1, -N, +1, +N} {
    85.                                 // try each direction
    86.                                 if move(pos, dir) {
    87.                                         // a valid move was found and executed,
    88.                                         // see if this new board has a solution
    89.                                         if solve() {
    90.                                                 unmove(pos, dir)
    91.                                                 println(string(board))
    92.                                                 return true
    93.                                         }
    94.                                         unmove(pos, dir)
    95.                                 }
    96.                         }
    97.                         last = pos
    98.                         n++
    99.                 }
    100.         }
    101.         // tried each possible move
    102.         if n == 1 && (center < 0 || last == center) {
    103.                 // there's only one peg left
    104.                 println(string(board))
    105.                 return true
    106.         }
    107.         // no solution found for this board
    108.         return false
    109. }
    110.  
    111. func main() {
    112.         if !solve() {
    113.                 fmt.Println("no solution found")
    114.         }
    115.         fmt.Println(moves, "moves tried")
    116. }

    例子嚒看懂,不做分析;

  9. 二叉排序树的比较
    1. // Go's concurrency primitives make it easy to
    2. // express concurrent concepts, such as
    3. // this binary tree comparison.
    4. //
    5. // Trees may be of different shapes,
    6. // but have the same contents. For example:
    7. //
    8. //        4               6
    9. //      2   6          4     7
    10. //     1 3 5 7       2   5
    11. //                  1 3
    12. //
    13. // This program compares a pair of trees by
    14. // walking each in its own goroutine,
    15. // sending their contents through a channel
    16. // to a third goroutine that compares them.
    17.  
    18. package main
    19.  
    20. import (
    21.         "fmt"
    22.         "math/rand"
    23. )
    24.  
    25. // A Tree is a binary tree with integer values.
    26. type Tree struct {
    27.         Left  *Tree
    28.         Value int
    29.         Right *Tree
    30. }
    31.  
    32. // Walk traverses a tree depth-first,
    33. // sending each Value on a channel.
    34. func Walk(t *Tree, ch chan int) {
    35.         if t == nil {
    36.                 return
    37.         }
    38.         Walk(t.Left, ch)
    39.         ch <- t.Value
    40.         Walk(t.Right, ch)
    41. }
    42.  
    43. // Walker launches Walk in a new goroutine,
    44. // and returns a read-only channel of values.
    45. func Walker(t *Tree) <-chan int {
    46.         ch := make(chan int)
    47.         go func() {
    48.                 Walk(t, ch)
    49.                 close(ch)
    50.         }()
    51.         return ch
    52. }
    53.  
    54. // Compare reads values from two Walkers
    55. // that run simultaneously, and returns true
    56. // if t1 and t2 have the same contents.
    57. func Compare(t1, t2 *Tree) bool {
    58.         c1, c2 := Walker(t1), Walker(t2)
    59.         for {
    60.                 v1, ok1 := <-c1
    61.                 v2, ok2 := <-c2
    62.                 if !ok1 || !ok2 {
    63.                         return ok1 == ok2
    64.                 }
    65.                 if v1 != v2 {
    66.                         break
    67.                 }
    68.         }
    69.         return false
    70. }
    71.  
    72. // New returns a new, random binary tree
    73. // holding the values 1k, 2k, ..., nk.
    74. func New(n, k int) *Tree {
    75.         var t *Tree
    76.         for _, v := range rand.Perm(n) {
    77.                 t = insert(t, (1+v)*k)
    78.         }
    79.         return t
    80. }
    81.  
    82. func insert(t *Tree, v int) *Tree {
    83.         if t == nil {
    84.                 return &Tree{nil, v, nil}
    85.         }
    86.         if v < t.Value {
    87.                 t.Left = insert(t.Left, v)
    88.                 return t
    89.         }
    90.         t.Right = insert(t.Right, v)
    91.         return t
    92. }
    93.  
    94. func main() {
    95.         t1 := New(100, 1)
    96.         fmt.Println(Compare(t1, New(100, 1)), "Same Contents")
    97.         fmt.Println(Compare(t1, New(99, 1)), "Differing Sizes")
    98.         fmt.Println(Compare(t1, New(100, 2)), "Differing Values")
    99.         fmt.Println(Compare(t1, New(101, 2)), "Dissimilar")
    100. }

    二叉排序树,首先是二叉树,其次,中序遍历的序列是有序序列。这里要比较两棵树是不是序列相同,只需要将中序遍历结果对比一下就可以了。

    管道放置二叉排序树的中序遍历结果;程序中貌似主要用了递归。

文章维护中...


有疑问加站长微信联系(非本文作者)

本文来自:陆仁贾个人站点

感谢作者:陆仁贾

查看原文:go官网的7个例子分析

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

12961 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传