rango sobre la interfaz {} que almacena un segmento

99

Dado el escenario en el que tiene una función que acepta t interface{}. Si se determina que tes una rebanada, ¿cómo puedo rangesuperar esa rebanada?

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,2,3}
    test(data)
}

func test(t interface{}) {
    switch reflect.TypeOf(t).Kind() {
    case reflect.Slice:
        // how do I iterate here?
        for _,value := range t {
            fmt.Println(value)
        }
    }
}

Ejemplo de Go Playground: http://play.golang.org/p/DNldAlNShB

Nucleón
fuente
¿Por qué no hacer que la función tome una [] interfaz {} en su lugar? ¿Estás intentando manejar varios tipos complejos?
Jeremy Wall
2
Sí, es para un sistema de plantillas, por lo que la interfaz {} podría ser un mapa, estructura, sector o matriz. En el código real hay muchas más declaraciones de casos, pero las eliminé de la publicación para hacer el problema más conciso.
Nucleon
1
Jeremy, una cadena [] no es un subtipo de [] interfaz {}, por lo que no puedes llamar a una función func ([] interfaz {}) con una cadena [] o [] int, etc. Sería bueno tener una característica en Go para tener un tipo que signifique "porción de algo", donde luego puede iterar sobre los elementos como interfaz {}, pero desafortunadamente necesita una reflexión para eso.
Bjarke Ebert
@JeremyWall No puede usar [] interfaz {} para actuar como segmento. Puede consultar aquí .
firelyu

Respuestas:

138

Bueno, usé reflect.ValueOfy luego, si es un segmento, puede llamar Len()y Index()en el valor para obtener lenel segmento y el elemento en un índice. No creo que pueda usar el rango de operación para hacer esto.

package main

import "fmt"
import "reflect"

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,2,3}
    test(moredata)
} 

func test(t interface{}) {
    switch reflect.TypeOf(t).Kind() {
    case reflect.Slice:
        s := reflect.ValueOf(t)

        for i := 0; i < s.Len(); i++ {
            fmt.Println(s.Index(i))
        }
    }
}

Ejemplo de Go Playground: http://play.golang.org/p/gQhCTiwPAq

masebase
fuente
26
Funciona muy bien, gracias. Lo único que agregar sería que s.Index(i)devuelva un, reflect.Valuepor lo que en mi caso necesitaba s.Index(i).Interface()hacer referencia al valor real.
Nucleon
1
¿Qué harías si tuvieras un puntero a un corte interface{}? Por ejemplo moredata := &[]int{1,2,3}
Ryan Walls
1
Respondiendo a mi pregunta: si tiene un puntero a un segmento en lugar de un segmento, deberá usarlo Elem()para obtener el valor subyacente. preflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls
¿Qué pasa si quiero obtener el valor del campo de la interfaz del segmento de la interfaz sin ninguna estructura? Probé esta solución, pero está limitada a obtener la referencia de la interfaz solo de la sección, no de los valores reales de los campos.
Amandeep kaur
woh muchas gracias, ese truco me ahorró mucho tiempo (¡y dolor de cabeza!)
Nicolas Garnier
27

No necesita utilizar la reflexión si sabe qué tipos esperar. Puede usar un interruptor de tipo , como este:

package main

import "fmt"

func main() {
    loop([]string{"one", "two", "three"})
    loop([]int{1, 2, 3})
}

func loop(t interface{}) {
    switch t := t.(type) {
    case []string:
        for _, value := range t {
            fmt.Println(value)
        }
    case []int:
        for _, value := range t {
            fmt.Println(value)
        }
    }
}

Mira el código en el patio de recreo .

Inanc Gumus
fuente
Sin embargo, esto no funcionará en una matriz, solo en un segmento
user1028741
@ user1028741 No, el código funciona para todos los tipos, incluidas las matrices + Siempre puede tomar una porción de una matriz array[:].
Inanc Gumus
Pero el tipo de una matriz estará con la capacidad real de la misma. [3] int no es del mismo tipo que [2] int o [] int. Por lo tanto, si el tipo de 't' es en realidad una matriz, no se aplicará el caso relevante.
user1028741
1
Lo que quiero decir es que su ejemplo no funcionará en matrices. Lo cambié aquí: play.golang.org/p/DAsg_0aXz-r
user1028741
No puede hacer que funcione fácilmente, si no conoce el tipo de su parámetro en primer lugar. Para usar su truco (matriz [:]), tendrá que usar la reflexión para decidir que es una matriz, luego convertirla en matriz y luego generar una porción de ella. Por eso dije que funciona en un segmento, no en una matriz. Claramente, con suficiente esfuerzo, puede producir una porción de la matriz ...
user1028741
4

hay una excepción en la forma en que se comporta la interfaz {}, @Jeremy Wall ya dio un puntero. si los datos pasados ​​se definen como [] interfaz {} inicialmente.

package main

import (
    "fmt"
)

type interfaceSliceType []interface{}

var interfaceAsSlice interfaceSliceType

func main() {
    loop(append(interfaceAsSlice, 1, 2, 3))
    loop(append(interfaceAsSlice, "1", "2", "3"))
    // or
    loop([]interface{}{[]string{"1"}, []string{"2"}, []string{"3"}})
    fmt.Println("------------------")


    // and of course one such slice can hold any type
    loop(interfaceSliceType{"string", 999, map[int]string{3: "three"}})
}

func loop(slice []interface{}) {
    for _, elem := range slice {
        switch elemTyped := elem.(type) {
        case int:
            fmt.Println("int:", elemTyped)
        case string:
            fmt.Println("string:", elemTyped)
        case []string:
            fmt.Println("[]string:", elemTyped)
        case interface{}:
            fmt.Println("map:", elemTyped)
        }
    }
}

salida:

int: 1
int: 2
int: 3
string: 1
string: 2
string: 3
[]string: [1]
[]string: [2]
[]string: [3]
------------------
string: string
int: 999
map: map[3:three]

Pruébalo

Matus Kral
fuente