函数和方法
函数和方法定义
dart
Dart 是一个真正的面向对象语言,方法也是对象并且具有一种 类型, Function。 这意味着,方法可以赋值给变量,也可以当做其他方法的参数。 也可以把 Dart 类的实例当做方法来调用。
// 方法可以有两种类型的参数:必需的和可选的。
// 必需的参数在参数列表前面, 后面是可选参数。
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
// 推荐 在公开的 APIs 上使用静态类型, 你当然也可以选择忽略类型定义
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
// 对于只有一个表达式的方法,你可以选择 使用缩写语法来定义
// 这个 => expr 语法是 { return expr; } 形式的缩写。
// => 形式 有时候也称之为 胖箭头 语法。
// 在箭头 (=>) 和冒号 (;) 之间只能使用一个 表达式 – 不能使用 语句。
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
go
实参通过值的方式传递,因此函数的形参是实参的拷贝。 如果实参包括引用类型,如指针,slice(切片)、map、function、channel等类型, 实参可能会由于函数的间接引用被修改。
package main
import "fmt"
// 注意类型在变量名 之后。
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
// 当连续两个或多个函数的已命名形参类型相同时,
// 除最后一个类型以外,其它都可以省略。
package main
import "fmt"
// x int, y int 被缩写为 x, y int
func add(x, y int) (int) {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
// 函数可以返回任意数量的返回值。
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
// 有函数体的函数声明,这表示该函数不是以Go实现的。
// 这样的声明定义了函数标识符。
package math
func Sin(x float64) float //implemented in assembly language
js
function print(s) {
console.log(s);
}
var print = function(s) {
console.log(s);
};
python
函数的 执行 会引入一个用于函数局部变量的新符号表。 更确切地说,函数中的所有变量赋值都将值存储在本地符号表中;
- 而变量引用首先在本地符号表中查找,
- 然后在封闭函数的本地符号表中查找,
- 然后在全局符号表中查找,
- 最后在内置符号表中查找。
所以全局变量不能直接在函数中赋值(除非使用 global 命名)
即使没有 return 语句的函数也会返回一个值,尽管它是一个相当无聊的值。 这个值称为 None (它是内置名称)。
def fib(n): # write Fibonacci series up to n
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
# Now call the function we just defined:
fib(2000)
# 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
f = fib
f(100)
# 0 1 1 2 3 5 8 13 21 34 55 89
fib(0)
print(fib(0))
# None
def fib2(n): # return Fibonacci series up to n
"""Return a list containing the Fibonacci series up to n."""
result = []
a, b = 0, 1
while a < n:
result.append(a) # see below
a, b = b, a+b
return result
f100 = fib2(100) # call it
f100 # write the result
# 0 1 1 2 3 5 8 13 21 34 55 89
默认返回值
Return values(默认返回值)
dart
所有的函数都返回一个值。 如果没有指定返回值,则 默认把语句 return null; 作为函数的最后一个语句执行。
go
js
python
命名返回值
dart
go
- Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
- 返回值的名称应当具有一定的意义,它可以作为文档使用。
- 没有参数的 return 语句返回已命名的返回值。也就是 直接 返回。
- 直接返回语句应当仅用在下面这样的短函数中。在长的函数中它们会影响代码的可读性。
package main
import "fmt"
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
//fmt.Println(split(17))
a, b := split(17)
fmt.Println(a, b)
}
js
python
方法
dart
go
- 方法就是一类带特殊的 接收者 参数的函数。
- 方法接收者在它自己的参数列表内,位于 func 关键字和方法名之间。
我们可以给任何自定义类型添加一个或多个方法。 每种类型对应的方法必须和类型的定义在同一个包中, 因此是无法给int这类内置类型添加方法的 (因为方法的定义和类型的定义不在一个包中)。 对于给定的类型,每个方法的名字必须是唯一的, 同时方法和函数一样也不支持重载。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
// 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
// method里面可以访问接收者的字段
// 调用method通过.访问,就像struct里面访问字段一样
package main
import (
"fmt"
"math"
)
type Rectangle struct { // 定义矩形
width, height 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 main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
c1 := Circle{10}
c2 := Circle{25}
fmt.Println("Area of r1 is: ", r1.area())
fmt.Println("Area of r2 is: ", r2.area())
fmt.Println("Area of c1 is: ", c1.area())
fmt.Println("Area of c2 is: ", c2.area())
}
// 复杂一点的例子
package main
import "fmt"
const(
WHITE = iota // 枚举
BLACK
BLUE
RED
YELLOW
)
type Color byte
type Box struct {
width, height, depth float64
color Color
}
type BoxList []Box // a slice of boxes
func (b Box) Volume() float64 {
return b.width * b.height * b.depth
}
func (b *Box) SetColor(c Color) {
b.color = c
}
func (bl BoxList) BiggestColor() Color {
v := 0.00
k := Color(WHITE)
for _, b := range bl {
if bv := b.Volume(); bv > v {
v = bv
k = b.color
}
}
return k
}
func (bl BoxList) PaintItBlack() {
for i, _ := range bl {
bl[i].SetColor(BLACK)
}
}
func (c Color) String() string {
strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
return strings[c]
}
func main() {
boxes := BoxList {
Box{4, 4, 4, RED},
Box{10, 10, 1, YELLOW},
Box{1, 1, 20, BLACK},
Box{10, 10, 1, BLUE},
Box{10, 30, 1, WHITE},
Box{20, 20, 20, YELLOW},
}
fmt.Printf("We have %d boxes in our set\n", len(boxes))
fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³")
fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String())
fmt.Println("The biggest one is", boxes.BiggestColor().String())
fmt.Println("Let's paint them all black")
boxes.PaintItBlack()
fmt.Println("The color of the second one is", boxes[1].color.String())
fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())
}
// 方法只是个带接收者参数的函数。
// 现在这个 Abs 的写法就是个正常的函数,功能并没有什么变化。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(Abs(v))
}
// 也可以为非结构体类型声明方法。
// 只能为在同一包内定义的类型的接收者声明方法,
// 而不能为其它包内定义的类型(包括 int 之类的内建类型)的接收者声明方法
package main
import (
"fmt"
"math"
)
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
// 指针接收者的方法可以修改接收者指向的值(就像 Scale 在这做的)。
// 由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。
// 若使用值接收者,那么 Scale 方法会对原始 Vertex 值的副本进行操作。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) { // 指针接收者
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4} // 初始化结构体
v.Scale(10) // 更改结构体自身数据
fmt.Println(v.Abs())
}
// 把 Abs 和 Scale 方法重写为函数。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func Scale(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
Scale(&v, 10)
fmt.Println(Abs(v))
}
// 带指针参数的函数必须接受一个指针:
// 而以指针为接收者的方法被调用时,接收者既能为值又能为指针:
// Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)。
package main
import "fmt"
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(2)
ScaleFunc(&v, 10)
p := &Vertex{4, 3}
p.Scale(3)
ScaleFunc(p, 8)
fmt.Println(v, p) // {60 80} &{96 72}
}
// 接受一个值作为参数的函数必须接受一个指定类型的值:
// 方法调用 p.Abs() 会被解释为 (*p).Abs()。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func AbsFunc(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
fmt.Println(AbsFunc(v))
p := &Vertex{4, 3}
fmt.Println(p.Abs())
fmt.Println(AbsFunc(*p))
}
// 使用指针接收者的原因有二:
// 首先,方法能够修改其接收者指向的值。
// 其次,这样可以避免在每次调用方法时复制该值。
// 若值的类型为大型结构体时,这样做会更加高效。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := &Vertex{3, 4}
fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs())
v.Scale(5)
fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs())
}
// 想要调用指针类型方法(*Point).ScaleBy
r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"
// 或者这样:
p := Point{1, 2}
pptr := &p
pptr.ScaleBy(2)
fmt.Println(p) // "{2, 4}"
// 或者这样:
p := Point{1, 2}
(&p).ScaleBy(2)
fmt.Println(p) // "{2, 4}"
// 如果接收器p是一个Point类型的变量,
// 并且其方法需要一个Point指针作为接收器,
// 我们可以用下面这种简短的写法:
p.ScaleBy(2)
// 所以下面这两种写法等价的:
pptr.Distance(q)
(*pptr).Distance(q)
js
python
方法继承
dart
go
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
}
type Employee struct {
Human //匿名字段
company string
}
//在human上面定义了一个method
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
js
python
方法重写
dart
go
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
}
type Employee struct {
Human //匿名字段
company string
}
//Human定义method
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
//Employee的method重写Human的method
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
js
python
接口
dart
go
接口类型具体描述了一系列方法的集合, 一个实现了这些方法的具体类型是这个接口类型的实例
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
接口类型 是由一组方法签名定义的集合。 接口类型的变量可以保存任何实现了这些方法的值。
interface类型定义了一组方法, 如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。
interface可以被任意的对象实现, 同理,一个对象可以实现任意多个interface。
任意的类型都实现了空interface(我们这样定义:interface{}), 也就是包含0个method的interface。
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
type MyFloat float64
type Vertex struct {
X, Y float64
}
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat 实现了 Abser
a = &v // a *Vertex 实现了 Abser
// 下面一行,v 是一个 Vertex(而不是 *Vertex)
// 所以没有实现 Abser。
// a = v
fmt.Println(a.Abs())
}
//
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
loan float32
}
type Employee struct {
Human //匿名字段
company string
money float32
}
//Human实现SayHi方法
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
//Human实现Sing方法
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
//Employee重载Human的SayHi方法
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone)
}
// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
SayHi()
Sing(lyrics string)
}
func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}
//定义Men类型的变量i
var i Men
//i能存储Student
i = mike
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain")
//i也能存储Employee
i = tom
fmt.Println("This is tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild")
//定义了slice Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, 3)
//这三个都是不同类型的元素,但是他们实现了interface同一个接口
x[0], x[1], x[2] = paul, sam, mike
for _, value := range x{
value.SayHi()
}
}
// 类型通过实现一个接口的所有方法来实现该接口。
// 隐式接口从接口的实现中解耦了定义,
// 这样接口的实现可以出现在任何包中,无需提前准备。
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
// 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。
func (t T) M() {
fmt.Println(t.S)
}
func main() {
// var i I
// var t T = T{"hello"}
// i = t
// i.M()
var i I = T{"hello"}
i.M()
}
// 接口也是值。它们可以像其它值一样传递。
// 接口值可以用作函数的参数或返回值。
// 在内部,接口值可以看做包含值和具体类型的元组:
package main
import (
"fmt"
"math"
)
type I interface {
M()
}
type T struct {
S string
}
type F float64
func (t *T) M() {
fmt.Println(t.S)
}
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = F(math.Pi)
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
// (&{Hello}, *main.T)
// Hello
// (3.141592653589793, main.F)
// 3.141592653589793
// 即便接口内的具体值为 nil,方法仍然会被 nil 接收者调用。
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
// 处理空指针
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
// (<nil>, *main.T)
// <nil>
// (&{hello}, *main.T)
// hello
// nil 接口值既不保存值也不保存具体类型。
package main
import "fmt"
type I interface {
M()
}
func main() {
var i I
describe(i) // (<nil>, <nil>)
i.M() // runtime error
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
// 任何实现了String方法的类型都能作为参数被fmt.Println调用
package main
import (
"fmt"
"strconv"
)
type Human struct {
name string
age int
phone string
}
// 通过这个方法 Human 实现了 fmt.Stringer
func (h Human) String() string {
return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years - ✆ " +h.phone+"❱"
}
func main() {
Bob := Human{"Bob", 39, "000-7777-XXX"}
fmt.Println("This Human is : ", Bob)
}
js
python
空接口
dart
go
// 指定了零个方法的接口值被称为 *空接口:*
// 空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)
// 空接口被用来处理未知类型的值。
package main
import "fmt"
func main() {
var i interface{}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
// (<nil>, <nil>)
// (42, int)
// (hello, string)
js
python
接口嵌套
dart
go
// 源码包container/heap
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less returns whether the element with index i should sort
// before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
type Interface interface {
sort.Interface //嵌入字段sort.Interface
Push(x interface{}) //a Push method to push elements into the heap
Pop() interface{} //a Pop elements that pops elements from the heap
}
// io包下面的 io.ReadWriter
type ReadWriter interface {
Reader
Writer
}
js
python
类型断言
dart
go
// 类型断言 提供了访问接口值底层具体值的方式。
// 该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t。
// 类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。
package main
import "fmt"
func main() {
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // 报错(panic)
fmt.Println(f)
}
js
python
类型选择
dart
go
// 类型选择 是一种按顺序从几个类型断言中选择分支的结构。
// 类型选择中的声明与类型断言 i.(T) 的语法相同,
// 只是具体类型 T 被替换成了关键字 type。
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
// 类型判断
package main
import "fmt"
func do(i interface{}) {
fmt.Printf("%v, %T\n",i,i)
}
func main() {
do(21)
do("hello")
do(true)
}
// 21, int
// hello, string
// true, bool
js
python
Stringer接口
dart
go
// fmt 包中定义的 Stringer 是最普遍的接口之一。
// type Stringer interface {
// String() string
// }
// fmt 包(还有很多包)都通过此接口来打印值。
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
// 通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。
package main
import "fmt"
type IPAddr [4]byte
// TODO: 给 IPAddr 添加一个 "String() string" 方法
func (ip IPAddr) String() string {
//var s = ""
//for _,v := range ip {
// fmt.Println(v)
// s += string(v)
//}
//return s
return fmt.Sprintf("%v.%v.%v.%v", ip[0],ip[1],ip[2],ip[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
js
python
error接口
dart
go
Go 程序使用 error 值来表示错误状态。
// type error interface {
// Error() string
// }
package main
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}
// at 2009-11-10 23:00:00 +0000 UTC m=+0.000000001, it didn't work
// 练习:错误
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x<0 {
return 0.0, ErrNegativeSqrt(x)
}
return math.Sqrt(x), nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
// 1.4142135623730951 <nil>
// 0 cannot Sqrt negative number: -2
js
python
Reader接口
dart
go
io 包指定了 io.Reader 接口,它表示从数据流的末尾进行读取。
// 示例代码创建了一个 strings.Reader 并以每次 8 字节的速度读取它的输出。
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
// n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
// b[:n] = "Hello, R"
// n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
// b[:n] = "eader!"
// n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
// b[:n] = ""
js
python
Image接口
dart
go
package main
import (
"fmt"
"image"
)
func main() {
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
}
// 练习
package main
import (
"golang.org/x/tour/pic"
"image/color"
"image"
)
type Image struct{} //新建一个Image结构体
func (i Image) ColorModel() color.Model{ //实现Image包中颜色模式的方法
return color.RGBAModel
}
func (i Image) Bounds() image.Rectangle{ //实现Image包中生成图片边界的方法
return image.Rect(0,0,200,200)
}
func (i Image) At(x,y int) color.Color{ //实现Image包中生成图像某个点的方法
return color.RGBA{uint8(x),uint8(y),uint8(255),uint8(255)}
}
func main() {
m := Image{}
pic.ShowImage(m) //调用
}
js
python
入口函数
dart
每个应用都需要有个顶级的 main() 入口方法才能执行。 main() 方法的返回值为
void 并且有个可选的 List<String>
参数。
void main() {
querySelector("#sample_text_id")
..text = "Click me!"
..onClick.listen(reverseText);
}
// 下面是一个命令行应用的 main() 方法,并且使用了 方法参数作为输入参数
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
go
程序的初始化和执行都起始于main包。 在定义时不能有任何的参数和返回值。
package main
就必须包含一个main
函数。
js
init函数
dart
go
每个源文件都可以通过定义自己的无参数 init 函数来设置一些必要的状态。 (其实每个文件都可以拥有多个 init 函数。) init 函数还常被用在程序真正开始执行前,检验或校正程序的状态。
func init() {
if user == "" {
log.Fatal("$USER not set")
}
if home == "" {
home = "/home/" + user
}
if gopath == "" {
gopath = home + "/go"
}
// gopath 可通过命令行中的 --gopath 标记覆盖掉。
flag.StringVar(&gopath, "gopath", gopath, "override default GOPATH")
}
js
python
参数
可选命名参数
Optional named parameters(可选命名参数)
dart
/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags({bool bold, bool hidden}) {
// ...
}
enableFlags(bold: true, hidden: false);
go
js
function f({x,y=2}) {
// ...
}
f({x:1})
python
关键字参数
# 接受一个必需的参数(voltage)和三个可选的参数(state, action,和 type)
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
可选位置参数
Optional positional parameters(可选位置参数)
dart
把一些方法的参数放到 [] 中就变成可选 位置参数了:
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
go
js
python
任意的参数列表
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
cheeseshop("Limburger",
"It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
# -- Do you have any Limburger ?
# -- I'm sorry, we're all out of Limburger
# It's very runny, sir.
# It's really very, VERY runny, sir.
# ----------------------------------------
# shopkeeper : Michael Palin
# client : John Cleese
# sketch : Cheese Shop Sketch
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
默认参数值
Default parameter values(默认参数值)
dart
在定义方法的时候,可以使用 = 来定义可选参数的默认值。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。
/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
// ...
}
// bold will be true; hidden will be false.
// 旧版本代码可能需要使用一个冒号 (:) 而不是 = 来设置参数默认值。
// 在以后版本不能使用:,
// Dart SDK 版本为 1.21 或者更高的版本,推荐使用 = 来设置默认值
enableFlags(bold: true);
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');
void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
// 默认参数必须为const类型
void main() {
List f({int a, List<int> L = const []}) {
// var L2 = new List.from(L);
var L2 = []..addAll(L);
L2.add(a);
return L2;
}
print(f(a:1));
print(f(a:2));
print(f(a:3));
}
// [1]
// [2]
// [3]
go
js
function f(a, L=[]) {
L.push(a);
return L;
}
console.log(f(1));
console.log(f(2));
console.log(f(3));
// [ 1 ]
// [ 2 ]
// [ 3 ]
python
这个函数可以通过几种方式调用:
- 只给出必需的参数:ask_ok('Do you really want to quit?')
- 给出一个可选的参数:ask_ok('OK to overwrite the file?', 2)
- 或者给出所有的参数:ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'): # in 关键字。它可以测试一个序列是否包含某个值。
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
# 默认值是在 定义过程 中在函数定义处计算的,所以
i = 5
def f(arg=i):
print(arg)
i = 6
f()
# 5
# 默认值为可变对象(列表、字典以及大多数类实例)时
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
# [1]
# [1, 2]
# [1, 2, 3]
# 如果你不想要在后续调用之间共享默认值
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
解包参数列表
dart
go
js
python
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
def parrot(voltage, state='a stiff', action='voom'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.", end=' ')
print("E's", state, "!")
d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d)
变参
dart
go
// arg ...int告诉Go这个函数接受不定数量的参数。
func myfunc(arg ...int) {}
// 变量arg是一个int的slice:
for _, n := range arg {
fmt.Printf("And the number is: %d\n", n)
}
js
python
一等方法对象
dart
// 可以把方法当做参数调用另外一个方法。
printElement(element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
// 方法也可以赋值给一个变量:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
go
- 函数也是值。它们可以像其它值一样传递。
- 函数值可以用作函数的参数或返回值。
package main
import (
"fmt"
"math"
)
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y) // 求直角斜边
}
fmt.Println(hypot(5, 12))
fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow)) // 指数
}
// 13
// 5
// 81
// 函数作为类型
// package main
import "fmt"
type testInt func(int) bool // 声明了一个函数类型
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// 声明的函数类型在这个地方当做了一个参数
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // 函数当做值来传递了
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven) // 函数当做值来传递了
fmt.Println("Even elements of slice are: ", even)
}
js
function add(x, y) {
return x + y;
}
// 将函数赋值给一个变量
var operator = add;
// 将函数作为参数和返回值
function a(op){
return op;
}
a(add)(1, 1)
// 2
python
匿名函数
Anonymous functions(匿名方法)匿名函数
dart
大部分方法都带有名字,例如 main() 或者 printElement()。 你有可以创建没有名字的方法,称之为 匿名方法, 有时候也被称为 lambda 或者 closure 闭包。
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
// 如果方法只包含一个语句,可以使用胖箭头语法缩写。
list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
go
package main
import "fmt"
func main() {
func() { // 匿名函数
fmt.Println("run!")
}()
}
js
箭头函数有几个使用注意点。
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
不适用场合
- 第一个场合是定义对象的方法,且该方法内部包括this。
- 第二个场合是需要动态this的时候,也不应使用箭头函数。
var sum = (num1, num2) => { return num1 + num2; }
let getTempItem = id => ({ id: id, name: "Temp" });
[1,2,3].map(x => x * x);
var result = values.sort((a, b) => a - b);
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
// 调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;
// 如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
// 点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
python
# 使用一个lambda表达式来返回一个函数
def make_incrementor(n):
return lambda x: x + n
f = make_incrementor(42)
# 传递一个小函数作为参数
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pair: pair[1])
# [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
函数标注
dart
go
js
python
def f(ham: str, eggs: str = 'eggs') -> str:
print("Annotations:", f.__annotations__)
print("Arguments:", ham, eggs)
return ham + ' and ' + eggs
f('spam')
作用域
Lexical scope(静态作用域)
dart
Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。 基本上大括号里面定义的变量就 只能在大括号里面访问,和 Java 作用域 类似。
var topLevel = true;
main() {
var insideMain = true;
myFunction() {
var insideFunction = true;
nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
go
js
python
闭包
Lexical closures(词法闭包)
dart
一个 闭包 是一个方法对象, 不管该对象在何处被调用, 该对象都可以访问其作用域内 的变量。 方法可以封闭定义到其作用域内的变量。
// makeAdder() 捕获到了变量 addBy。
// 不管你在那里执行 makeAdder() 所返回的函数,都可以使用 addBy 参数
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
go
闭包是一个函数值,它引用了其函数体之外的变量。 该函数可以访问并赋予其引用的变量的值, 换句话说,该函数被这些变量“绑定”在一起。
// squares返回一个匿名函数。
// 该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
func main() {
f := squares()
fmt.Println(f()) // "1"
fmt.Println(f()) // "4"
fmt.Println(f()) // "9"
fmt.Println(f()) // "16"
}
package main
import "fmt"
func adder() func(int) int { // 返回:func(int) int
sum := 0
return func(x int) int { // 返回:int
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
// 0 0
// 1 -2
// 3 -6
// 6 -12
// 10 -20
// 15 -30
// 21 -42
// 28 -56
// 36 -72
// 45 -90
package main
import "fmt"
// 返回一个“返回int的函数”
func fibonacci() func() int {
var n int
x,y := 0,1
return func() int {
n = x
x,y = y,x+y
return n
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
js
function f1() {
var n = 999;
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
function createIncrementor(start) {
return function () {
return start++;
};
}
var inc = createIncrementor(5);
inc() // 5
inc() // 6
inc() // 7
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name,
getAge: getAge,
setAge: setAge
};
}
var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25
python
测试函数是否相等
Testing functions for equality(测试函数是否相等)
dart
foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = new A(); // Instance #1 of A
var w = new A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
go
js
python
立即调用的函数表达式
(IIFE)
dart
go
package main
import "fmt"
func main() {
func() {
fmt.Println("run!")
}()
}
js
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
python