Почему пакет изображений Go обрезает + вставляет циклы по пикселям?
Если вы посмотрите на пакет изображений здесь http://golang.org/src/pkg/image/image.go то увидите, что реализация Opaque() для каждого изображения делает то же самое, отличаясь только пиксельно-специфическими логика.
Для этого есть причина? Будет ли какое-либо общее решение менее эффективным? Это просто недосмотр? Есть ли какое-то ограничение (я не вижу такого) в системе типов, которое затруднило бы полиморфный подход [was: generic]?
[edit] Тип решения, о котором я думал (который не нуждается в обобщениях в смысле Java), будет выглядеть так:
type ColorPredicate func(c image.Color) bool;
func AllPixels (p *image.Image, q ColorPredicate) bool {
var r = p.Bounds()
if r.Empty() {
return true
}
for y := r.Min.Y; y < r.Max.Y; y++ {
for x := r.Min.X; x < r.Max.X; x++ {
if ! q(p.At(x,y)) {
return false
}
}
}
return true
}
но у меня возникают проблемы с компиляцией (все еще очень плохо для Go - он будет компилироваться с изображением, но не с указателем изображения!).
Это слишком сложно для оптимизации? (вам нужно иметь встроенную функцию, но тогда не будет ли какая-либо проверка типов выведена из цикла?). Кроме того, теперь я понимаю, что не должен был использовать слово "универсальный" ранее - я имел в виду только общий (ха) способ.
2 ответа
Существует ограничение системы типов, которое препятствует общему решению (или, по крайней мере, делает его очень неэффективным).
Например, тела RGBA.Opaque и NRGBA.Opaque идентичны, так что вы могли бы подумать, что они могут быть преобразованы в третью функцию с сигнатурой примерно так:
func opaque(pix []Color, stride int, rect Rectangle) bool
Вы бы хотели вызвать эту функцию следующим образом:
func (p *RGBA) Opaque() bool {
return opaque([]Color(p.Pix), p.Stride, p.Rect)
}
Но ты не можешь. p.Pix не может быть преобразован в []Color, потому что эти типы имеют различные представления в памяти, и спецификация запрещает это. Мы могли бы выделить новый фрагмент, преобразовать каждый отдельный элемент p.Pix и передать его, но это было бы очень неэффективно.
Обратите внимание, что RGBAColor и NRGBAColor имеют одинаковую структуру. Возможно, мы могли бы выделить функцию только для этих двух типов, так как представление в пикселях в памяти точно такое же:
func opaque(pix []RGBAColor, stride int, rect Rectangle) bool
func (p *NRGBA) Opaque() bool {
return opaque([]RGBAColor(p.Pix), p.Stride, p.Rect)
}
Увы, это опять не разрешено. Похоже, это скорее проблема спецификации / языка, чем технической. Я уверен, что это уже упоминалось в списке рассылки раньше, но я не могу найти хорошее обсуждение этого.
Это похоже на область, где дженерики пригодятся, но пока нет решения для дженериков в Go.
Почему у Go нет универсальных типов?
Дженерики вполне могут быть добавлены в какой-то момент. Мы не чувствуем срочности для них, хотя понимаем, что это делают некоторые программисты.
Обобщения являются удобными, но они сложны в системе типов и времени выполнения. Мы еще не нашли дизайн, который дает ценность, пропорциональную сложности, хотя мы продолжаем думать об этом. Между тем, встроенные в Go карты и фрагменты, а также возможность использовать пустой интерфейс для создания контейнеров (с явным распаковыванием) означают, что во многих случаях можно написать код, который делает то, что позволят дженерики, если не так гладко.
Это остается открытым вопросом.