分享
  1. 首页
  2. 文章

go官网的7个例子分析

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

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

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

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

    这个是从上面的那个——如何处理命令行参数——问题衍生出来的程序。 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. // fib returns a function that returns
    3. // successive Fibonacci numbers.
    4. func fib() func() int {
    5. a, b := 0, 1
    6. return func() int {
    7. a, b = b, a+b
    8. return a
    9. }
    10. }
    11. func main() {
    12. f := fib()
    13. // Function calls are evaluated left-to-right.
    14. println(f(), f(), f(), f(), f())
    15. }

    斐波拉契跟闭包其实嚒神马关系!斐波拉契数列是灰常出名的数列: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. // This program demonstrates the power of Go's
    5. // segmented stacks when doing massively
    6. // recursive computations.
    7. package main
    8. import "fmt"
    9. // Number is a pointer to a Number
    10. type Number *Number
    11. // The arithmetic value of a Number is the
    12. // count of the nodes comprising the list.
    13. // (See the count function below.)
    14. // -------------------------------------
    15. // Peano primitives
    16. func zero() *Number {
    17. return nil
    18. }
    19. func isZero(x *Number) bool {
    20. return x == nil
    21. }
    22. func add1(x *Number) *Number {
    23. e := new(Number)
    24. *e = x
    25. return e
    26. }
    27. func sub1(x *Number) *Number {
    28. return *x
    29. }
    30. func add(x, y *Number) *Number {
    31. if isZero(y) {
    32. return x
    33. }
    34. return add(add1(x), sub1(y))
    35. }
    36. func mul(x, y *Number) *Number {
    37. if isZero(x) || isZero(y) {
    38. return zero()
    39. }
    40. return add(mul(x, sub1(y)), x)
    41. }
    42. func fact(n *Number) *Number {
    43. if isZero(n) {
    44. return add1(zero())
    45. }
    46. return mul(fact(sub1(n)), n)
    47. }
    48. // -------------------------------------
    49. // Helpers to generate/count Peano integers
    50. func gen(n int) *Number {
    51. if n> 0 {
    52. return add1(gen(n - 1))
    53. }
    54. return zero()
    55. }
    56. func count(x *Number) int {
    57. if isZero(x) {
    58. return 0
    59. }
    60. return count(sub1(x)) + 1
    61. }
    62. // -------------------------------------
    63. // Print i! for i in [0,9]
    64. func main() {
    65. for i := 0; i <= 9; i++ {
    66. f := count(fact(gen(i)))
    67. fmt.Println(i, "! =", f)
    68. }
    69. }

    这是一个 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. import (
    8. "fmt"
    9. "math"
    10. )
    11. func main() {
    12. fmt.Println(pi(5000))
    13. }
    14. // pi launches n goroutines to compute an
    15. // approximation of pi.
    16. func pi(n int) float64 {
    17. ch := make(chan float64)
    18. for k := 0; k <= n; k++ {
    19. go term(ch, float64(k))
    20. }
    21. f := 0.0
    22. for k := 0; k <= n; k++ {
    23. f += <-ch
    24. }
    25. return f
    26. }
    27. func term(ch chan float64, k float64) {
    28. ch <- 4 * math.Pow(-1, k) / (2*k + 1)
    29. }

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

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

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

  6. 并发通过筛选法求素数
    1. // A concurrent prime sieve
    2. package main
    3. // Send the sequence 2, 3, 4, ... to channel 'ch'.
    4. func Generate(ch chan<- int) {
    5. for i := 2; ; i++ {
    6. ch <- i // Send 'i' to channel 'ch'.
    7. }
    8. }
    9. // Copy the values from channel 'in' to channel 'out',
    10. // removing those divisible by 'prime'.
    11. func Filter(in <-chan int, out chan<- int, prime int) {
    12. for {
    13. i := <-in // Receive value from 'in'.
    14. if i%prime != 0 {
    15. out <- i // Send 'i' to 'out'.
    16. }
    17. }
    18. }
    19. // The prime sieve: Daisy-chain Filter processes.
    20. func main() {
    21. ch := make(chan int) // Create a new channel.
    22. go Generate(ch) // Launch Generate goroutine.
    23. for i := 0; i < 10; i++ {
    24. prime := <-ch
    25. print(prime, "\n")
    26. ch1 := make(chan int)
    27. go Filter(ch, ch1, prime)
    28. ch = ch1
    29. }
    30. }
  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. package main
    4. import "fmt"
    5. const N = 11 + 1 // length of a row (+1 for \n)
    6. // The board must be surrounded by 2 illegal
    7. // fields in each direction so that move()
    8. // doesn't need to check the board boundaries.
    9. // Periods represent illegal fields,
    10. // くろまる are pegs, and しろまる are holes.
    11. var board = []rune(
    12. `...........
    13. ...........
    14. ....くろまるくろまるくろまる....
    15. ....くろまるくろまるくろまる....
    16. ..くろまるくろまるくろまるくろまるくろまるくろまるくろまる..
    17. ..くろまるくろまるくろまるしろまるくろまるくろまるくろまる..
    18. ..くろまるくろまるくろまるくろまるくろまるくろまるくろまる..
    19. ....くろまるくろまるくろまる....
    20. ....くろまるくろまるくろまる....
    21. ...........
    22. ...........
    23. `)
    24. // center is the position of the center hole if
    25. // there is a single one; otherwise it is -1.
    26. var center int
    27. func init() {
    28. n := 0
    29. for pos, field := range board {
    30. if field == 'しろまる' {
    31. center = pos
    32. n++
    33. }
    34. }
    35. if n != 1 {
    36. center = -1 // no single hole
    37. }
    38. }
    39. var moves int // number of times move is called
    40. // move tests if there is a peg at position pos that
    41. // can jump over another peg in direction dir. If the
    42. // move is valid, it is executed and move returns true.
    43. // Otherwise, move returns false.
    44. func move(pos, dir int) bool {
    45. moves++
    46. if board[pos] == 'くろまる' && board[pos+dir] == 'くろまる' && board[pos+2*dir] == 'しろまる' {
    47. board[pos] = 'しろまる'
    48. board[pos+dir] = 'しろまる'
    49. board[pos+2*dir] = 'くろまる'
    50. return true
    51. }
    52. return false
    53. }
    54. // unmove reverts a previously executed valid move.
    55. func unmove(pos, dir int) {
    56. board[pos] = 'くろまる'
    57. board[pos+dir] = 'くろまる'
    58. board[pos+2*dir] = 'しろまる'
    59. }
    60. // solve tries to find a sequence of moves such that
    61. // there is only one peg left at the end; if center is
    62. //>= 0, that last peg must be in the center position.
    63. // If a solution is found, solve prints the board after
    64. // each move in a backward fashion (i.e., the last
    65. // board position is printed first, all the way back to
    66. // the starting board position).
    67. func solve() bool {
    68. var last, n int
    69. for pos, field := range board {
    70. // try each board position
    71. if field == 'くろまる' {
    72. // found a peg
    73. for _, dir := range [...]int{-1, -N, +1, +N} {
    74. // try each direction
    75. if move(pos, dir) {
    76. // a valid move was found and executed,
    77. // see if this new board has a solution
    78. if solve() {
    79. unmove(pos, dir)
    80. println(string(board))
    81. return true
    82. }
    83. unmove(pos, dir)
    84. }
    85. }
    86. last = pos
    87. n++
    88. }
    89. }
    90. // tried each possible move
    91. if n == 1 && (center < 0 || last == center) {
    92. // there's only one peg left
    93. println(string(board))
    94. return true
    95. }
    96. // no solution found for this board
    97. return false
    98. }
    99. func main() {
    100. if !solve() {
    101. fmt.Println("no solution found")
    102. }
    103. fmt.Println(moves, "moves tried")
    104. }

    例子嚒看懂,不做分析;

  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. package main
    18. import (
    19. "fmt"
    20. "math/rand"
    21. )
    22. // A Tree is a binary tree with integer values.
    23. type Tree struct {
    24. Left *Tree
    25. Value int
    26. Right *Tree
    27. }
    28. // Walk traverses a tree depth-first,
    29. // sending each Value on a channel.
    30. func Walk(t *Tree, ch chan int) {
    31. if t == nil {
    32. return
    33. }
    34. Walk(t.Left, ch)
    35. ch <- t.Value
    36. Walk(t.Right, ch)
    37. }
    38. // Walker launches Walk in a new goroutine,
    39. // and returns a read-only channel of values.
    40. func Walker(t *Tree) <-chan int {
    41. ch := make(chan int)
    42. go func() {
    43. Walk(t, ch)
    44. close(ch)
    45. }()
    46. return ch
    47. }
    48. // Compare reads values from two Walkers
    49. // that run simultaneously, and returns true
    50. // if t1 and t2 have the same contents.
    51. func Compare(t1, t2 *Tree) bool {
    52. c1, c2 := Walker(t1), Walker(t2)
    53. for {
    54. v1, ok1 := <-c1
    55. v2, ok2 := <-c2
    56. if !ok1 || !ok2 {
    57. return ok1 == ok2
    58. }
    59. if v1 != v2 {
    60. break
    61. }
    62. }
    63. return false
    64. }
    65. // New returns a new, random binary tree
    66. // holding the values 1k, 2k, ..., nk.
    67. func New(n, k int) *Tree {
    68. var t *Tree
    69. for _, v := range rand.Perm(n) {
    70. t = insert(t, (1+v)*k)
    71. }
    72. return t
    73. }
    74. func insert(t *Tree, v int) *Tree {
    75. if t == nil {
    76. return &Tree{nil, v, nil}
    77. }
    78. if v < t.Value {
    79. t.Left = insert(t.Left, v)
    80. return t
    81. }
    82. t.Right = insert(t.Right, v)
    83. return t
    84. }
    85. func main() {
    86. t1 := New(100, 1)
    87. fmt.Println(Compare(t1, New(100, 1)), "Same Contents")
    88. fmt.Println(Compare(t1, New(99, 1)), "Differing Sizes")
    89. fmt.Println(Compare(t1, New(100, 2)), "Differing Values")
    90. fmt.Println(Compare(t1, New(101, 2)), "Dissimilar")
    91. }

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

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

文章维护中...


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

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

感谢作者:陆仁贾

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

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

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

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏