Golang代码优化:哪种实现方式更好求建议

Golang代码优化:哪种实现方式更好求建议 我想用Go语言创建链表代码,但不确定应该使用哪种风格。

*** 第一段代码开始 ***

package main

import "fmt"

type List struct {
	head *Node
}

type Node struct {
	next    *Node
	element int
}

func (list *List) append(element int) {
	if list.head == nil {
		list.head = &Node{nil, element}
	} else {
		node := list.head
		for node.next != nil {
			node = node.next
		}
		node.next = &Node{nil, element}
	}
}

func (list *List) appendFirst(element int) {
	list.head = &Node{list.head, element}
}

func (list *List) Print() {
	node := list.head
	for node != nil {
		fmt.Print(node.element, " ")
		node = node.next
	}
	fmt.Println("")
}

func main() {
	ll := List{}
	for i := 0; i < 10; i++ {
		ll.appendFirst(i)
		// ll.append(i)
	}
	ll.Print()
}

*** 第一段代码结束 ***

*** 第二段代码开始 ***

package main

import "fmt"

type List *Node

type Node struct {
	next    List
	element int
}

func append(list *List, element int) {
	if *list == nil {
		*list = &Node{nil, element}
	} else {
		append(&((*list).next), element)
	}
}

func appendFirst(list *List, element int) {
	*list = &Node{*list, element}
}

func Print(list List) {
	node := list
	for node != nil {
		fmt.Print(node.element, " ")
		node = node.next
	}
	fmt.Println("")
}

func main() {
	var list List = nil
	for i := 0; i < 10; i++ {
		appendFirst(&list, i)
		//append(&list, i)
	}
	Print(list)
}

*** 第二段代码结束 ***

请告诉我应该使用哪种代码风格。


更多关于Golang代码优化:哪种实现方式更好求建议的实战教程也可以访问 https://www.itying.com/category-94-b0.html

14 回复

如果我是Go开发者,我会使用标准库中的链表。

func main() {
    fmt.Println("hello world")
}

更多关于Golang代码优化:哪种实现方式更好求建议的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的回复。

但这个答案并没有解决我的问题。试想一下,如果您是一名 Go 开发者,您会如何编写一个链表程序。是否有其他更好的程序。

感谢您的回复。

您说得对,我们可以直接使用内置的数据结构来完成。但有些教授正在用 Go 语言教授链表。我给出了基于面向对象编程的答案,但他对我的解决方案并不满意。所以我想,我应该采用基于双指针的过程式解决方案。

你好 @sylvie

感谢你的回答,我认为你提到了一些关键点。你说的“单一用途包倾向于函数模式”是什么意思?

这个函数模式是什么?确实,strings 包暴露了一些函数(而不是实现接口的对象),但数据结构是一个提供功能的对象。请提供更多细节。

@hollowaykeanho

这是一个很棒的想法 @hollowaykeanho。你还应该定义一个包含这些方法的接口,并让你的数据结构实现它。当其他人查看你的代码时,他们会看到这个接口,并对你提供的功能有一个清晰的认识。

方法和函数都是有效的;方法只是一个带有特殊接收者参数的函数。实际上,两者没有太大区别。一些人遵循的经验法则是,在管理状态时使用方法,因为这更自然流畅。请注意,你也可以编写指针接收者来修改接收者所指向的值。

此外,示例2中的这个函数与内置函数 append 冲突。你在提交给讲师之前测试过你的程序吗?Go 开发者直接阅读代码就能轻易发现这一点。

这会不会让你的讲师感到不快?

func append(list *List, element int) {

感谢您的回复。

您说得对,我们可以直接使用内置的数据结构来完成。但有些教授正在用 Go 语言教授链表。我给出了基于面向对象编程的答案,但他对我的解决方案并不满意。所以我想,我应该采用基于双指针的过程式解决方案。

命名规范我会修正。但问题是,编写代码应该采用过程式还是面向对象的方法。

我的风格

package linkedlist

func NewStack[T any]() *Stack[T] {
	return new(Stack[T])
}

type Stack[T any] struct {
	length int
	head   *Iterator[T]
}

func (c *Stack[T]) Next(iter *Iterator[T]) *Iterator[T] {
	return iter.next
}

func (c *Stack[T]) Begin() *Iterator[T] {
	return c.head
}

func (c *Stack[T]) End(iter *Iterator[T]) bool {

此文件已被截断。显示原文

你好 @hemant_jain

很高兴看到你在编写这个数据结构,欢迎来到 Go 论坛。

请采用第一种方法,也就是你的第一个实现。我认为第二种实现方式不是 Go 语言中常见的编码风格,我不喜欢 if *list == nil 这种写法,因为它使用了过多的指针,使得代码更难阅读和编写。

现在,在编写任何类型的数据结构时,你都应该编写大量的单元测试。同时,尝试使用泛型(你的链表应该能够存储任何数据类型,而不仅仅是 int)。

另外,请将 append 方法设为公开(Append),并在 append 方法和 Node 数据结构上方添加注释。

hemant_jain:

你说得对,我们可以直接使用内置数据结构来完成。但有些教授正在用 Go 语言教授链表。

你完全确定你的讲师真的期望在 Go 中实现一个链表,还是期望一些跳出常规的解法(这很明显)?

我曾经有一位讲师,在一次结构完整性作业中,当我的同学们都在使用金属丝等其他材料时,他却暗中期望我只用一袋生的意大利面来举起 5+3 公斤的重物。隐藏的期望是在工程技术细节之外,还需要具备成本核算和产品生命周期规划(该建筑只需要维持 10 分钟寿命)等商业敏锐度。


旁注:

如果你的讲师真的期望你用 Go 实现一个链表,我会深深地叹一口气。把它当作人生中的一次模拟吧。有时,生活会给你柠檬。试着把它做成柠檬水。祝你好运。注意:在 Go 中完全可以使用双指针。

hemant_jain:

我之前给出了基于 OOP 的答案

啊…… Go 不是 OOP 语言。它不支持多态或数据类型的继承。方法 1 看起来像 OOP,但其实不是。

这些方法是鸭子类型的接口。你无法更改任何已定义的方法,也无法基于现有定义及其所有方法来定义新的数据类型而不完全重写——只能使用组合。

也许他/她是因为这个原因而感到失望?

哦对了……我差点忘了:在开发一个“不安全”的库时,尽量提供一个完整的创建、读取、更新和删除(CRUD)功能。按理说,链表最困难的部分通常是删除,因为在C语言中,内存是手动管理的。既然你用的是Go,垃圾回收器显然会支持你,所以你需要咨询你的讲师,了解在你的演示中是否需要手动管理内存。

你已经完成了:

Append - 将一个元素添加到列表的末尾 Prepend - 将一个元素添加到列表的开头 String - 将列表以字符串形式打印出来

缺失的API有:

Len / Length - 提供列表的总长度 Index - 按位置读取值 Insert - 按位置添加元素 Pop - 按位置移除元素 Search - 按值扫描索引 Scan - 按值扫描所有索引 Join - 将两个链表合并为一个 Split - 将一个链表拆分为两个不同的链表 Copy - 复制一个链表 Delete / Destroy - 通过释放内存空间来移除所有元素,然后销毁链表 Sanitize - 深度检查每个元素是否可用且安全 Zero - 将所有元素的值设置为零(通常在安全释放时使用,即在释放内存之前将机密数据清零) Reserve - 在使用前预留并初始化链表中的一组元素(用于音频/视频等对时间敏感的环境)

我相信就这些了。请与你的决策者确认。祝你好运。

从代码可读性和Go语言惯用法的角度来看,第一种实现方式更好。以下是具体分析:

第一种实现的优势:

  1. 结构清晰List作为独立结构体,封装了链表头指针,更符合面向对象的设计思想
  2. 方法接收器明确:使用指针接收器(list *List),代码意图清晰
  3. 内存安全:避免了二级指针操作,减少空指针风险
  4. Go语言惯用:符合Go标准库中数据结构的实现模式

示例对比:

第一种实现的追加方法更直观:

// 第一种:方法调用简洁
ll := List{}
ll.append(10)

// 第二种:需要传递指针
var list List = nil
append(&list, 10)

性能考虑:

第一种实现的append方法使用循环而非递归,避免了递归调用的栈开销:

// 第一种:迭代方式,效率更高
func (list *List) append(element int) {
    if list.head == nil {
        list.head = &Node{nil, element}
    } else {
        node := list.head
        for node.next != nil {
            node = node.next
        }
        node.next = &Node{nil, element}
    }
}

// 第二种:递归方式,链表长时可能栈溢出
func append(list *List, element int) {
    if *list == nil {
        *list = &Node{nil, element}
    } else {
        append(&((*list).next), element)
    }
}

扩展性:

第一种实现更容易添加其他链表操作,例如计算长度:

func (list *List) Length() int {
    count := 0
    node := list.head
    for node != nil {
        count++
        node = node.next
    }
    return count
}

结论: 选择第一种实现方式,它在可读性、安全性和性能方面都更优,且更符合Go语言的编码规范。

hemant_jain:

考虑一下,如果你是Go开发者,你会如何编写一个链表程序。是否有其他更好的程序。

啊……我们不用Go写链表。事实上,我们根本不用它。我们很少遇到需要使用它的场景。

我们有垃圾收集器、哈希表(map)和slice(不要与array混淆)。链表只在像C这样没有这些特性的语言中才有意义。

实际上,你已经让我们陷入了一个困境,要为一个没有合适用例的场景编写代码,这对我们任何人来说都是职业自杀。rofl

hemant_jain:

appendFirst

它应该叫prepend

Skurhse_Rage:

方法和函数都是有效的;方法只是一个带有特殊接收者参数的函数。实际上差别不大。

她说得对。最终决定权在你的审阅者、面试官、老板和客户手里。两者都可以。

hemant_jain:

func appendFirst(list *List, element int) {

hemant_jain:

func append(list *List, element int) {

注意你的命名规范。在Go中,以大写字母开头意味着是导出函数。请阅读Effective Go - The Go Programming Language

hemant_jain:

	if list.head == nil {
		list.head = &Node{nil, element}
	} else {
		node := list.head
		for node.next != nil {
			node = node.next
		}
		node.next = &Node{nil, element}
	}

我更喜欢双指针的实现方式。参考:Double Pointers and Linked List in C | Dev Notes

但是,请记住,在标准的Go中,无论你做什么(上面两个示例),正确的做法是:

func main() {
    var list []int

	// 追加
	for i := 0; i < 10; i++ {
		list = append(list, i)
	}

	// 前置
	for i := 0; i < 10; i++ {
		list = append([]int{i}, list...)
	}
}

老兄,你用错语言了。

回到顶部