brew link python3 出错


$ brew install python3
Warning: python3 3.6.3 is already installed, it's just not linked.
You can use `brew link python3` to link this version.
$ brew link python3
Linking /usr/local/Cellar/python3/3.6.3... Error: Permission denied @ dir_s_mkdir - /usr/local/Frameworks

发现 /usr/local/ 下没有路径 /usr/local/Frameworks,需要新建该路径,并修改权限。

解决:

$ sudo mkdir /usr/local/Frameworks
$ sudo chown $(whoami):admin /usr/local/Frameworks

成功:

$ brew link python3
Linking /usr/local/Cellar/python3/3.6.3... 1 symlinks created
阅读全文

HTTP 传输内容的压缩


HTTP 压缩,在 HTTP 协议中,其实是内容编码的一种。
在 HTTP 协议中,可以对内容(也就是 body 部分)进行编码,可以采用 gzip 这样的编码,从而达到压缩的目的。也可以使用其他的编码把内容搅乱或加密,以此来防止未授权的第三方看到文档的内容。
所以 HTTP 压缩,其实就是 HTTP 内容编码的一种。
HTTP 压缩是指: Web 服务器和浏览器之间压缩传输的"文本内容"的方法。HTTP 采用通用的压缩算法,比如 gzip 来压缩
HTML、JavaScript、CSS 文件。能大大减少网络传输的数据量,提高了用户显示网页的速度。
首先,浏览器发送一个请求(request)给 Web 服务器,支持一个压缩格式如(gzip),服务端会将原来的源码压缩之后,通过
HTTP 响应(response)信息返回给 Web 浏览器,浏览器接收之后,显示出来。

HTTP 压缩的过程:
1.浏览器发送 HTTP Request 给 Web 服务器,Request 中有 Accept-Encoding:gzip、deflate。
2.Web 服务器接到 Request 后,生成原始的 Response,其中有原始的 Content-Type 和 Content-Length。
3.Web 服务器通过 gzip,来对 Response 进行编码,编码后 header 中有 Content-Type 和 Content-Length(压缩后的大小),并且增加了 Content-Encoding:gzip,然后把 Response 发送给浏览器。
4.浏览器接到 Response 后,根据 Content-Encoding:gzip 来对 Response 进行解码。获取到原始 Response 后,然后显示出网页。











阅读全文

Go 单例模式


Lazy Loading:

type singleton struct {
}

// private
var instance *singleton

// public
func GetInstance() *singleton {
    if instance == nil {
        // not thread safe
        instance = &singleton{}
    }
    return instance
}

带锁:

type singleton struct {
}

var instance *singleton
var mu sync.Mutex

func GetInstance() *singleton {
    mu.Lock()
    defer mu.Unlock()
    if instance == nil {
        // unnecessary locking if instance already created
        instance = &singleton{}
    }
    return instance
}

带检查锁:

// <-- Not yet perfect. since it's not fully atomic
if instance == nil {
    mu.Lock()
    defer mu.Unlock()
    if instance == nil {
        instance = &singleton{}
    }
}
return instance

阅读全文

Go 语言小细节(一)


多个 defer 出现的时候,多个 defer 之间按照 LIFO(后进先出)的顺序执行

package main
import "fmt"
func main(){
    defer func(){
        fmt.Println("1")
    }()
    defer func(){
        fmt.Println("2")
    }()
    defer func(){
        fmt.Println("3")
    }()
}

对应的输出是:

3
2
1

阅读全文

Go 单元测试


Go 语言中自带有一个轻量级的测试框架 testing 和自带的 go test 命令来实现单元测试和性能测试。
实际操作就是建立一个 gotest 目录。在该目录下面创建两个文件:gotest.go 和 gotest_test.go。
gotest.go 这个文件里面我们是创建了一个包,里面有一个函数实现了除法运算:

package gotest

import (
    "errors"
)
// 除法函数
func Division(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为0")
    }
    return a / b, nil
}

gotest_test.go 这是我们的单元测试文件,但是记住下面的这些原则:

  • 文件名必须是 _test.go 结尾的,这样在执行 go test 的时候才会执行到相应的代码。
  • 你必须 import testing 这个包。
  • 所有的测试用例函数必须是 Test 开头。
  • 测试用例会按照源代码中写的顺序依次执行。
  • 测试函数 TestXxx() 的参数是 testing.T,我们可以使用该类型来记录错误或者是测试状态。

测试格式:
func TestXxx (t *testing.T),Xxx 部分可以为任意的字母数字的组合,但是首字母不能是小写字母 [a-z],例如
Testintdiv 是错误的函数名。函数中通过调用 testing.T 的 Error,Errorf,FailNow,Fatal,FatalIf 方法,说明测试不通过,调用 Log 方法用来记录测试的信息。





阅读全文

Go 语言小细节(二)


数组用于函数传参时是值复制
方法或函数调用时,传入参数都是值复制,跟赋值一致,除非是 map、slice、channel、指针类型这些特殊类型是引用传递。

x := [3]int{1,2,3}

数组在函数中传参是值复制

func(arr [3]int) {
    arr[0] = 7
    fmt.Println(arr)
    // [7 2 3]
}(x)
fmt.Println(x)
// [1 2 3]

使用数组指针实现引用传参

func(arr *[3]int) {
    (*arr)[0] = 7
    fmt.Println(arr)
    // &[7 2 3]
}(&x)
fmt.Println(x)
// [7 2 3]

阅读全文

Go 实现简单的 Queue 队列


需求
队列的特性较为单一,基本操作即初始化、获取大小、添加元素、移除元素等。最重要的特性就是满足先进先出。

实现
接下来还是按照以前的套路,一步一步来分析如何利用Go的语法特性实现Queue这种数据结构。

定义
首先定义每个节点 Node 结构体,照例 Value 的值类型可以是任意类型,节点的前后指针域指针类型为 Node

type node struct {
    value interface{}
    prev *node
    next *node
}

继续定义链表结构,定义出头结点和尾节点的指针,同时定义队列大小 size:

type LinkedQueue struct {
    head *node
    tail *node
    size int
}



阅读全文

Go 实现简单的 Set


需求
对于 Set 类型的数据结构,其实本质上跟 List 没什么多大的区别。无非是 Set 不能含有重复的 Item 的特性,Set 有初始化、Add、Clear、Remove、Contains 等操作。

Go中Map的数据结构,Key是不允许重复的:

m := map[string]string{
    "1": "one",
    "2": "two",
    "1": "one",
    "3": "three",
}
fmt.Println(m)

程序会直接报错,提示重复 Key 值,这样就非常符合 Set 的特性需求了。

定义
前面分析出 Set 的 Value 为固定的值,用一个常量替代即可。但是笔者分析的实现源码,用的是一个空结构体来实现的,如下所示:

// 空结构体
var Exists = struct{}{}
// Set is the main interface
type Set struct {
    // struct 为结构体类型的变量
    m map[interface{}]struct{}
}


阅读全文

Go 语言小细节(四)


map 的容量
你可以在 map 创建时指定它的容量,但你无法在 map 上使用 cap() 函数。

package main
func main() {  
    m := make(map[string]int,99)
    cap(m)
}

匿名函数作用域陷阱

import (
    "fmt"
)

func main(){
    var msgs []func()
    array := []string{
        "1", "2", "3", "4",
    }
    for _, e := range array{
        msgs = append(msgs, func(){
            fmt.Println(e)
        })
    }
    for _, v := range msgs{
        v()
    }
}

阅读全文