Част от (изберете един от крайния брой интерфейси)

Как мога да накарам функцията RemoveDead да приема други части от интерфейси (или може би дори части от структурни указатели), които имплементират Liver с малко въздействие върху производителността?

Струва ми се, че функцията ще трябва да приеме interface{} като аргумент и да прави преобразувания по време на изпълнение, но не съм сигурен как да направя преобразуванията. Бих предположил също, че правенето на x.(Liver) е много по-бавно от Liver(x), защото последното е преобразуване на времето за компилиране.

Най-доброто решение ли е да копирате и поставите функцията и да промените аргумента и типа на връщане във всяко копие? Ще са необходими само три или четири копия, но все пак ще изглежда като много тромаво решение.

type Updater interface {
    Liver
    Update() bool
}

type Liver interface {
    Alive() bool
}

func RemoveDead(all []Updater) []Updater {
    for i := len(all) - 1; i >= 0; i-- {
        if Liver(all[i]).Alive() == false {
            all[i] = all[len(all)-1]
            all = all[:len(all)-1]
        }
    }
    return all
}

person Joonazan    schedule 06.12.2013    source източник
comment
Може да ви е интересно да прочетете този SO въпрос: stackoverflow.com/questions/20163660/   -  person Robert Kajic    schedule 07.12.2013


Отговори (2)


Както споменахте, част от тип []Updater не може да бъде превърната в []Liver с просто твърдение за тип; техният тип не са интерфейси, а части от интерфейси. По същата причина не е възможно да се предаде []Updater на функция, която иска []interface{} като параметър.

Можете обаче да правите каквото желаете, като използвате пакета reflect. Отражението е полезно, но ще има цена на ефективността. Ако смятате, че цената е висока, вероятно ще трябва да използвате решението за копиране и поставяне.

Кодът по-долу със сигурност може да бъде подобрен, но показва как да решите проблема с отражението и може да бъде полезен, когато правите бенчмарк. В момента той счита всяка четна U стойност за жива:

package main

import (
    "fmt"
    "reflect"
)

type Updater interface {
    Alive() bool
    Update() bool
}

type Liver interface {
    Alive() bool
}

type U int
func (u U) Alive() bool  { return u % 2 == 0 }

func RemoveDead(all interface{}) interface{} {
    v := reflect.ValueOf(all)
    if v.Kind() != reflect.Slice {
        panic("RemoveDead requires a slice")
    }
    for i := v.Len() - 1; i >= 0; i-- {
        l := v.Index(i)
        if l.Interface().(Liver).Alive() == false {
            l.Set(v.Index(v.Len()-1))
            v = v.Slice(0, v.Len()-1)
        }
    }
    return v.Interface()
}

func main() {
    u := []U{1,4,7,2,12}
    fmt.Println("Before: ", u)
    u  = RemoveDead(u).([]U)
    fmt.Println("After: ", u)
}

Изход:

Before:  [1 4 7 2 12]
After:  [2 4 12]

Playground

person ANisus    schedule 06.12.2013

Можете да дефинирате трети интерфейс:

type UpdaterLiver interface {
    Updater
    Liver
}

след това променете дефиницията на RemoveDead да бъде

func RemoveDead(all []UpdaterLiver) []UpdaterLiver
person maxbublis    schedule 06.12.2013
comment
Това всъщност няма да помогне, тъй като различните типове срезове не са взаимозаменяеми: play.golang.org/p/ dhlqMjvBjv - person James Henstridge; 06.12.2013