如何编写一个 pop() 函数

huangapple go评论84阅读模式
英文:

How to write a pop() function

问题

如何创建一个pop()函数,使其对任何类型的数组都能起作用?

以下是我目前想出的解决方案:

func pop(a *[]interface{}) interface{} {
	x := (*a)[len(*a)-1]
	*a = (*a)[:len(*a)-1]
	return x
}

func main() {
	a := []int{1, 2, 3}
	x := pop(&a)
	fmt.Println(a, x) // -> [1,2] 3
}

但是,如果我试图通过试错来调整代码,可能会出现cannot use a (type []int) as type []interface {}或其他错误消息。

英文:
a := []int{1,2,3}
x, a := a[len(a)-1], a[:len(a)-1]
fmt.Println(a,x)

How to create a pop() function that will do the same for any type of an array?

Here is what I came up with so far:

func pop(a []*interface{}) interface{}{
	x := a[len(a)-1]
	a = a[:len(a)-1]
	return x
}

func main(){
	a := []int{1,2,3}
	x = pop(a)
	fmt.Println(a,x)  // ->  [1,2] 3
}

But I get cannot use a (type []int) as type []interface {}or other error messages if I try to tweak the code by trial and error.

答案1

得分: 2

package main

import (
	"fmt"
	"reflect"
)

func pop(a interface{}) interface{} {
	v := reflect.ValueOf(a).Elem()
	x := v.Index(v.Len() - 1)
	v.SetLen(v.Len() - 1)
	return x.Interface()
}

func main() {
	a := []int{1, 2, 3}
	x := pop(&a)
	fmt.Println(a, x) // -> [1 2] 3
}

尽管可以实现这个功能,但我仍然认为x, a = a[len(a)-1], a[:len(a)-1]比pop函数更好。

英文:
package main

import (
	"fmt"
	"reflect"
)

func pop(a interface{}) interface{} {
	v := reflect.ValueOf(a).Elem()
	x := v.Index(v.Len() - 1)
	v.SetLen(v.Len() - 1)
	return x
}

func main() {
	a := []int{1, 2, 3}
	x := pop(&a)
	fmt.Println(a, x) // ->  [1,2] 3
}

Though this can be implemented, I still think that x, a = a[len(a)-1], a[:len(a)-1] should be better than a pop function.

答案2

得分: 0

go类型系统不允许你从[]type1 -> []type2进行类型转换。即使允许,接口也是一个包含类型标识和指向对象的指针的结构体,而通常你只需要对象本身。因此,你需要使用interface{}并使用反射来进行切片操作。

func pop(slice interface{}) (interface{}, interface{}) {
    v := reflect.ValueOf(slice)
    return v.Slice(0, v.Len()-1).Interface(), v.Index(v.Len()-1).Interface()
}

注意,这会丧失编译时的类型安全性,因为它必须使用接口。此外,由于使用接口,弹出的值可能会被分配内存,增加额外的垃圾回收压力。

通常,常见的Go风格建议不要编写这样的函数,而是手动内联少量的代码。

英文:

The go type system doesn't allow you to cast from []type1 -> []type2. Even if it did interfaces are a struct containing a type id and pointer to the object, where normally you would just have the object. Because of this you need to take a interface{} and use reflect to do the slicing.

func pop(slice interface{}) (interface{}, interface{}) {
	v := reflect.ValueOf(slice)
	return v.Slice(0,v.Len()-1).Interface(), v.Index(v.Len()-1).Interface()
}

Go Playground

Note that this loses compile time type safety, because it must use an interface. Additionally, due to using interfaces the poped value may be allocated, creating extra GC pressure.

Common Go style typically recommends not writing a function like this, and just inlining the small amount of code manually.

答案3

得分: 0

在使用反射进行了很多很好的回答之后,我还想添加一个提供更符合Go语言习惯的答案。就像Rob Pike在他关于Go谚语的精彩演讲中所说的:

  • interface{} 什么都不说
  • 反射从不清晰

因此,应该有一个答案展示符合Go语言习惯的方式。这种解决方案对于标准类型的切片不起作用。但是在这种情况下,_cshu_的答案展示了最佳解决方案:x, a = a[len(a)-1], a[:len(a)-1]

对于自定义类型,我们需要定义一个 Poper 接口,并且 Pop 函数以该接口作为输入并返回一个空接口。

type Poper interface {
    Pop() interface{}
}

type MyType struct {
    a []int
}

func (mt *MyType) Pop() interface{} {
    x := mt.a[len(mt.a)-1]
    mt.a = mt.a[:len(mt.a)-1]
    return x
}

func Pop(p Poper) interface{} {
    return p.Pop()
}

func main() {
    a := &MyType{[]int{1, 2, 3}}
    fmt.Println(Pop(a), a)
}

https://play.golang.org/p/UbDkoVYSMA

总的来说,返回空接口并不是一个好主意,因为所有后续的代码都必须支持 interface{}。

以下代码示例不起作用:

func main() {
    a := &MyType{[]int{1, 2, 3}}
    fmt.Println(Pop(a), a)
    var b int
    b = Pop(a)
}

https://play.golang.org/p/wg9__O44A8

错误信息完全说明了这个问题:cannot use Pop(a) (type interface {}) as type int in assignment: need type assertion

因此,Pop() 函数通过返回 interface{} 来工作,但是使用该函数的其余代码需要进行类型断言。因此,如果可以避免使用类型断言,应该寻找另一种使用类型的解决方案。

英文:

After all that really good anwers using reflection I also want to add one answer which offers a more idiomatic Go solution. Like Rob Pike said in his great talk about Go Proverbs

  • interface{} says nothing
  • Reflection is never clear

So there should be also one answer showing the idiomatic Go way. This solution does not work for slices of standard types. But there the answer of cshu shows the best solution: x, a = a[len(a)-1], a[:len(a)-1]

For own defined types we have to define a Poper interface and the Pop function takes that as input and returns an empty interface.

type Poper interface {
	Pop() interface{}
}

type MyType struct {
	a []int
}

func (mt *MyType) Pop() interface{} {
	x := mt.a[len(mt.a)-1]
	mt.a = mt.a[:len(mt.a)-1]
	return x
}

func Pop(p Poper) interface{} {
	return p.Pop()
}

func main() {
	a := &MyType{[]int{1, 2, 3}}
	fmt.Println(Pop(a), a)
}

https://play.golang.org/p/UbDkoVYSMA

At all it is not a good idea to return an empty interface, because all following code has to support the interface{}.

The following code example does not work:

func main() {
	a := &MyType{[]int{1, 2, 3}}
	fmt.Println(Pop(a), a)
	var b int
	b = Pop(a)
}

https://play.golang.org/p/wg9__O44A8

The error says everything about that problem: cannot use Pop(a) (type interface {}) as type int in assignment: need type assertion

So the Pop() function does work by returning interface{} but the rest of the code using the result of that function needs to make a type assertion. So if you can avoid it you should search for another solution using types.

huangapple
  • 本文由 发表于 2017年2月26日 19:30:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/42467910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定