Archive for 算法-编程

py2exe可以将python程序打包成exe,方便没有python环境的用户使用。 在目录下建立py文件setup.py写入代码:

#-*- coding:utf-8 -*-
from distutils.core import setup
import glob
import py2exe,sys,os

options = {"py2exe":
           {"dll_excludes": ["MSVCP90.dll"],
            "compressed": 1, #压缩
            "optimize": 2,
            #"ascii": 1,
            #"includes":includes,
            "bundle_files": 1 #所有文件打包成一个exe文件 }
            }
           }
setup(windows=[{"script": "mp3.py","icon_resources": [(1,"mp3.ico")]}],

      options=options,
      zipfile=None,#不生成library.zip文件

      )
然后执行:python setup.py py2exe就成功了。 这是第二次使用py2exe打包python程序,感觉生成的文件好大,写编译给别人用的程序还是vc来的实际。   主要参考:http://justcoding.iteye.com/blog/900993

Continue

学习golang来自己写的代码,先贴出来。

package main
import (
    "fmt"
    "os"
    "errors"
    "strings"
)
//no tag 
var ErrFormat = errors.New("No TAG!")
type Mp3 struct{
    //the file attr
    Name string
    Size int64
    //the mp3 attr
    Title string
    Artist string
    Album string
    Year string
    Comment string
    Genre uint8
}
func (m *Mp3) PrintInfo(){
    fmt.Printf("%v:\n",m.Name)
    fmt.Printf("size:%v\ntitle:%v\nartist:%v\nalbum:%v\nyear:%v\ncomment:%v\n",m.Size,m.Title,m.Artist,m.Album,m.Year,m.Comment)
    fmt.Printf("genre:%v",m.Genre)
}
func (m *Mp3) GetInfo(filename string) (err error){
    id3 := make([]byte,128)
    //for read access.
    f,err := os.Open(filename)
    if err != nil{
        return
    }
    defer f.Close()
    //return FileInfo
    fileInfo,err := f.Stat()
    if err != nil{
        return
    }
    //the file size,int64
    m.Size = fileInfo.Size()
    //the file name,string. eq filename
    m.Name = fileInfo.Name()
    //if
    _,err = f.ReadAt(id3,m.Size-128)
    if err != nil{
        return
    }
    tag := string(id3[:3])
    if tag != "TAG"{
        //err  "No ID3~"
        return ErrFormat
    }
    m.Title = strings.Trim(string(id3[3:33]),"\x00")
    m.Album = strings.Trim(string(id3[33:63]),"\x00")
    m.Artist = strings.Trim(string(id3[63:93]),"\x00")
    m.Year = string(id3[93:97])
    m.Comment = strings.Trim(string(id3[97:127]),"\x00")
    m.Genre = uint8(id3[127])
    return nil
}
func main(){
    m := new(Mp3)

    f,err := os.Open(".")
    if err != nil{
        fmt.Print(err)
    }
    defer f.Close()
    names,err := f.Readdirnames(-1)
    if err != nil{
        fmt.Print(err)
    }
    for _,name := range names{
        a := strings.Split(name,".")
        fileExt := strings.ToLower(a[len(a)-1])
        //fmt.Print(i,fileExt)
        if fileExt == "mp3"{
            err = m.GetInfo(name)
            if err != nil{
                fmt.Print(name,err)
            }
            m.PrintInfo()
        }
    }
    //fmt.Printf("%v",names)
}
有关id3的介绍,程序只读取id3 vi版本的信息,位于mp3文件末尾的128个字节。 数据结构定义如下: char Header[3]; /*标签头必须是"TAG"否则认为没有标签*/ char Title[30]; /*标题*/ char Artist[30]; /*作者*/ char Album[30]; /*专集*/ char Year[4]; /*出品年代*/ char Comment[30]; /*备注*/ char Genre; /*类型*/ ID3V1的各项信息都是顺序存放,没有任何标识将其分开,比如标题信息不足30个字节,则使用'\0'补足,否则将造成信息错误。

Continue

名字服务 功能:向服务器提交一个名字,然后服务器返回一个与这个名字关联的值。

-module(kvs).
-export([start/0,store/2,lookup/1]).

start() -> register(kvs,spawn(fun() -> loop() end)).
store(Key,Value) -> rpc({store,Key,Value}).
lookup(Key) -> rpc({lookup,Key}).

rpc(Q) ->
    kvs ! {self(),Q},
    receive
        {kvs,Reply} ->
            Reply
    end.
loop() ->
    receive
        {From,{store,Key,Value}} ->
            put(Key,{ok,Value}),
            From ! {kvs,true},
            loop();
        {From,{lookup,Key}} ->
            From ! {kvs,get(Key)},
            loop()
    end.
4> c(kvs.erl). {ok,kvs} 5> kvs:start(). true 6> kvs:store({location,joe},"Stockholm"). true 7> kvs:store(weather,raining). true 8> kvs:lookup(weather). {ok,raining} 10> kvs:lookup({location,joe}). {ok,"Stockholm"} 11> kvs:lookup({location,jane}). undefined 执行kvs:start(),将创建服务器进程,将接受消息并操作。匹配{From,{store,Key,Value}} 用于接收需要存储的键和值,存储后,给客户端发送保存成功的消息。 {From,{lookup,Key}}取得Key对应的值,并返回该值。 执行6句,将向服务进程发送key和value,第八句则是取值。 同一台机器上,客户端运行一个节点而服务端运行一个节点 需要打开两个终端。 启动一个名为s的erlang节点。并启动服务 D:\erlang5.9.1\code>erl -sname s@localhost (s@localhost)1> kvs:start(). true 启动名为a的erlang节点,然后使用erlang标准库模块prc来调用kvs模块中的函数。 D:\erlang5.9.1\code>erl -sname a@localhost (a@localhost)1> rpc:call(s@localhost,kvs,store,[weather,fine]). =PROGRESS REPORT==== 10-Aug-2012::11:53:03 === supervisor: {local,inet_gethost_native_sup} started: [{pid,<0.49.0>},{mfa,{inet_gethost_native,init,[[]]}}] =PROGRESS REPORT==== 10-Aug-2012::11:53:03 === supervisor: {local,kernel_safe_sup} started: [{pid,<0.48.0>}, {name,inet_gethost_native_sup}, {mfargs,{inet_gethost_native,start_link,[]}}, {restart_type,temporary}, {shutdown,1000}, {child_type,worker}] true (a@localhost)2> rpc:call(s@localhost,kvs,lookup,[weather]). {ok,fine} 在a节点验证weather的值 (s@localhost)2> kvs:lookup(weather). {ok,fine} 函数rpc:call(Node,Mod,Func,[Arg1,Arg2,...,])在Node上执行一个远程调用。被调用的函数是Mod:Func(Arg1,Arg2,...,).

Continue

1.带超时的receive。 防止消息不来,receive语句陷入无限等待中。   只有超时的receive 功能:让当前进程暂停Tms。

sleep(T) ->
    receive
    after T ->
        true
    end.
超时时间为0的receive 超时时间为0的语句会立即触发一个超时,但在此之前,会尝试对邮箱进行模式匹配。
flush_buffer() ->
    receive
        _Any ->
	    flush_buffer()
    after 0 ->
        true
    end.
程序分析: 触发超时之前,会先进行模式匹配,_Any匹配到,又会调用flush_buffer(),一直到清空进程邮箱中的所有消息。 没有超时子句,在邮箱为空情况下,flush_buffer()会永久暂停,不会返回。
priority_receive() ->
    receive
        {alarm,X} ->
	    {alarm,X}
    after 0 ->
        receive
	    Any ->
	        Any
	end
    end.
触发超时前,进行模式匹配时,如果没有消息与{alarm,X}匹配那么执行后面会接收邮箱中的第一个消息。如果没有消息,会在内层的receive上暂停,然后返回它受到的第一个消息。 只有在邮箱中的消息都进行过模式匹配后才会检查after段是否需要进行运算。 2.注册进程。 4个BIF管理注册进程。 register(AnAtom,Pid) AnAtom为原子。注册进程Pid为原子AnAtom。如果该原子被另一个进程注册,那么注册失败。 unregister(AnAtom)移除对应的注册信息。如果一个注册进程死亡,那么也会自动取消注册。 whereis(AnAtom) -> Pid | undefined 判断原子是否被注册,如果被注册,返回进程id,如果没有被注册,返回原子undefined. registered() -> [AnAtom::atom()]返回系统中所有已经注册的名称列表。      

Continue

golang接口代码:

package main
import "fmt"
type S struct {
    i int
}
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int){
    p.i = v
}
type I interface{
    Get() int
    Put(int)
}
func f(p I){
    fmt.Println(p.Get())
    p.Put(1)
}
func main(){
    var s S
    f(&s)
    fmt.Println(s.Get())

}
go代码分析: 开始定义了一个具有一个字段和两个方法的结构类型S 14-17行,定义了一个接口类型,他是方法的集合。对于接口I,S是其合法实现,因为结构S定义了I所需的两个方法。 往下,函数f()的参数为接口类型。p能够实现I,则有方法Get()和Put()。函数的功能是 打印字段i的值,和将i至1. main()函数中,因为S是I的合法实现,定义S类型的s,并将指针传入函数f()。 空接口 每个类型都能匹配空接口:interface{}.我们可以定义一个空接口作为参数的函数: func g(something interface{}) int { return something.(I).Get() } 这个函数中参数为interface{}空接口,能接受任何类型。.(I)是类型断言,用于转换something到I类型的接口。如果有这个类型,则可以调用Get()。 例如: s=new(S) fmt.Println(g(s)) 这个是成功的。创建S类型满足I,可以调用Get()。再看下面这个: i := 6 fmt.Println(g(i)) 这能编译通过,但运行会出错,因为内建类型int没有Get()方法。他不能实现I。 这种问题解决办法:
package main
import "fmt"
type S struct {
    i int
}
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int){
    p.i = v
}
type I interface{
    Get() int
    Put(int)
}
func f(p I){
    fmt.Println(p.Get())
    p.Put(1)
}
func g(something interface{}){
    if t,ok := something.(I); ok{
        fmt.Println("I:",t.Get())
    }else if t,ok := something.(int); ok{
        fmt.Println("int:",t)
    }else{
        fmt.Println("not found:",something)
    }
}
func h(something interface{}){
    switch t:=something.(type){
        case I:
            fmt.Println("I:",t.Get())
        case int:
            fmt.Println("int:",t)
        case *S:
            fmt.Println("S:",t.Get())
        default:
            fmt.Println("not found:",something)
    }
}
func main(){
    var s S
    f(&s)
    fmt.Println(s.Get())
    g(&s)
    h(&s)
    i := 6
    g(i)
    h(i)
}
与上面相比添加了函数g()h(),与前面不同,这个没有返回值,我修改了一下把fmt.Println写到函数里面了。 g()函数里的switch语句。变量t保存了something的类型,在switch之外使用(type)是非法的。 这里还发现一个问题: 在type switch里不能使用fallthrough,使用时报错:.\9.go:33: cannot fallthrough in type switch 接口类型的方法 接口定义为一个方法的集合。方法包含实际的代码。换句话说,一个接口就是定义,而方法就是实现。 因此,接收者不能定义为接口类型,这样做的话会引起invalid receiver type ... 的编译器错误。 接收者类型必须是T或*T,这里的T是类型名。T叫做接收者基础类型或简称基础类型。基础类型一定不能使指针或接口类型,并且定义在与方法相同的包中。 接收者指 receiver 接口指针 在Go 中创建指向接口的指针是无意义的。实际上创建接口值的指针也是非法的。 var buf bytes.Buffer io.Copy(buf,os.Stdin) 就会复制标准输入到buf的副本,而不是buf本身。这看起来永远不会是一个期望的结果。

Continue

方法:面向对象的味道 go代码:

package main
import (
    "fmt"
    "math"
)

type Rectangle struct{
    width,height float64
    rarea float64
}
type Circle struct{
    radius float64
}

func (r Rectangle) area() float64{
    return r.width * r.height
}
func (c Circle) area() float64{
    return c.radius * c.radius *math.Pi
}
func (r *Rectangle) setarea(a float64) {
    r.rarea = a
}
func (r Rectangle) setarea2(a float64) (Rectangle){
    r.rarea = a
    return r
}

func main(){
    r1 :=Rectangle{12,2,0}
    r1.setarea(r1.area())
    fmt.Println(r1.rarea)

    r2 := Rectangle{3,4,0}
    newr2 := r2.setarea2(r2.area())
    fmt.Println(r2.rarea)
    fmt.Println(newr2.rarea)
    c1 := Circle{10}
    c2 := Circle{2}
    fmt.Println("r1:",r1.area())
    fmt.Println(r2.area())
    fmt.Println(c1.area())
    fmt.Println(c2.area())

}
结果: D:\code>go run 7.go 24 0 12 r1: 24 12 314.1592653589793 12.566370614359172 代码中有两个同名方法area,他们前面括号里是ReceiverType,称作receiver。 拥有不同receiver的方法是不同的,即使他们有相同的名字。 内建类型不能定义方法,但是可以定义一个有方法的这种类型。如: type ages int setarea()修改了字段rarea的值,比较setarea()和setarea2(),会发现如果不使用指针,赋值操作是给了一个复制版的r, 并不是原来的值。 匿名字段代码:
package main
import "fmt"

type Human struct{
    name string
    age int
    phone string
}
type Employee struct{
    Human//Human类型的匿名字段
    speciality string
    phone string
}

func main(){
    Bob := Employee{Human{"bob",34,"777777-3"},"designer","33-22"}
    fmt.Println(Bob.name)
    fmt.Println(Bob.phone)
    fmt.Println(Bob.Human.phone)
    fmt.Println(Bob.Human.name)
}
运行结果: D:\code>go run 8.go bob 33-22 777777-3 bob 从程序中,当有字段冲突时,我们必须写全字段名称;当没有冲突时,两种方式都可以。

Continue

package 包名约定使用小写字符。包中的函数名以大写字母开头的是可导出函数,小写字母是私有函数。这个规定同样适用于新类型、全局变量。 当包导入(通过import)时,包名成为了内容的入口。在 import "bytes" 之后,导入包的可以调用函数bytes.Buffer。任何使用这个包的人,可以使用同样的名字访问到它的内容,因此这样的包名是好的:短的、简洁的、好记的。 根据规则,包名是小写的一个单词;不应当有下划线或混合大小写。由于每个人都可能需要录入这个名字,所以尽可能的简短。不要提前考虑冲突。 包名是导入的默认名称。 在Go中使用混合大小写MixedCaps或者mixedCaps,而不是下划线区分含有多个单词的名字。 包文档: 每个包都应该有包注释,在package 前的一个注释块。对于多文件包,包注释只需要出现在一个文件前,任意一个文件都可以。 包注释应当对包进行介绍,并提供相关于包的整体信息。这会出现在go doc生成的关于包的页面上,并且相关的细节会一并显示。 每个定义(并且导出)的函数应当有一小段文字描述该函数的行为。 常用包:http://golang.org/pkg/ 指针 go有指针,没有指针运算。一个新定义的没有指向的指针值为nil。

package main
import "fmt"

func main(){
var p *int
fmt.Printf("%v\n",p)
fmt.Printf("%T\n",p)
var i int
p = &i
fmt.Printf("%v\n",p)
*p =8
fmt.Printf("%v\n",*p)
}
结果: *int 0x10dd0018 8 *p++,表示(*p)++,先获取指针指向的值,然后加一。 内存分配 使用new分配内存 new(T)分配了零值填充的T类型的内存空间,返回其地址,一个*T类型的值。返回一个指针,指向新分配的类型T的零值。 用make分配内存 make(T,args)只能创建slice,map和channel,并且返回一个有初始值(非零)的T类型,不是*T.原因是指向数据结构的引用在使用前必须被初始化。 v := make([]int,100) 定义自己的类型
type person struct {
    name string
    age int
}

var P person
P.name = "Tom" // Assign "Tom" to P's name field.
P.age = 25 // Set P's age to 25 (an int).
fmt.Println("The person's name is %s", P.name)
P是一个person类型的变量。
package main
import "fmt"

type person struct{
    name string
    age int
}

func main(){
    a := new(person)
    a.name = "wang"
    a.age = 42
    b := person{"Tom",11}
    c := person{"rose",44}
    fmt.Printf("%v,%v,%v",a,b,c)
}
字段名字大写,可以导出,可以在其他包使用。小写则不可以。

Continue

函数 golang作用域,定义在函数外的变量是全局的,函数内部定义的变量是局部的。如果一个局部变量和一个全局变量有相同的名字,在函数执行的时候,局部变量将覆盖全局变量。

package main
var a=6
func main(){
p()
q()
p()
}

func p(){
println(a)
}
func q(){
    //a := 5//定义变量
    a = 5//变量赋值
    println(a)
}
当在函数内定义是,输出656,而赋值为655. 局部变量仅在定义它的函数时有效。
package main
var a int
func main(){
    a=5
    println(a)
    f()
}

func f(){
    a := 6
    println(a)
    g()
}
func g(){
    println(a)
}
输出565 多值返回。 教程上有趣的例子:
package main

func nextInt(b []byte, i int) (int, int){
    x :=0
    for ; i

定义一个函数,传递一个byte数组和一个位置i,返回两个int类型。

看到golang的格言: 用更少的代码做更多的事

延迟代码defer。
在defer后指定的函数会在函数退出前调用。

func ReadWrite() bool{
file.Open("file")
//do something
if failureX{
file.Close()
return false
}
if failureY{
file.Close()
return false
}
file.Close()
return true
}
可以用defer修改为:
func ReadWrite() bool{
file.Open("file")
defer file.Close()
//do something
if failureX{
return false
}
if failureY{
return false
}
return true
}
函数在退出前会执行file.Close() 延迟的函数是按照后进先出的顺序执行。
for i:=0;i<5;i++{
defer fmt.Print("%d",i)
}
defer可以修改返回值。
package main
func f() (ret int){
    println(ret)
    defer func(){
        ret++
    }()
    return 0
}
func main(){
    println(f())
}
将输出1,ret是一个int型的命名返回值,这个不要看错了。 变参 例子代码:
package main

func myfunc(arg ...int){
    for _,n := range arg{
        println(n)
    }
}
func main(){
    myfunc(1,2,3,4,5)
}
将打印所有参数。arg ...int说明这个函数接受不定数量的参数。参数类型全部是int。在函数体中,变量arg是一个int类型的slice。 函数作为值
package main
import "fmt"
func main(){
    a := func(){
        println("hello")
    }
    a()//
    fmt.Printf("%T",a)
}
先定义一个匿名函数,赋值给a,然后调用函数a(),下面语句是输出a的类型。 接受函数作为参数的函数 回调 Panic和Recover golang没有像Java那样的异常机制:不能抛出异常。作为替代,使用panic和recover机制。这要作为最后手段使用。 没看懂。。。

Continue

array array由[n]定义,n标示array长度,type是类型。 var arr [10]int arr[0] = 42 arr[1] = 13 当向函数内传递一个数组的时候,会获得一个数组的副本,而不是数组的指针。 声明数组时,必须在方括号内输入一些内容:数字或者三个点。三个点,go会自动统计元素个数。 a:= [2][2]int{[2]int{1,2},[2]int{3,4}} a:= [2][2]int{[...]int{1,2},[...]int{3,4}} 或者 a:= [2][2]int{{1,2},{3,4}}//当元素复合声明的类型和外部一致时 slice slice与数组区别,slice可以怎讲啊长度,slice总是指向底层的一个array。slice是指向array的指针。slice是引用类型的,当一个函数需要一个slice参数,函数内对slice修改。slice总是与一个固定长度的array承兑出现。 int:var array[m]int //创建一个m个元素的array。 slice := array[0:n] 几个长度 len(slice)==n cap(slice)==m len(array)==cap(array)==m map 定义map的方法:map[] monthdays := map[string]int{ "Jan":31,"Feb":28, } 最后一个逗号是必须的。 先写到这里了,准备粗略把教程看完。原因是现在用golang学习语法时,太麻烦了。每次都是修改源文件--打开shell--go run,感觉要受不了了,决定写一个像python在shell里一样方便的东西学习语法。

Continue

放在同一行的多个语句,必须用分号分隔。 变量的类型在变量名后面。 var a int var b bool a = 15 b = false 等价: a := 15 b := false 多个var声明可以成组。 var{ x int b bool } 同样适用于const和import。 相同类型的多个变量可以在一行内完成声明: a,b := 20,16 一个特殊的变量名_(下划线),任何赋值给他的值都被丢弃。 _,b := 34,35 golang编译器对声明却未使用的变量报错。 变量名 declared and not used。 布尔类型: true和false 数字类型: int ,根据硬件决定长度。32位硬件是32位,64位为64.也可以明确其长度,完整的整数类型表; int8,int16,int32,int64 byte, uint8,uint16,uint32 uint64.byte是uint8的别名。 浮点型有float32,float64(没有float类型)。 64位的整数和浮点数都是64位的,即便是在32位的架构上。这些类型全部独立,混合这些类型向变量赋值引起编译器错误。 常量:只能是数字、字符串和布尔值。可以使用iota生成枚举值。 const( a = iota b = iota ) 第一个iota表示0,因此a为0,当iota再次进行新的一行使用时,值增加1,b的值为1. 可以省略重复的iota。 const( a = iota b ) 字符串: string s := "hello world!" go中字符串是UTF-8的由双引号包裹。单引号表示一个字符,不是string。一旦给变量赋值,字符串就不能修改了。 一下做法非法: var s string = "hello" s[0] = 'c' go中实现的方法:

s := "hello"
c := []byte(s) //转换s为字节数组
c[0] = 'c' //修改数组
s2 := string(c) //创建新字符串
多行字符串
//错误写法:
s := "starting part"
    + "ending part"
//会被转换成
s := "starting part";
    + "ending part";
//正确写法
s := "starting part" + 
    "ending part"
//另一种写法,使用`反引号作为原始字符串符号
s := `starting part
    ending part`
//最后一个s将包含换行。在原始字符串标识中的值字符是不转义的。
//可以写一个例子,将上面修改为:
s := `starting \n part
ending part`
fmt.Printf("%s",s)
//会发现starting和part间没有换行而是‘\n’
rune :int32别名。用UTF-8进行编码。需要遍历字符串中的字符。可以循环每个字符。 复数:变量类型是complex128(64位虚数部分),complex64(32位虚数部分) 复数写为re+imi,re是实数部分,im虚数部分。 var c complex64 = 5+5i 错误:error。var a error定义a为一个error,a的值为int。

Continue