函数和方法

函数和方法定义

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

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

我们可以给任何自定义类型添加一个或多个方法。 每种类型对应的方法必须和类型的定义在同一个包中, 因此是无法给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 函数还常被用在程序真正开始执行前,检验或校正程序的状态。

main函数引入包初始化流程图

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

这个函数可以通过几种方式调用:

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

箭头函数有几个使用注意点。

不适用场合

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