go 语言或是 golang 的官网首页上有 7 个例子代码,赶脚灰常好,赶脚值得写篇文章简单分析一下 —— 虽然第 6 个例子不是太懂,貌似是个玩游戏的。
- hello,世界
- package main
- import "fmt"
- func main() {
- fmt.Println("Hello, 世界")
- }
这是hello,世界程序,要比hello,world其实要难一些,因为里面有中文!windows 下直接运行这个程序一般都有错误或是乱码出现,主要是编码的问题,倒不是程序的问题(请使用 utf-8 编码程序以及终端!)。关于安装以及其他细节可以看这篇文章—— 介绍 windows 下 go 环境的搭建,不仅仅是安装一下的问题。
从这个例子中,可以看出来,go 是通过 package(包)来实现功能或是模块分割的;控制台/终端输出要借助于 fmt 包中的 Println 函数,或是 Printf 函数;使用包之前,先导入!虽然你也许可以直接使用 print/println 函数,但是不能保证永远可以用!
go 支持单独的函数使用。所以,go 更像是 c !
程序从 main 包的 main 函数开始执行;但是 main 函数不接受参数(参数怎么处理?),没有返回值,而且貌似语句没有分号!分号实际跟 c 语言类似,是语句的分隔符,不过 go 有自动分号插入规则,所以很多时候,偶们都可以省略分号。
- 处理命令行参数
- package main
- import (
- // "fmt"
- "os"
- "flag" // command line option parser
- )
- var nFlag = flag.Bool("n", true, "末尾是否换行")
- func main() {
- flag.Parse(); // 解析命令行选项
- s := "";
- for i := 0; i < flag.NArg(); i++ {
- if i > 0 {
- s += " " // 单词之间用空格间隔开来
- }
- s += flag.Arg(i)
- }
- if *nFlag { // 换行与否
- s += "\n"
- }
- // fmt.Println(flag.Args());
- os.Stdout.WriteString(s);
- }
- // results in cmd:
- > go run hello.go -n=false good morning
- good morning
- > go run hello.go -n=true good morning
- good moring
- > go run hello.go good morning
- good moring
- >
这个是从上面的那个——如何处理命令行参数——问题衍生出来的程序。 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 包中的函数进行输出。
- 斐波拉契闭包
- package main
- // fib returns a function that returns
- // successive Fibonacci numbers.
- func fib() func() int {
- a, b := 0, 1
- return func() int {
- a, b = b, a+b
- return a
- }
- }
- func main() {
- f := fib()
- // Function calls are evaluated left-to-right.
- println(f(), f(), f(), f(), f())
- }
斐波拉契跟闭包其实嚒神马关系!斐波拉契数列是灰常出名的数列: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 表明是个函数,然后写函数名,加上参数,而函数的返回值类型写在后面;函数名如果么有的话,那就是匿名函数!
- 皮亚诺整数
- // Peano integers are represented by a linked
- // list whose nodes contain no data
- // (the nodes are the data).
- // This program demonstrates the power of Go's
- // segmented stacks when doing massively
- // recursive computations.
- package main
- import "fmt"
- // Number is a pointer to a Number
- type Number *Number
- // The arithmetic value of a Number is the
- // count of the nodes comprising the list.
- // (See the count function below.)
- // -------------------------------------
- // Peano primitives
- func zero() *Number {
- return nil
- }
- func isZero(x *Number) bool {
- return x == nil
- }
- func add1(x *Number) *Number {
- e := new(Number)
- *e = x
- return e
- }
- func sub1(x *Number) *Number {
- return *x
- }
- func add(x, y *Number) *Number {
- if isZero(y) {
- return x
- }
- return add(add1(x), sub1(y))
- }
- func mul(x, y *Number) *Number {
- if isZero(x) || isZero(y) {
- return zero()
- }
- return add(mul(x, sub1(y)), x)
- }
- func fact(n *Number) *Number {
- if isZero(n) {
- return add1(zero())
- }
- return mul(fact(sub1(n)), n)
- }
- // -------------------------------------
- // Helpers to generate/count Peano integers
- func gen(n int) *Number {
- if n > 0 {
- return add1(gen(n - 1))
- }
- return zero()
- }
- func count(x *Number) int {
- if isZero(x) {
- return 0
- }
- return count(sub1(x)) + 1
- }
- // -------------------------------------
- // Print i! for i in [0,9]
- func main() {
- for i := 0; i <= 9; i++ {
- f := count(fact(gen(i)))
- fmt.Println(i, "! =", f)
- }
- }
这是一个 n!的例子,但又不是单单的一个求 n! 的例子;这个例子让 e 们赶脚到了 —— 自然数可以构造出来或是一一数出来!“皮亚诺自然数公理”:0 是一个自然数;每个自然数后面都跟有一个自然数;除 0 之外,每个自然数前面都有一个自然数;—— 0 很特殊,是第一个自然数,因为它前面没有自然数!同时例子中也反映了:阶乘依赖于自然数乘法;自然数乘法依赖于自然数加法,而自然数加法其实是个递归定义。
自然数就是一个链表,链表的第一个元素代表 0 ,第二个元素代表 1,第三个 代表 2,一直下去。10 就是一个长度为 10 的链表!求 10!的思路不再是循环一下,乘一下!而是,构造出一个长度是 10! 的链表,然后 count 一下长度,就是 10!多此一举?!这里所有的操作只建立在“加一”、“减一”和“递归”的基础之上。
- 并发求圆周率 π
- // Concurrent computation of pi.
- // See http://goo.gl/ZuTZM.
- //
- // This demonstrates Go's ability to handle
- // large numbers of concurrent processes.
- // It is an unreasonable way to calculate pi.
- package main
- import (
- "fmt"
- "math"
- )
- func main() {
- fmt.Println(pi(5000))
- }
- // pi launches n goroutines to compute an
- // approximation of pi.
- func pi(n int) float64 {
- ch := make(chan float64)
- for k := 0; k <= n; k++ {
- go term(ch, float64(k))
- }
- f := 0.0
- for k := 0; k <= n; k++ {
- f += <-ch
- }
- return f
- }
- func term(ch chan float64, k float64) {
- ch <- 4 * math.Pow(-1, k) / (2*k + 1)
- }
割圆术求圆周长或是圆周率,听的比较多,but,太不切实可行!不管公式怎么来的,总之有下面一个公式,本例子就是用这个公式求 π :
例子中使用了并发求每单项的值,然后将每单项的值加起来,赶脚是,加的项数越多,便越接近与真值。
go 中的管道(channel)提供了一种很方便的并发同步机制。管道是连接多个并发函数的通道,就像是水管,一端可以流进去,另一端可以流出去。这里多个函数/进程将每个单项的求值放到管道之后,谁先谁后是不确定的,然后另外一个函数/进程从管道中取值然后加起来,最后的结果便是就得的 π 的近似值。管道的同步操作已经内置,也就是不需要偶们自己去管来的。 go 是偶目前看到的写并发程序最容易的语言,么有之一!
- 并发通过筛选法求素数
- // A concurrent prime sieve
- package main
- // Send the sequence 2, 3, 4, ... to channel 'ch'.
- func Generate(ch chan<- int) {
- for i := 2; ; i++ {
- ch <- i // Send 'i' to channel 'ch'.
- }
- }
- // Copy the values from channel 'in' to channel 'out',
- // removing those divisible by 'prime'.
- func Filter(in <-chan int, out chan<- int, prime int) {
- for {
- i := <-in // Receive value from 'in'.
- if i%prime != 0 {
- out <- i // Send 'i' to 'out'.
- }
- }
- }
- // The prime sieve: Daisy-chain Filter processes.
- func main() {
- ch := make(chan int) // Create a new channel.
- go Generate(ch) // Launch Generate goroutine.
- for i := 0; i < 10; i++ {
- prime := <-ch
- print(prime, "\n")
- ch1 := make(chan int)
- go Filter(ch, ch1, prime)
- ch = ch1
- }
- }
- 孔明棋
- // This program solves the (English) peg
- // solitaire board game.
- package main
- import "fmt"
- const N = 11 + 1 // length of a row (+1 for \n)
- // The board must be surrounded by 2 illegal
- // fields in each direction so that move()
- // doesn't need to check the board boundaries.
- // Periods represent illegal fields,
- // ● are pegs, and ○ are holes.
- var board = []rune(
- `...........
- ...........
- ....●●●....
- ....●●●....
- ..●●●●●●●..
- ..●●●○●●●..
- ..●●●●●●●..
- ....●●●....
- ....●●●....
- ...........
- ...........
- `)
- // center is the position of the center hole if
- // there is a single one; otherwise it is -1.
- var center int
- func init() {
- n := 0
- for pos, field := range board {
- if field == '○' {
- center = pos
- n++
- }
- }
- if n != 1 {
- center = -1 // no single hole
- }
- }
- var moves int // number of times move is called
- // move tests if there is a peg at position pos that
- // can jump over another peg in direction dir. If the
- // move is valid, it is executed and move returns true.
- // Otherwise, move returns false.
- func move(pos, dir int) bool {
- moves++
- if board[pos] == '●' && board[pos+dir] == '●' && board[pos+2*dir] == '○' {
- board[pos] = '○'
- board[pos+dir] = '○'
- board[pos+2*dir] = '●'
- return true
- }
- return false
- }
- // unmove reverts a previously executed valid move.
- func unmove(pos, dir int) {
- board[pos] = '●'
- board[pos+dir] = '●'
- board[pos+2*dir] = '○'
- }
- // solve tries to find a sequence of moves such that
- // there is only one peg left at the end; if center is
- // >= 0, that last peg must be in the center position.
- // If a solution is found, solve prints the board after
- // each move in a backward fashion (i.e., the last
- // board position is printed first, all the way back to
- // the starting board position).
- func solve() bool {
- var last, n int
- for pos, field := range board {
- // try each board position
- if field == '●' {
- // found a peg
- for _, dir := range [...]int{-1, -N, +1, +N} {
- // try each direction
- if move(pos, dir) {
- // a valid move was found and executed,
- // see if this new board has a solution
- if solve() {
- unmove(pos, dir)
- println(string(board))
- return true
- }
- unmove(pos, dir)
- }
- }
- last = pos
- n++
- }
- }
- // tried each possible move
- if n == 1 && (center < 0 || last == center) {
- // there's only one peg left
- println(string(board))
- return true
- }
- // no solution found for this board
- return false
- }
- func main() {
- if !solve() {
- fmt.Println("no solution found")
- }
- fmt.Println(moves, "moves tried")
- }
例子嚒看懂,不做分析;
- 二叉排序树的比较
- // Go's concurrency primitives make it easy to
- // express concurrent concepts, such as
- // this binary tree comparison.
- //
- // Trees may be of different shapes,
- // but have the same contents. For example:
- //
- // 4 6
- // 2 6 4 7
- // 1 3 5 7 2 5
- // 1 3
- //
- // This program compares a pair of trees by
- // walking each in its own goroutine,
- // sending their contents through a channel
- // to a third goroutine that compares them.
- package main
- import (
- "fmt"
- "math/rand"
- )
- // A Tree is a binary tree with integer values.
- type Tree struct {
- Left *Tree
- Value int
- Right *Tree
- }
- // Walk traverses a tree depth-first,
- // sending each Value on a channel.
- func Walk(t *Tree, ch chan int) {
- if t == nil {
- return
- }
- Walk(t.Left, ch)
- ch <- t.Value
- Walk(t.Right, ch)
- }
- // Walker launches Walk in a new goroutine,
- // and returns a read-only channel of values.
- func Walker(t *Tree) <-chan int {
- ch := make(chan int)
- go func() {
- Walk(t, ch)
- close(ch)
- }()
- return ch
- }
- // Compare reads values from two Walkers
- // that run simultaneously, and returns true
- // if t1 and t2 have the same contents.
- func Compare(t1, t2 *Tree) bool {
- c1, c2 := Walker(t1), Walker(t2)
- for {
- v1, ok1 := <-c1
- v2, ok2 := <-c2
- if !ok1 || !ok2 {
- return ok1 == ok2
- }
- if v1 != v2 {
- break
- }
- }
- return false
- }
- // New returns a new, random binary tree
- // holding the values 1k, 2k, ..., nk.
- func New(n, k int) *Tree {
- var t *Tree
- for _, v := range rand.Perm(n) {
- t = insert(t, (1+v)*k)
- }
- return t
- }
- func insert(t *Tree, v int) *Tree {
- if t == nil {
- return &Tree{nil, v, nil}
- }
- if v < t.Value {
- t.Left = insert(t.Left, v)
- return t
- }
- t.Right = insert(t.Right, v)
- return t
- }
- func main() {
- t1 := New(100, 1)
- fmt.Println(Compare(t1, New(100, 1)), "Same Contents")
- fmt.Println(Compare(t1, New(99, 1)), "Differing Sizes")
- fmt.Println(Compare(t1, New(100, 2)), "Differing Values")
- fmt.Println(Compare(t1, New(101, 2)), "Dissimilar")
- }
二叉排序树,首先是二叉树,其次,中序遍历的序列是有序序列。这里要比较两棵树是不是序列相同,只需要将中序遍历结果对比一下就可以了。
管道放置二叉排序树的中序遍历结果;程序中貌似主要用了递归。
筛选法找素数是个不错的方法,2、3、4、5、6、7、8、9、10 ... 这些数中,第一个数 2 是素数,取出来,然后将 2 的倍数全部去掉;剩下的第一个数 3 还是素数,再去掉所有 3 的倍数,一直进行下去,就能找到很多素数。本例子也就是用的这个方法。
具体逻辑是:第一个管道记录从2开始的自然数,取第一自然数/质数 2;然后第二个管道记录从第一个管道中过滤后的所有自然数,再取一个质数 3;第三个管道取第二个管道中过滤后的数,又得一个质数;所有的管道都是无限长的,只要程序嚒有终止,这些筛选/过滤便一直在进行着。
文章维护中...
有疑问加站长微信联系(非本文作者)