Golang入门教程11:迭代器模式与range

72 阅读5分钟

关于迭代器有趣的小故事

时间:6月15日晚上7点 地点:电影院 人物:小明、小王、售票员、观众、奇怪的人

周末的夜晚,小明和小王兴致冲冲地决定去电影院看一场热映的电影。两人来到电影院,期待着一个美好的观影体验。

进入电影院大厅,他们看到售票员正忙碌地接待观众,颇具亲和力的微笑让人感到温暖。两人排队等候时,售票员发现了一个身着怪异服装的奇怪人,他手里拿着一只大大的气球。

“先生,请购票后入场。”售票员礼貌地提醒道。

“哎呀,我只是想带着气球看电影,没关系吧?”奇怪人有些局促地说。

“当然可以,但是请购票后再进场,谢谢。”售票员微笑回应。

奇怪人犹豫了一下,最终买了一张电影票,满足地走向电影厅。小明和小王感叹售票员的细心和耐心,为了让每个人都能体验到电影的乐趣,售票员竭尽所能。

正当小明和小王选择座位时,一位观众走到售票窗口,表情不满:“这场电影真是太烂了,我要退票!”售票员面带微笑地解释:“很抱歉,已经开始放映的电影无法退票,但我们会尽量为您提供更好的服务。”

见售票员的耐心和专业态度,原本愤怒的观众也渐渐冷静了下来,领着一份小礼品转而离去。

电影进行到一半,小偷突然在影厅内活动,引起一片骚动。小明看到了售票员急切的目光,明白他需要帮助。

“小王,我们去帮助售票员,阻止小偷!”小明毫不犹豫地说道。

小王点头,两人快速走到售票员身边。在售票员的指引下,他们成功地制止了小偷,把他交给了电影院的工作人员。

电影结束后,小明和小王深深地感慨:这场电影之旅虽然充满了波折,但售票员的细心、耐心和专业素养却让整个经历更加有趣和难忘。

在离开电影院时,小明对小王说:“这位售票员不仅是卖票的,更是一位懂得服务艺术的高手。他的微笑和耐心不仅让每位观众感到愉悦,还让整个电影院充满了温馨的氛围。”

小王附和道:“是啊,这次电影之旅不仅让我们欣赏到好片,还让我们见识到了一位服务至上的售票员,真是太棒了!”

迭代器模式概述

迭代器模式是一种行为设计模式,它提供一种顺序访问集合对象元素的方法,而不需要暴露集合的内部表示。通过使用迭代器模式,可以在不知道集合底层结构的情况下遍历集合中的元素。

关键元素

  1. 迭代器(Iterator): 定义访问和遍历元素的接口,包括获取下一个元素、判断是否还有元素等方法。

  2. 具体迭代器(ConcreteIterator): 实现迭代器接口,负责管理遍历集合的当前位置,并实现具体的遍历逻辑。

  3. 集合(Aggregate): 定义创建迭代器对象的接口,可以是抽象类或接口。

  4. 具体集合(ConcreteAggregate): 实现集合接口,负责创建具体迭代器对象。

工作原理

  1. 客户端通过集合接口获取迭代器对象。
  2. 迭代器负责跟踪遍历集合的状态。
  3. 客户端使用迭代器的方法遍历集合元素。

示例代码(使用 Golang)

package main

import "fmt"

// 迭代器接口
type Iterator interface {
	HasNext() bool
	Next() interface{}
}

// 集合接口
type Aggregate interface {
	CreateIterator() Iterator
}

// 具体迭代器
type ConcreteIterator struct {
	data  []interface{}
	index int
}

func (ci *ConcreteIterator) HasNext() bool {
	return ci.index < len(ci.data)
}

func (ci *ConcreteIterator) Next() interface{} {
	item := ci.data[ci.index]
	ci.index++
	return item
}

// 具体集合
type ConcreteAggregate struct {
	data []interface{}
}

func (ca *ConcreteAggregate) CreateIterator() Iterator {
	return &ConcreteIterator{data: ca.data}
}

func main() {
	// 创建具体集合
	aggregate := &ConcreteAggregate{data: []interface{}{"A", "B", "C", "D", "E"}}

	// 获取迭代器
	iterator := aggregate.CreateIterator()

	// 遍历集合
	for iterator.HasNext() {
		item := iterator.Next()
		fmt.Println(item)
	}
}

这个示例中,ConcreteAggregate 实现了集合接口,负责创建具体迭代器 ConcreteIterator。客户端通过集合接口获取迭代器对象,然后使用迭代器的方法遍历集合元素。这样,无论集合的内部结构如何变化,客户端都不受影响。

Golang中的range

如果上面的故事和设计模式没有看懂也没有关系,现在许多语言已经将这种模式内化了,使用起来非常的简单,只看下面的例子就行;如果前面都能看懂就最好了。

在Go语言中,range 关键字用于迭代数组、切片、字符串、map 等数据结构。range 在循环中提供了对数据结构元素的访问,使得遍历变得更加简洁和直观。

range 是Go语言中一种强大的迭代工具,简化了对数据结构的遍历操作。

  1. 计算切片元素的和:

    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)
    

    在这里,使用 range 遍历了切片 nums 中的元素,并将其累加到 sum 变量中,最后打印总和。

  2. 查找切片中元素的索引:

    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }
    

    在这段代码中,range 返回了切片 nums 中的索引和值,通过判断值是否为3,找到了对应的索引并打印。

  3. 遍历映射(map):

    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }
    

    这里使用 range 遍历了映射 kvs 中的键值对,并打印键值对。

  4. 仅遍历映射的键:

    for k := range kvs {
        fmt.Println("key:", k)
    }
    

    当仅需要映射的键时,可以省略值的接收。

  5. 遍历字符串:

    for i, c := range "go" {
        fmt.Println(i, c)
    }
    

    使用 range 遍历了字符串 "go" 中的 Unicode 字符,打印了字符的索引和Unicode码点。

完整练习代码

package main

import "fmt"

func main() {
   nums := []int{2, 3, 4}
   sum := 0
   for _, num := range nums {
      sum += num
   }
   fmt.Println("sum:", sum)

   for i, num := range nums {
      if num == 3 {
         fmt.Println("index:", i)
      }
   }

   kvs := map[string]string{"a": "apple", "b": "banana"}
   for k, v := range kvs {
      fmt.Printf("%s -> %s\n", k, v)
   }

   for k := range kvs {
      fmt.Println("key:", k)
   }

   for i, c := range "go" {
      fmt.Println(i, c)
   }
}