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


¿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


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 .


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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.