英文:
Golang : interface to swap two numbers
问题
我可以帮你翻译代码部分。以下是翻译好的代码:
package main
import "fmt"
type num struct {
value interface{}
}
type numbers struct {
b *num
c *num
}
func (a *num) SwapNum(var1, var2 interface{}) {
temp := var1
var1 = var2
var2 = temp
}
func main() {
a := 1
b := 2
c := 3.5
d := 5.5
SwapNum(a, b)
fmt.Println(a, b) // 2 1
SwapNum(c, d)
fmt.Println(c, d) // 5.5 3.5
}
请注意,你需要将SwapNum
函数改为SwapNum(a *num, var1, var2 interface{})
,并在main
函数中使用a.SwapNum
来调用该函数。此外,你还需要在main
函数中创建一个num
类型的变量,然后将其传递给SwapNum
函数。这样才能正确地使用接口来交换两个输入数字。
英文:
I want to swap two numbers using interface but the interface concept is so confusing to me.
http://play.golang.org/p/qhwyxMRj-c
This is the code and playground. How do I use interface and swap two input numbers? Do I need to define two structures?
type num struct {
value interface{}
}
type numbers struct {
b *num
c *num
}
func (a *num) SwapNum(var1, var2 interface{}) {
var a num
temp := var1
var1 = var2
var2 = temp
}
func main() {
a := 1
b := 2
c := 3.5
d := 5.5
SwapNum(a, b)
fmt.Println(a, b) // 2 1
SwapNum(c, d)
fmt.Println(c, d) // 5.5 3.5
}
答案1
得分: 16
首先,interface{}
类型是一个接受所有值的类型,因为它是一个带有空方法集的接口,而每种类型都可以满足该接口。例如,int
类型没有任何方法,interface{}
类型也没有。
要编写一个交换两个变量值的方法,首先需要确保这些变量是可修改的。传递给函数的值总是被复制的(除了引用类型,如切片和映射,但这不是我们目前关注的问题)。通过使用指向变量的指针,可以实现可修改的参数。
因此,根据这个知识,可以这样定义SwapNum
:
func SwapNum(a interface{}, b interface{})
现在,SwapNum
是一个接受任意类型的两个参数的函数。
不能写成:
func SwapNum(a *interface{}, b *interface{})
因为这只接受*interface{}
类型的参数,而不是任意类型的参数。
(可以在这里尝试一下)
所以我们有了函数签名,唯一剩下的就是交换值了。
func SwapNum(a interface{}, b interface{}) {
*a, *b = *b, *a
}
不,这样是不会起作用的。使用interface{}
时,我们必须进行运行时类型断言,以检查我们是否做对了。因此,代码必须使用reflect
包进行扩展。如果你对反射不熟悉,可以参考这篇文章。
基本上,我们需要这个函数:
func SwapNum(a interface{}, b interface{}) {
ra := reflect.ValueOf(a).Elem()
rb := reflect.ValueOf(b).Elem()
tmp := ra.Interface()
ra.Set(rb)
rb.Set(reflect.ValueOf(tmp))
}
这段代码使用reflect.ValueOf()
对a
和b
进行反射,以便我们可以检查它们。在同一行中,我们假设我们得到了指针值,并通过调用.Elem()
对它们进行解引用。
这基本上等同于ra := *a
和rb := *b
。
之后,我们通过使用.Interface()
请求值并进行赋值(实际上是进行了复制),从而复制了*a
。
最后,我们使用ra.Set(rb)
将a
的值设置为b
的值,这相当于*a = *b
,然后将之前存储在临时变量中的b
赋值给a
。为此,我们需要将tmp
转换回自身的反射,以便可以使用rb.Set()
(它接受一个reflect.Value
作为参数)。
我们能做得更好吗?
是的!我们可以使代码更加类型安全,或者更好地说,通过使用reflect.MakeFunc
使Swap
的定义类型安全。在文档中(点击链接)有一个非常类似于你尝试的示例。基本上,你可以使用反射来填充函数原型的内容。由于你提供了函数的原型(签名),编译器可以检查类型,而当值被缩小为interface{}
时,编译器无法检查类型。
示例用法:
var intSwap func(*int, *int)
a, b := 1, 0
makeSwap(&intSwap)
intSwap(&a, &b)
// 现在a的值为0,b的值为1
这背后的代码是:
swap := func(in []reflect.Value) []reflect.Value {
ra := in[0].Elem()
rb := in[1].Elem()
tmp := ra.Interface()
ra.Set(rb)
rb.Set(reflect.ValueOf(tmp))
return nil
}
makeSwap := func(fptr interface{}) {
fn := reflect.ValueOf(fptr).Elem()
v := reflect.MakeFunc(fn.Type(), swap)
fn.Set(v)
}
swap
函数的代码基本上与SwapNum
的代码相同。makeSwap
与文档中使用的代码相同,其中解释得很清楚。
**免责声明:**上述代码对给定的内容和值做了很多假设。通常需要检查,例如,给定的值是否实际上是指针值等等。出于清晰起见,我省略了这些检查。
英文:
First of all, the interface{}
type is simply a type which accepts all values as it is an interface with an empty method set and every type can satisfy that. int
for example does not have any methods, neither does interface{}
.
For a method which swaps the values of two variables you first need to make sure these variables are actually modifiable. Values passed to a function are always copied (except reference types like slices and maps but that is not our concern at the moment). You can achieve modifiable parameter by using a pointer to the variable.
So with that knowledge you can go on and define SwapNum
like this:
func SwapNum(a interface{}, b interface{})
Now SwapNum
is a function that accepts two parameters of any type.
You can't write
func SwapNum(a *interface{}, b *interface{})
as this would only accept parameters of type *interface{}
and not just any type.
(Try it for yourself here).
So we have a signature, the only thing left is swapping the values.
func SwapNum(a interface{}, b interface{}) {
*a, *b = *b, *a
}
No, this will not work that way. By using interface{}
we must do runtime type assertions to check whether we're doing the right thing or not. So the code must be expanded using the reflect
package. This article might get you started if you don't know about reflection.
Basically we will need this function:
func SwapNum(a interface{}, b interface{}) {
ra := reflect.ValueOf(a).Elem()
rb := reflect.ValueOf(b).Elem()
tmp := ra.Interface()
ra.Set(rb)
rb.Set(reflect.ValueOf(tmp))
}
This code makes a reflection of a
and b
using reflect.ValueOf()
so that we can
inspect it. In the same line we're assuming that we've got pointer values and dereference
them by calling .Elem()
on them.
This basically translates to ra := *a
and rb := *b
.
After that, we're making a copy of *a
by requesting the value using .Interface()
and assigning it (effectively making a copy).
Finally, we set the value of a
to b
with [ra.Set(rb)
]5, which translates to *a = *b
and then assigning b
to a
, which we stored in the temp. variable before. For this,
we need to convert tmp
back to a reflection of itself so that rb.Set()
can be used
(it takes a reflect.Value
as parameter).
Can we do better?
Yes! We can make the code more type safe, or better, make the definition of Swap
type safe
by using reflect.MakeFunc
. In the doc (follow the link) is an example which is very
like what you're trying. Essentially you can fill a function prototype with content
by using reflection. As you supplied the prototype (the signature) of the function the
compiler can check the types, which it can't when the value is reduced to interface{}
.
Example usage:
var intSwap func(*int, *int)
a,b := 1, 0
makeSwap(&intSwap)
intSwap(&a, &b)
// a is now 0, b is now 1
The code behind this:
swap := func(in []reflect.Value) []reflect.Value {
ra := in[0].Elem()
rb := in[1].Elem()
tmp := ra.Interface()
ra.Set(rb)
rb.Set(reflect.ValueOf(tmp))
return nil
}
makeSwap := func(fptr interface{}) {
fn := reflect.ValueOf(fptr).Elem()
v := reflect.MakeFunc(fn.Type(), swap)
fn.Set(v)
}
The code of swap
is basically the same as that of SwapNum
. makeSwap
is the same
as the one used in the docs where it is explained pretty well.
Disclaimer: The code above makes a lot of assumptions about what is given and
what the values look like. Normally you need to check, for example, that the given
values to SwapNum
actually are pointer values and so forth. I left that out for
reasons of clarity.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论