searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Go 1.23 中的新特性 Range-Over Functions

2024-08-30 09:39:24
139
0

Go 1.23 新特性 Range-Over Functions

介绍

在Go 1.23中,添加了一项新的实验性功能:range-over function。这一特性为Go语言引入了类似于迭代器的标准协议,简化了在容器类型上的迭代操作。本文将通过代码示例,详细讲解这一新特性。

基本迭代模式

首先,让我们回顾一下在没有range-over function时,如何对容器类型进行迭代。以下是一个基于链表实现的简单栈(Stack)类型的例子:

type node[T any] struct {
    value T
    next  *node[T]
}

type Stack[T any] struct {
    head *node[T]
}

func (s *Stack[T]) Push(v T) {
    s.head = &node[T]{v, s.head}
}

var ErrEmpty = errors.New("empty stack")

func (s *Stack[T]) Pop() (T, error) {
    if s.head == nil {
        var v T
        return v, ErrEmpty
    }

    n := s.head
    s.head = s.head.next
    return n.value, nil
}

以上代码定义了一个栈结构,并提供了Push和Pop方法。栈并不直接跟踪任何迭代状态,因此我们需要额外实现一个迭代器来完成单次迭代的支持。

func (s *Stack[T]) Items() *StackIterator[T] {
    return &StackIterator[T]{s.head}
}

type StackIterator[T any] struct {
    node *node[T]
}

func (s *StackIterator[T]) Next() (T, bool) {
    if s.node == nil {
        var v T
        return v, false
    }

    n := s.node
    s.node = s.node.next
    return n.value, true
}

以上代码实现了一个迭代器来遍历栈。使用方式如下:

it := s.Items()
for v, ok := it.Next(); ok; v, ok = it.Next() {
    fmt.Println(v)
}

虽然这种方式可以工作,但它在编写与容器解耦的迭代逻辑时仍显繁琐。因此,我们可以引入一种更通用的方式来实现控制反转的迭代。

控制反转

我们可以通过引入控制反转,编写一个通用的Do方法,让用户传递逻辑处理函数来处理栈中的每一个元素。如下所示:

func (s *Stack[T]) Do(yield func(v T)) {
    for n := s.head; n != nil; n = n.next {
        yield(n.value)
    }
}

使用方式如下:

s.Do(func(n int) {
    fmt.Println(n)
})

Do方法允许用户定义具体的逻辑(如打印、保存到文件等),并将此逻辑应用于栈的每一个元素。然而,该方法的一个局限性是无法中途停止迭代。为了克服这一问题,Go 1.22引入了range-over function。

Range-Over Functions

使用range-over function,你可以通过迭代器函数和for-range循环轻松实现对容器的迭代,甚至可以控制迭代的停止。我们首先定义一个新方法来支持range-over function:

func (s *Stack[T]) Iter() func(func(T) bool) {
    iter := func(yield func(T) bool) {
        for n := s.head; n != nil; n = n.next {
            if !yield(n.value) {
                return
            }
        }
    }
    return iter
}

以上方法返回一个符合iter.Seq类型的函数,用户可以使用它进行迭代:

for v := range s.Iter() {
    fmt.Println(v)
}

此外,我们还可以定义返回键值对的迭代器:

func (s *Stack[T]) Iter2() func(func(int, T) bool) {
    iter := func(yield func(int, T) bool) {
        for i, n := 0, s.head; n != nil; i, n = i+1, n.next {
            if !yield(i, n.value) {
                return
            }
        }
    }
    return iter
}

使用方式如下:

for i, v := range s.Iter2() {
    fmt.Println(i, v)
}

使用Pull函数提取值

iter包中还提供了Pull函数,用于逐个提取迭代值并控制迭代停止。以下是一个计算栈中最大值的例子:

func Max[T cmp.Ordered](seq iter.Seq[T]) (T, error) {
    pull, stop := iter.Pull(seq)
    defer stop()

    max, ok := pull()
    if !ok {
        return max, fmt.Errorf("Max of empty sequence")
    }

    for v, ok := pull(); ok; v, ok = pull() {
        if v > max {
            max = v
        }
    }

    return max, nil
}

使用方式如下:

m, err := Max(s.Iter())
if err != nil {
    fmt.Println("ERROR:", err)
} else {
    fmt.Println("max:", m)
}

结论

range-over function为Go语言提供了一种通用的迭代器机制,通过iter.Seq和iter.Seq2,可以使用熟悉的for循环进行迭代,并让实现库的开发者承担迭代的具体实现。我希望通过这些示例,帮助你理解这一新特性并在实际开发中加以应用。

0条评论
作者已关闭评论
范****荣
4文章数
0粉丝数
范****荣
4 文章 | 0 粉丝
原创

Go 1.23 中的新特性 Range-Over Functions

2024-08-30 09:39:24
139
0

Go 1.23 新特性 Range-Over Functions

介绍

在Go 1.23中,添加了一项新的实验性功能:range-over function。这一特性为Go语言引入了类似于迭代器的标准协议,简化了在容器类型上的迭代操作。本文将通过代码示例,详细讲解这一新特性。

基本迭代模式

首先,让我们回顾一下在没有range-over function时,如何对容器类型进行迭代。以下是一个基于链表实现的简单栈(Stack)类型的例子:

type node[T any] struct {
    value T
    next  *node[T]
}

type Stack[T any] struct {
    head *node[T]
}

func (s *Stack[T]) Push(v T) {
    s.head = &node[T]{v, s.head}
}

var ErrEmpty = errors.New("empty stack")

func (s *Stack[T]) Pop() (T, error) {
    if s.head == nil {
        var v T
        return v, ErrEmpty
    }

    n := s.head
    s.head = s.head.next
    return n.value, nil
}

以上代码定义了一个栈结构,并提供了Push和Pop方法。栈并不直接跟踪任何迭代状态,因此我们需要额外实现一个迭代器来完成单次迭代的支持。

func (s *Stack[T]) Items() *StackIterator[T] {
    return &StackIterator[T]{s.head}
}

type StackIterator[T any] struct {
    node *node[T]
}

func (s *StackIterator[T]) Next() (T, bool) {
    if s.node == nil {
        var v T
        return v, false
    }

    n := s.node
    s.node = s.node.next
    return n.value, true
}

以上代码实现了一个迭代器来遍历栈。使用方式如下:

it := s.Items()
for v, ok := it.Next(); ok; v, ok = it.Next() {
    fmt.Println(v)
}

虽然这种方式可以工作,但它在编写与容器解耦的迭代逻辑时仍显繁琐。因此,我们可以引入一种更通用的方式来实现控制反转的迭代。

控制反转

我们可以通过引入控制反转,编写一个通用的Do方法,让用户传递逻辑处理函数来处理栈中的每一个元素。如下所示:

func (s *Stack[T]) Do(yield func(v T)) {
    for n := s.head; n != nil; n = n.next {
        yield(n.value)
    }
}

使用方式如下:

s.Do(func(n int) {
    fmt.Println(n)
})

Do方法允许用户定义具体的逻辑(如打印、保存到文件等),并将此逻辑应用于栈的每一个元素。然而,该方法的一个局限性是无法中途停止迭代。为了克服这一问题,Go 1.22引入了range-over function。

Range-Over Functions

使用range-over function,你可以通过迭代器函数和for-range循环轻松实现对容器的迭代,甚至可以控制迭代的停止。我们首先定义一个新方法来支持range-over function:

func (s *Stack[T]) Iter() func(func(T) bool) {
    iter := func(yield func(T) bool) {
        for n := s.head; n != nil; n = n.next {
            if !yield(n.value) {
                return
            }
        }
    }
    return iter
}

以上方法返回一个符合iter.Seq类型的函数,用户可以使用它进行迭代:

for v := range s.Iter() {
    fmt.Println(v)
}

此外,我们还可以定义返回键值对的迭代器:

func (s *Stack[T]) Iter2() func(func(int, T) bool) {
    iter := func(yield func(int, T) bool) {
        for i, n := 0, s.head; n != nil; i, n = i+1, n.next {
            if !yield(i, n.value) {
                return
            }
        }
    }
    return iter
}

使用方式如下:

for i, v := range s.Iter2() {
    fmt.Println(i, v)
}

使用Pull函数提取值

iter包中还提供了Pull函数,用于逐个提取迭代值并控制迭代停止。以下是一个计算栈中最大值的例子:

func Max[T cmp.Ordered](seq iter.Seq[T]) (T, error) {
    pull, stop := iter.Pull(seq)
    defer stop()

    max, ok := pull()
    if !ok {
        return max, fmt.Errorf("Max of empty sequence")
    }

    for v, ok := pull(); ok; v, ok = pull() {
        if v > max {
            max = v
        }
    }

    return max, nil
}

使用方式如下:

m, err := Max(s.Iter())
if err != nil {
    fmt.Println("ERROR:", err)
} else {
    fmt.Println("max:", m)
}

结论

range-over function为Go语言提供了一种通用的迭代器机制,通过iter.Seq和iter.Seq2,可以使用熟悉的for循环进行迭代,并让实现库的开发者承担迭代的具体实现。我希望通过这些示例,帮助你理解这一新特性并在实际开发中加以应用。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0