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 socket通信实现多人聊天室疑惑
  • ¥15 DEV-C++编译缺失
  • ¥33 找熟练码农写段Pyhthon程序
  • ¥100 怎么让数据库字段自动更新
  • ¥15 antv g6 力导向图布局
  • ¥15 quartz框架,No record found for selection of Trigger with key
  • ¥15 锅炉建模+优化算法,遗传算法优化锅炉燃烧模型,ls-svm会搞,后面的智能算法不会
  • ¥20 MATLAB多目标优化问题求解
  • ¥15 windows2003服务器按你VPN教程设置后,本地win10如何连接?
  • ¥15 求一阶微分方程的幂级数