进阶[1]

  1. 当像这样增加一个值的时候,什么类型可以工作?

…这里没有指针运算, 因此如果这样写: *p++, 它被解释为 (*p)++:首先解析引用然后增加值。

答案:这仅能工作于指向数字(int, uint 等等)的指针值。

  1. 为什么它不能工作在所有类型上?

答案:++ 仅仅定义在数字类型上,同时由于在 Go 中没有运算符重载,所以会

在其他类型上失败(编译错误)。

  1. 使用练习 Q12 的答案,利用 interface 使其更加通用。让它至少能同时工

作于 int 和 string。

package main

import "fmt"
//一个空的interface{}
type e interface {

}
//类型判断函数
func mult2(f e) e {
	switch f.(type) {
	case int:
		return f.(int) * 2
	case string:
		return f.(string) + f.(string) + f.(string) + f.(string)

	}

	return f
}

func Map (n []e, f func (e) e) []e {
	m := make([]e, len(n))

	for k, v := range n {
		m[k] = f(v)
	}
	return m
}

func main() {
	m := []e {1,2,3,4,5}
	s := []e {"1", "2", "3"}

	mf := Map(m, mult2)

	sf := Map(s, mult2)

	fmt.Printf("%v\n", mf)
	fmt.Printf("%v\n", sf)

}
  1. 假设定义了下面的结构:
type Person struct {
  name string 
  age int 
}

下面两行之间的区别是什么?

var p1 Person 
p2 := new(Person)

答案:第一行:var p1 Person 分配了 Person值 给 p1。p1 的类型是Person

第二行:p2 := new(Person)分配了内存并且将指针赋值给 p2。 p2 的类型 是 *Person

  1. 下面两个内存分配的区别是什么?
func Set(t *T) { x = t }
//和
func Set(t T) { x= &t }

答案:在第二个函数中,x 指向一个新的(堆上分配的)变量 t,其包含了实际 参数值的副本。 在第一个函数中,x 指向了 t 指向的内容,也就是实际上的参数指向的 内容。 因此在第二个函数,我们有了 “额外” 的变量存储了相关值的副本。

  1. 使用 container/list 包创建(双向)链表。将值 1,2 和 4 存入并打印。
import container/list
import fmt
func main() {
	l := list.New()
	l.PushBack(1)
	l.PushBack(2)
	
	for i := l.Front(); i != nil; i = i.Next() {
		fmt.Print(i.Value)
	}
}
  1. 自行实现链表。

--

package main

import (
	"errors"
	"fmt"
)

type Value int

type myList struct {
	//包含起始节点和尾节点
	head, tail *Node
}


//每一个节点
type Node struct {
	Value //数据
	//和指向前一个节点和后一个节点
	next, prev *Node
}


//起始部
func (l *myList) Front() *Node {
	return l.head
}

//下一个节点
func (n *Node) Next() *Node {
	return n.next
}
//压入一个元素
func (l *myList) Push(v Value) *myList {
	//用这个元素创建一个节点等待插入
	n := &Node{Value:v}

	//如果头部为空的话,把这个元素放到头节点
	if l.head == nil {
		l.head = n
	} else {
		//否则就是把这个元素放到尾节点的下一个节点
		l.tail.next = n
		//确保新节点指向先前存在的节点
		n.prev = l.tail
	}
	//扩充链表尾部
	l.tail = n
	return l

}

var ListEmptyErr = errors.New("list is empty")

func (l *myList) Pop() (v Value, err error) {
	//如果尾部是空的说明list已空
	if l.tail == nil {
		err = ListEmptyErr
	} else {
		//从尾部取值
		v = l.tail.Value
		//然后摘除这个node,缩小链表尾部
		l.tail = l.tail.prev
		if l.tail == nil {
			l.head = nil
		}
	}

	return v, err
}

func main()  {
	l := new(myList)

	l.Push(1)
	l.Push(2)
	l.Push(5)

	for n := l.Front(); n != nil; n = n.Next() {
		fmt.Print(n.Value)
	}

	for v, err := l.Pop(); err == nil; v, err = l.Pop() {
		fmt.Print(v)
	}
}
  1. 编写一个程序,模仿 Unix 的 cat 程序(待测试)。
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
)

var numberFlag = flag.Bool("n", false, "number each line")

func cat(r *bufio.Reader) {
	i := 1

	for {
		buf, err := r.ReadBytes('\n')
		//文件结束的话
		if err == io.EOF {
			break
		}
		//如果需要显示行号
		if *numberFlag {
			fmt.Fprintf(os.Stdout, "%5d %s", i, buf)
		} else {
			fmt.Fprintf(os.Stdout, "%s", buf)
		}
	}
	return
}

func main() {
	//参数匹配
	flag.Parse()
	//没有参数的话
	if flag.NArg() == 0 {
		cat(bufio.NewReader(os.Stdin))
	}

	for i := 0; i < flag.NArg(); i ++ {
		//打开需要打开的文件
		f, err := os.Open(flag.Arg(i))

		if err != nil {
			fmt.Fprintf(os.Stderr, "%s error reading from %s: %s\n",
				os.Args[0], flag.Arg(i), err.Error())
			continue
		}
		cat(bufio.NewReader(f))
	}
}

书本链接learning go

下一章是接口ヽ( ̄▽ ̄)و


  1. 题图出处 ↩︎