dongxing7318 2013-10-10 16:54
浏览 70
已采纳

Golang:交换两个数字的接口

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条回答 默认 最新

  • dongshanyan0322 2013-10-10 17:58
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥15 请问Lammps做复合材料拉伸模拟,应力应变曲线问题
  • ¥30 python代码,帮调试
  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿