【问题标题】:Why the Element and Ring structs for golang list/ring?为什么要使用 golang 列表/环的 Element 和 Ring 结构?
【发布时间】:2024-04-11 11:50:02
【问题描述】:

为什么 golang 中的 list/ring 类型对单个项目使用额外的结构 Element/Ring 而不是 interface{} ?我假设有一些好处,但我看不到。

编辑:我的意思是询问 api 而不是在实现中使用 Element/Ring。实现仍然可以使用非导出类型,但让 api 提供和获取接口{},那么为什么要让用户进出 Element/Ring?

Edit2:例如,列表 Back() 函数可能类似于

func (l *List) Back() interface{} {
    if l.len == 0 {
        return nil
    }
    return l.root.prev.Value
}

列表仍然在内部使用 Element 但它只是元素(未导出),因为它不会返回它而只返回值。

【问题讨论】:

    标签: list go


    【解决方案1】:

    container/list 是链表,所以有List struct 可以对整个列表进行操作并跟踪列表的开头和结尾将是有益的。

    由于它是一个链接列表,您希望能够将项目链接在一起并从一个项目导航到下一个或上一个项目。这需要一个结构来保存指向下一个和上一个项目的指针,并允许您导航到这些项目(使用 Next() 和 Prev() 函数)。 Element 结构用于此目的,它包含指向下一个/上一个项目的指针以及实际值。

    这是结构体的定义方式,它们也有各种成员函数

    type List struct {
        root Element // sentinel list element, only &root, root.prev, and root.next are used
        len  int     // current list length excluding (this) sentinel element
    }
    
    type Element struct {
        // Next and previous pointers in the doubly-linked list of elements.
        // To simplify the implementation, internally a list l is implemented
        // as a ring, such that &l.root is both the next element of the last
        // list element (l.Back()) and the previous element of the first list
        // element (l.Front()).
        next, prev *Element
    
        // The list to which this element belongs.
        list *List
    
        // The value stored with this element.
        Value interface{}
    }
    

    container/ring 没有你暗示的“额外”结构。只有 Ring 结构将一个项目链接到下一个/上一个项目并且还保存该值。没有环的开始/结束,因此不需要有一个结构在整个环上运行或跟踪开始。

    type Ring struct {
        next, prev *Ring
        Value      interface{} // for use by client; untouched by this library
    }
    

    【讨论】:

    • @cellige 在这种情况下,您是如何以任何合理的方式获得下一个/上一个项目的?如果您在某个节点,并且想要获取下一个节点,则必须将“当前”值传递给列表。而且列表实现必须线性扫描列表以找到包含“当前”值的元素并返回其下一个指针,这非常可怕。
    • @nos:实现不需要线性扫描任何东西。它可以看到未导出的字段,并直接使用这些字段获取下一个或上一个节点。
    • @nos:Ring 结构使用 Ring 结构上的方法公开 next 和 prev 指针。这允许实现确保没有人以破坏容器不变量的方式更改指针。
    • @KenBloom 我说的是OP希望API直接采用接口{}的情况,这听起来像是他想直接处理值,而不是包装值的结构. (或者更确切地说,如果 interface{} 真的只是一个 Element,那么处理 interface{} 而不是 Element 又有什么意义?)
    • 是的@nos 我的意思是正确的。我想知道为什么他们让用户在 Element 中处理 Value 而不仅仅是返回 interface{} 并将 interface{} 用于推送功能。仍在内部使用元素来跟踪指针。
    【解决方案2】:

    它们包含过滤或未导出的字段。


    Package list

    文件list.go

    // Package list implements a doubly linked list.
    
    // Element is an element of a linked list.
    type Element struct {
        // Next and previous pointers in the doubly-linked list of elements.
        // To simplify the implementation, internally a list l is implemented
        // as a ring, such that &l.root is both the next element of the last
        // list element (l.Back()) and the previous element of the first list
        // element (l.Front()).
        next, prev *Element
    
        // The list to which this element belongs.
        list *List
    
        // The value stored with this element.
        Value interface{}
    }
    

    Package ring

    文件ring.go

    // Package ring implements operations on circular lists.
    
    // A Ring is an element of a circular list, or ring.
    // Rings do not have a beginning or end; a pointer to any ring element
    // serves as reference to the entire ring. Empty rings are represented
    // as nil Ring pointers. The zero value for a Ring is a one-element
    // ring with a nil Value.
    //
    type Ring struct {
        next, prev *Ring
        Value      interface{} // for use by client; untouched by this library
    }
    

    显然,ElementRing 的类型不能是 interface{},因为它没有任何意义。接口类型不能有方法。

    The Go Programming Language Specification

    Method declarations

    方法是带有接收器的函数。方法声明绑定一个 标识符,方法名称,到一个方法,并关联该方法 与接收者的基本类型。

    MethodDecl   = "func" Receiver MethodName ( Function | Signature ) .
    Receiver     = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
    BaseTypeName = identifier .
    

    接收器类型必须是 T 或 *T 形式,其中 T 是类型名称。 T 表示的类型称为接收者基类型;它一定不是 指针或接口类型,并且必须以相同的方式声明 包作为方法。该方法被称为绑定到基类型 并且方法名称仅在该类型的选择器中可见。

    【讨论】:

    • @cellige:没有澄清。用代码给出一个具体的例子,说明你想做什么以及它应该如何工作。您的 API 的外观和工作方式如何?
    • 这个例子在上面,先生!
    最近更新 更多