A标签默认是inline元素,所以标签内容的长短决定了鼠标hover的范围,在有些时候,比如某些导航列表中,需要hover父标签(li)时能hover到a标签。

在制作一个API文档的侧边栏导航列表的时候,html大概是这样的

<div class="toc-macro absolute">
<ul class="toc-indentation">
<li><a href="#id-1">1. 绑定手机号</a></li>
<li><a href="#id-2">2. 查询手机号码是否可以绑定</a></li>
<li><a href="#id-3">3. 添加邮箱</a></li>
<li><a href="#id-4">4. 检查邮箱是否可以添加</a></li>
</ul>
</div>

其他的样式就不具体写了,其实现的效果是在右侧边栏实现一个列表导航。一些主要的样式大概如下:

.toc-macro{ position: fixed; top: 100px; }
.toc-macro ul {list-style-type:none; }
.toc-macro a { text-decoration: none; }
.toc-macro li { padding: 6px 8px 2px;text-decoration:none; }
li:hover { background-color: #efefef;color: #7DB75C; }
.toc-macro a:hover { background-color: #efefef;color: #7DB75C; }

其中使用了决定定位时导航不会随着滚动条而滚动,使我们在查看API文档时总是能看到这个导航。
这样子基本符合我们的需求了,但是有一个小小的问题,那就是鼠标放到导航上如果没有放到a链接上面,li标签会变色,但是由于鼠标没有在a上所以还是箭头的形状。。。这个小小的瑕疵需要解决一下。

首先我们想到的是把a标签的宽度设置到 100%:

.toc-macro a { display:inline-block; width: 100%; }

但是这样似乎并没有按照我们所想的表现出来。我们需要再加一些东西,让a标签的inline-block可以把宽度填充

.toc-macro li { vertical-align: middle; line-height: 15px;}

另外还有一种实现思路那就是把div下所有标签的display属性设置为block,同时a的height设置为100%:

.toc-macro ul { display: block; }
.toc-macro li { display: block;}
.toc-macro a { display:block; height:100%; }

这样实现应该也是可行的,不过没有测试过了。

Go语言提供了一套“新颖的”错误系统,与Java中的异常系统不一样,Go中没有抛出异常的概念

基础知识

如果有一定面向对象语言编程经验的话,肯定会对异常/错误系统有一定的了解。如果使用Java比较多的话,一定会认为抛异常是编程语言天生的一部分,因为在Java中抛异常真的是太家常便饭,无处不在了;但是在Go语言中,根本没有异常这个东西!

看一段官方的QA是怎么解释这个问题的:

We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.

Go takes a different approach. For plain error handling, Go’s multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go’s other features, makes error handling pleasant but quite different from that in other languages.

Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function’s state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.

多值返回为Go提供了一个新的错误处理机制,如果对为什么Go不提供异常体系感兴趣可以在 这里 找到更多的细节。

在之前文章中,已经使用过Go语言的多值返回来返回详细的错误描述。按照规定,错误的类型通常为error,这是一个内建的简单接口

type error interface {
Error() string
}

可以通过实现这个接口定制错误,例如打开文件时的错误:

type PathError struct {
Op string
Path string
Err error // 由系统调用返回
}

fanc (pe *PathError) Error() string {
// 返回定制的错误信息
return pe.Op + " " + pe.Path + ": " + pe.Err.Error()
}

effective go中有一个示例非常有利于我们理解错误的使用,这段代码的作用是创建一个文件,如果创建失败,检查是不是磁盘空间不足,如果是则删除掉一些临时文件,再尝试创建一次:

for try:=0; try<2; try++ {
file, err := os.Create(filename)
if err == nill {
return //没有错误
}
// 检查是不是空间不足
if e,ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC {
deleteTempFiles() // 删除一些临时文件
continue // 再创建一次
}
return
}

可以查看源码中PathError的实现 https://golang.org/src/os/error.go

Panic函数

函数可以通过将error作为额外的返回值来向调用者报告错误,对于一些“致命”的错误,他们导致程序不能继续运行了,Go内建了一个Panic函数,它会产生一个运行时错误并重孩子程序。该函数接受一个任意类型的实参在程序终止时打印。

只需要在需要它的地方调用一下panix()就可以了。

func init() {
if user == "" {
panic("user变量必须有值")
}
}

这是一个示例,但是如果是设计库函数则应该避免使用panic,一个常见的使用panic的场景是初始化,如上面的示例。

恢复

panic函数被调用后,程序将终止当前函数的执行。并开始追溯Go程的栈,运行任何被推迟的函数。我如果回溯到栈顶,程序会终止。
我们可以通过使用内建的recover函数来重新取回Go程的控制权。
调用recover将停止回溯,并返回传入panic的是西安。因为回溯只有被推迟执行的函数中的代码在执行,所以recover也只能在被推迟的函数中才有效。

一个应用:在服务器中终止失败的Go程而无需杀死其他正在执行的Go程:

func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}

func safelyDo(work *Work) {
// defer 函数是被推迟执行的
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
do(work)
}

如果do函数中调用panic函数,回溯会执行defer的函数,这时recover会起作用,打印错误信息后退出Go程。
可以使用上面的思路简化错误处理。

对于频繁的字符串操作,可以使用bytes.Buffer接口提供的方法快速拼接

和一般语言类似,Go也为string重载了”+”操作符,可以使用它进行字符串拼接,但是在C#和Java中对于大量的字符串拼接操作推荐使用StringBuilder这样的类,同样对于Go中的大量字符串拼接也不推荐直接使用”+”操作。

可以使用bytes.Buffer提供的方法进行快速拼接:

import (
"bytes"
"fmt"
)
func concat() string {
var buffer bytes.Buffer

for i:=0; i<1000; i++ {
buffer.WriteString("abcd")
}
str := buffer.String()
// do something to str
...
return str
}

这样可以获得O(n)的时间复杂度。

参考: stackoverflow.com/

一些vim插件需要提供Lua支持,特别是常见的补全插件,前段时间安装的[yadr](https

下载源码

首先,编译安装嘛,先下载源码, 可是呢这两sourceforge挂掉了,vim是托管在sf上的,导致下载页面也不能访问了,甚至vim的官网 www.vim.org 也不能访问了,幸好vim在github上有一个备份 https://github.com/vim/vim 或者直接访问vim.org的ftp站: ftp://ftp.vim.org/pub/vim/Unix/

// 使用下面之一的方法下载源码
// git下载的话体积会大一些,好处是以后可以方便地更新
git clone git@github.com:vim/vim.git
wget -O ftp://ftp.vim.org/pub/vim/Unix/vim-7.4.tar.bz2
wget ftp://ftp.vim.org/pub/vim/Unix/vim-7.4.tar.bz2

tar xjvf vim-7.4.tar.bz2
cd vim74

编译

vim的编译其实很简单,就configure->make->make install 这样的流程。但是要添加 Lua支持,就有一些麻烦了。

configure的配置大概是这样的:

./configure --prefix=/data/vim74 --with-features=huge --with-luajit --enable-luainterp=yes

首先如果不在 configure配置那手动打开 --enable-fail-if-missing 这个选项,你会发现,configure没有问题,make没有问题,make install也OK,但是运行生成的vim: vim --version会发现Lua前面还是一个”-“(表示没有Lua支持)
因为其实configure那根本就没有找到lua的支持,只是默认跳过了 --enable-luainterp=yes 选项。。。
所以应该这样运行configure:

./configure --prefix=/data/vim74 --with-features=huge --with-luajit --enable-luainterp=yes --enable-fail-if-missing

如果你的机器没有安装lua 和luajit的话会在检查lua支持那里中断了。

安装Lua和LuaJit

Lua的安装倒是比较方便,官方repo里面就有,当然要想安装新版的也可以自己编译安装。

sudo yum install lua lua-devel -y

luajit不在centos的官方repo里面,我们需要编译安装;

wget http://luajit.org/download/LuaJIT-2.0.4.tar.gz
tar -xzvf LuaJIT-2.0.4.tar.gz
cd LuaJIT-2.0.4
// 使用默认安装路径
make
sudo make install

编译安装

安装好lua支持后,再运行configure就不会报错了

./configure --prefix=/data/vim74 --with-features=huge --with-luajit --enable-luainterp=yes --enable-fail-if-missing
make
sudo make install

运行

运行编译的vim:

/data/vim74/bin/vim

可能你会发现这样的错误:

/data/vim74/bin/vim: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

显然是安装的luajit有问题。我们找一下luajit这个.so文件在哪里

sudo find / -name libluajit-5.1.so.2

发现下面有这个文件:

  1. /home/wuxu/tars/ngx_openresty-1.7.10.2/build/luajit-root/data/openresty/luajit/lib/libluajit-5.1.so.2
  2. /usr/local/lib/libluajit-5.1.so.2
  3. /data/openresty/luajit/lib/libluajit-5.1.so.2

显然1,3是openresty编译的,第二个才是我们安装luajit支持,我们需要给这个.so生成一个软链接,让vim能找到它:

sudo ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

这样再运行vim就不会有问题了~也有Lua支持了,不过Vim的自动补全还没用过呢,学会了再做记录吧。

effective go 中的部分笔记,这是第三部分

部分笔记摘要,参考: https://go-zh.org/doc/effective_go.html

空白标识符

空白标识符是指”_”,是一个特殊的变量标识,在之前的for…range 循环中已经使用过。

为未使用的变量或者包使用

在Go中如果定义了一个变量没有使用,或者导入一个没有使用的包会在编译时报错,未使用的包会让程序体积变大并拖慢编译速度,初始化了而不是用的变量会留下某种隐患。
但是某些时候我们确实需要丢弃一些没有的变量,比如在for range循环中,可能需要丢弃键或者值,或者程序写到中途某些变量已经定义只是还没有使用等等,这时就需要空白标识符了。

// 检查某个路径是否存在
if _, err := os.Stat(paht); os.IsNotExist(err) {
fmt.Println("path not exist")
}

对于未使用的变量和未使用的导入包,可以把它们先赋值给空白标识符”_”关闭编译器的错误,在使用了这些变量后再删掉这些代码:

fd = 32
....
_ = fd
var _ = fmt.Printf // 用于调试使用,结束时删除

接口检查

如果只需要判断某个类型是否实现了某个接口而不需要实际使用接口本身,可以使用空白标识符:

if _, ok := val.(Milk); ok {
fmt.Printf("value %v has implement interface milk", val)
}

// 接口检查示例
type iMilk interface {
getName() string
}
type Milk struct {
name string
}
func (m Milk) getName() string {
return m.name
}
func main() {
var m interface {}
m = Milk{"yili"}
if _,ok := m.(iMilk); ok {
fmt.Printf("value %v has implement iMilk\n ", m)
} else {
fmt.Println("has not implememt")
}
}

内嵌/Embedding

Go语言并不提供典型的类与继承系统,所以也没有子类化的概念。继承是一个重要的概念,但是很多时候更推荐的做法是使用组合,Go提供的内嵌和组合概念差不多,可以将类型内嵌到结构体或者接口中,实现某种组合功能。

一种典型的内嵌的ReadWriter接口的定义:

type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// 一个结合了Reader和Writer的接口
type ReadWriter interface {
Reader
Writer
}

// 也可以内嵌到一个结构体中
type ReadWriter struct {
r *Reader
w *Writer
}

内嵌的接口一般需要实现被内嵌接口的方法,满足其需求,比如要提供Read方法:

func (re *ReadWriter) Read(p []byte) (n int, err error) {
return rw.r.Read(p)
}

而通过直接内嵌结构体,我们就能避免如此繁琐。 内嵌类型的方法可以直接引用
当内嵌一个类型时,该类型的方法会成为外部类型的方法, 但当它们被调用时,该方法的接收者是内部类型,而非外部的。在我们的例子中,当 bufio.ReadWriter 的 Read 方法被调用时, 它与之前写的转发方法具有同样的效果;接收者是 ReadWriter 的 reader 字段,而非 ReadWriter 本身:

type Logger struct {
content string
}
func (l Logger) Log(s string) {
l.content += s
fmt.Println(s)
}
type infoLog struct {
count int
*Logger
}
func main() {
var logger = infoLog{0, &Logger{"sss"} }
// 可以直接调用内嵌类型的方法
logger.Log("embedding function invoke")
}

内嵌类型可能会引起命名冲突的问题。更深层次的命名字段或方法会被覆盖。若相同的嵌套层级上出现同名冲突,通常会产生一个错误。但是如果冲突的名字不会被使用就没有问题……

并发部分有点多,单独做一篇吧

在php中经常会使用memcache或者memcached来做缓存系统,为了方便操作经常会对PHP提供的接口进行一层封装

todo

effective go 中的部分笔记,这是第四部分

部分笔记摘要,参考: https://go-zh.org/doc/effective_go.html

并发

通过通信共享内存

并发编程中很麻烦的一点是对共享变量访问的控制,在一般环境中,使用同步互斥锁机制等实现互斥的访问,使得代码繁琐而且难以理解。Go语言提出了一个独特的机制:信道。
Go将共享的值通过信道传递。在给定的时间点,只有一个Go程能访问信道中的值,在设计上杜绝了数据在同一时间点被放线程访问。

不要通过共享内存来通信,而应通过通信来共享内存。

为了方便理解,可以把Go的并发处理方式看做类型安全的Unix管道的实现。

Go程/Goroutines

Go程是Golang提出的术语,Go程有简单的模型,它是与其他Go程并发运行在同一地址空间的函数。Go程可以看作是轻量级的线程,它的开销几乎只有栈空间的分配,它的使用很廉价。
Go程在多线程操作系统上可以实现多路复用,后面会详细讲到。多路复用在多线程编程中非常重要。

那如何运行一个Go程呢?很简单,只需要在要调用的函数前添加”go”关键字就能在Go程中运行这个函数,函数调用结束时,Go程也就自动退出。

go list.Sort()
// 使用函数字面量运行
go func() {
// do something here
}

信道/Channels

前面已经提到了信道的概念,在多线程中,具体怎么使用呢,下面就详细介绍信道(channel)

之前讲过,在Go中切片,映射和信道都使用make来分配内存,make返回的值充当对底层数据结构的引用。信道的声明和初始化示例如下:

// 前两个是无缓冲的正数类型信道
c1 := make(chan int)
c2 := make(chan int, 0)
// 指向文件指针的带缓冲区的信道
c3 := make(chan *os.File, 100)

若不指定第二个参数,信道就是无缓冲的同步信道。使用一个同步信道,一个示例如下:

c1 := make(chan int) // 一个不带缓冲的,同步信道
go func() {
time.Sleep(500 * time.Millisecond)
// send one value to channl
c1 <- 1
}()
// do something here
fmt.Println("wait for goroutine to quir")
<-c1 // 主线程在此等待Go程结束,丢弃信道中的值,因为该值只是用作同步的标识
fmt.Println("go routine done")

看完无缓冲的信道,下面看一下有缓冲的信道。有缓冲的信道可以看作是信号量,操作系统里面学过信号量的东西。信号量可以看作是资源不足时用来限制线程执行,使其等待的机制。常用来限制吞吐量,限制调用的process的数量等。
比如要对Web请求使用Go程处理:

func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
// 考虑到闭包的性质,这里传入值 *Request,
// 如果不使用 *Request则所有Go程处理的是同一个变量req
go func(req *Request) {
process(req)
<-sem
}(req)
}

// 另一种方式是,重新声明一个变量
for req := range queue {
// 参考js中for循环中使用timeInterval函数时要重新声明变量
req := req // 为该Go程创建 req 的新实例。
sem <- 1
go func() {
process(req)
<-sem
}()
}
}

信道中的信道/channels of channels

这部分比较麻烦,待续

并行化/Parallelization

在多CPU核心上实现并行计算。

并发是用可独立执行的组件构造程序的方法, 而并行则是为了效率在多CPU上平行地进行计算。Go仍然是种并发而非并行的语言,且Go的模型并不适合所有的并行问题

目前Go运行时的实现默认并不会并行执行代码,它只为用户层代码提供单一的处理核心。 任意数量的Go程都可能在系统调用中被阻塞,而在任意时刻默认只有一个会执行用户层代码。 它应当变得更智能,而且它将来肯定会变得更智能。但现在,若你希望CPU并行执行, 就必须告诉运行时你希望同时有多少Go程能执行代码。有两种途径可以使用:

  1. 在运行你的工作时将 GOMAXPROCS 环境变量设为你要使用的核心数
  2. 导入 runtime 包并调用 runtime.GOMAXPROCS(NCPU)。 runtime.NumCPU() 的值可能很有用,它会返回当前机器的逻辑CPU核心数。

当然,随着调度算法和运行时的改进,将来会不再需要这种方法。

一个缓冲区泄漏示例/A leak buffer

客户端Go程从某些来源,可能是网络中循环接收数据。为避免分配和释放缓冲区, 它保存了一个空闲链表,使用一个带缓冲信道表示。若信道为空,就会分配新的缓冲区。 一旦消息缓冲区就绪,它将通过 serverChan 被发送到服务器。 serverChan.

var freeList = make(chan *Buffer, 100)
var serverChan = make(chan *Buffer)

func client() {
for {
var b *Buffer
// 若缓冲区可用就用它,不可用就分配个新的。
select {
case b = <-freeList:
// 获取一个,不做别的。
default:
// 非空闲,因此分配一个新的。
b = new(Buffer)
}
load(b) // 从网络中读取下一条消息。
serverChan <- b // 发送至服务器。
}
}

服务器从客户端循环接收每个消息,处理它们,并将缓冲区返回给空闲列表。

func server() {
for {
b := <-serverChan // 等待工作。
process(b)
// 若缓冲区有空间就重用它。
select {
case freeList <- b:
// 将缓冲区放大空闲列表中,不做别的。
default:
// 空闲列表已满,保持就好。
}
}
}

客户端试图从 freeList 中获取缓冲区;若没有缓冲区可用, 它就将分配一个新的。服务器将 b 放回空闲列表 freeList 中直到列表已满,此时缓冲区将被丢弃,并被垃圾回收器回收。(select 语句中的 default 子句在没有条件符合时执行,这也就意味着 selects 永远不会被阻塞。)依靠带缓冲的信道和垃圾回收器的记录, 我们仅用短短几行代码就构建了一个可能导致缓冲区槽位泄露的空闲列表。(没懂TT)

上一段的英文如下,总觉得中文的翻译不太对

The client attempts to retrieve a buffer from freeList; if none is available, it allocates a fresh one. The server’s send to freeList puts b back on the free list unless the list is full, in which case the buffer is dropped on the floor to be reclaimed by the garbage collector. (The default clauses in the select statements execute when no other case is ready, meaning that the selects never block.) This implementation builds a leaky bucket free list in just a few lines, relying on the buffered channel and the garbage collector for bookkeeping.

vim作为键盘操作为主的编辑器,有许多功能丰富的快捷键,其实不只在普通模式下有快捷键,其实在插入模式下也有一些有用的快捷键…

插入模式的快捷键

对于新手来说,vim的插入模式和记事本一样“好用”,记事本有一些快捷键,但是在vim的插入模式下,我们却经常忘记使用快捷键:

<C-h>/Ctrl+h	 删除前一个字符
<C-w> 删除前一个单词
<C-u> 删除到行首

Esc/<C-[> Esc进入普通模式,其实Ctrl+[和Esc的作用是一样的
<Ctrl-o> Ctrl+o进入插入-普通模式,此模式下可以执行一个普通模式下的命令并返回插入模式,
常用 <C-o>zz:保持插入模式并把光标行滚动到屏幕中间

可以在插入模式下使用vim的计算工具<C-r>= 插入模式下这个组合键访问表达式寄存器(=),这是会在屏幕下方提示输入运算符,输入1+2+3<CR>(回车),vim就会把运算结果插入到刚刚的光标位置,并且仍然是插入模式

对于104键的键盘可以在插入模式下使用Shift+insert组合键粘贴系统剪切板的内容,可以使用<C-r>n粘贴寄存器n中的内容

在插入模式下 <C-v>用来插入一个特殊字符,比如<C-v>u00bf可以插入一个返过来的问号~这是用来插入unicode字符的。

可视模式下的快捷键

普通模式下的快捷键

分栏

sp {filename} 水平切分上下两栏。filename可选,默认当前文件,快捷键<C-w>s
vsp {filenamem} 垂直切分,左右两栏 <C-w>v

如果安装了yadr扩展的话可以使用contrl+h,j,k,l来跳转到相应方向的分栏
没有yadr可以使用快捷键 <C-w><C-w>在分栏中循环切换,或这使用<C-w>[h,j,k,l] 跳转到分栏。

<C-w>= 所有窗口等宽等高
<C-w>_ 最大化活动窗口高度
<C-w>| 最大化活动窗口宽度
[n]<C-w>_ 活动窗口高度设为n
[n]<C-w>| 活动窗口宽度设为n

:clo 关闭活动窗口, 快捷键 <C-w>c
:on[ly] 只保留当前窗口 快捷键 <C-w>o

很有用的重新对其代码,命令模式下 gg=G
待续

effective go 中的部分笔记,这是第二部分

部分笔记摘要,参考: https://go-zh.org/doc/effective_go.html

方法/Methods

与一般的面向对象语言不同;一般的面向对象语言定义类,然后在类中定义属性和方法,通过类的继承来抽象一套机制,但是在Go中,首先是定义结构,然后为已经命名的结构(除了指针或接口)定义方法,这里有一个方法接收者的概念,为一个结构体绑定方法的常用用法如下:

type Book struct {
price float64
}

func (p Book) Disount(c float64) float64 {
// 注意int和float64类型不一样,
// 在Go中并不能直接操作,需要进行类型转换
// 但是float64 可以与字面量值直接相乘... p.price*100不会有问题
return p.price * c
}

对一些方法,传入的参数是值的拷贝而不是地址,经常需要在方法的结尾放回新的结果,可以通过把参数的指针传入,直接修改参数的地址指向的值,常见的io.Write接口就是在指针添加方法。

type byteSlice []byte
func (slice *byteSlice) Write(data []byte) (n int, err error) {
...
}

var b byteSlice
fmt.Fprintf(&b, "this is a string", 7)

以指针或值为接收者的区别在于:值方法可通过指针和值调用, 而指针方法只能通过指针来调用

接口与其他类型/interface

Go中的接口为指定对象的行为提供了一种方法:如果某样东西可以完成这个, 那么它就可以用在这里。我们已经见过许多简单的示例了;通过实现 String 方法,我们可以自定义打印函数,而通过 Write 方法,Fprintf 则能对任何对象产生输出。

Go中的的接口只是需要实现它的类接受了同名的方法即可,这是很特别的。

类型转换

下面的代码:

type squence []int
func (s squence) String stirng {
// do some thing to s
return ftm.Sprint(s)
}

如果忽略类型名,squence和[]int其实是相同的,只不过squence有一个新的类型名而已,所以在他们之间进行类型转换是可以的:

is := []int(s)

在Go语言中,为了访问不同的方法集会经常进行类型转换。甚至对int和float之间的简单运算都会用到类型转换。

接口转换与类型断言

对于混合类型,我们经常需要将一种接口转换为另一种接口,要判断一个值是否实现了某个接口,需要使用到类型断言。类型断言接受一个接口值,并从中提取指定的明确的明确类型的值。与其他语言判断类型不同,它的用法如下:

var.(typeName)
str.(string)

// 对于使用接口定义的值,可以使用下面的方式
// 其中iv是用 var iv interface{} 定义的,Milk是一中类型
switch str := iv.(type) {
case string:
fmt.Println(str)
case Milk:
fmt.Println("milk", str)
default:
fmt.Println("interface type:", str)
}

这是一个看起来很奇怪的用法,不知道Go中有没有提供想instanceof这样的关键字。

通用性/Generality

这部分还不是很了解

若某种现有的类型仅实现了一个接口,且除此之外并无可导出的方法,则该类型本身就无需导出。 仅导出该接口能让我们更专注于其行为而非实现,其它属性不同的实现则能镜像该原始类型的行为。 这也能够避免为每个通用接口的实例重复编写文档。

在这种情况下,构造函数应当返回一个接口值而非实现的类型。

接口和方法

Go中几乎任何类型(除了指针和接口)都能添加方法(添加的示例已经看过很多了,就不赘述了),因此几乎任何类型都能满足一个接口(即使是基于int的类型也能用来满足一个特别的接口),常见的用法就是任何实现了ServeHTTP方法的对象都能处理HTTP请求

甚至可以为func添加方法,当然需要先将函数定义为一种新类型

Handler接口定义如下:

type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

下面是一个实现该接口的示例:

import "net/http"

type milkServer struct {
n int
}
// 注意参数中的http不能少,参考Go的包机制
func (m *MilkServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
m.n++
// do some thing
fmt.Fprintf(w, "request count at %d\n", m.n)
...
}

// 添加绑定
m := new(Milk)
http.Handle("/count", m)
// 也可以直接绑定一个与ServeHTTP签名相同的函数
func ArgsFunc(w http.ResponseWriter, req *http.Request) {
...
}
http.HandleFunc("args", ArgsFunc)

当然也可以绑定到一个信道上面,然后使用信道新更新一些内部状态:

type Chan chan *http.Request
func (c Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c <- req
fmt.Fprint(w, "request received")
}

前段时间去php.net上闲逛发现PHP7的beta1版本出来了,就尝试着编译安装了一下

之前在鸟哥的博客里看到了他们做的PHP的性能测试,相对于PHP5.6都是有很大的提升的,并且PHP的主版本号已经是2004年发布PHP5后,11年来首次更新,肯定PHP7是有很大的改变的,下面那就来试试PHP7的beta版吧,正式版大概在今年年底会发布。

下载源码

测试版本的PHP下载页面: https://downloads.php.net/~ab/

php7 beta1: https://downloads.php.net/~ab/php-7.0.0beta1.tar.gz

wget https://downloads.php.net/~ab/php-7.0.0beta1.tar.gz
或者
curl -O https://downloads.php.net/~ab/php-7.0.0beta1.tar.gz
阅读全文 »