Как идиоматично написать метод, работающий с «универсальным» массивом?
У меня есть типизированный массив:
a := make([]int, 0)
Я хочу написать простой метод, который мог бы работать с массивом любого типа:
func reverse(a []interface{}) []interface{} {
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
return a
}
Использование этого метода a = reverse(a)
дает мне 2 ошибки:
cannot use a (type []int) as type []interface {} in argument to reverse
cannot use reverse(a) (type []interface {}) as type []int in assignment
Прагматичное решение состоит в том, чтобы написать одну обратную функцию для каждого конкретного типа среза, который вы используете. В том маловероятном случае, если существует много типов сгенерировать код.
Пока не появятся дженерики (которые, скорее всего, будут называться контракты), отражение и интерфейсы — единственные инструменты для достижения такого обобщения.
Вы можете определить reverse()
так, чтобы оно принимало значение interface{}
, и использовать пакет reflect
для его индексации и замены элементов. Обычно это медленно, и его сложнее читать/обслуживать.
Интерфейсы обеспечивают более приятный способ, но требуют, чтобы вы писали методы для разных типов. Взгляните на пакет sort
, особенно на функцию sort.Sort()
:
func Sort(data Interface)
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
sort.Sort()
может сортировать любые слайсы, которые реализуют sort.Interface
, любые слайсы, которые имеют методы, необходимые алгоритму сортировки для выполнения своей работы. Преимущество этого подхода в том, что вы можете сортировать и другие структуры данных, а не только срезы (например, связанный список или массив), но обычно используются срезы.
Терпение! Согласно последний проект предложения по добавлению параметров типа к языку, вы сможете написать такую общую функцию reverse
в будущей версии Go:
func reverse[T any](s []T) []T {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
func main() {
s := []int{1, 2, 3, 4, 5}
s = reverse(s)
fmt.Println(s)
}
Не то чтобы вы могли использовать дженерики в производстве на данный момент (по состоянию на 2 октября 2020 г.), но для людей, заинтересованных в предстоящей функции дженериков go, с последней версией эскизный проект go вы можете написать универсальную функцию reverse
, как показано ниже.
package main
import (
"fmt"
)
func reverse[T any](s []T) []T {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
func main() {
s := []int{1, 2, 3, 4, 5}
s = reverse(s)
fmt.Println(s)
}
Выход:
[5 4 3 2 1]
Не сильно отличается от моего предыдущего ответа, не так ли?
@ jub0bs В вашем коде до последнего редактирования использовался синтаксис из прошлогоднего черновика.