Как мне обойти Го, не имея параметрического полиморфизма? - PullRequest
18 голосов
/ 18 октября 2011

Я новичок в Go, но я читал, что постоянные Go не пропускают параметрический полиморфизм.Каждый раз, когда я пытаюсь выучить новый язык, я использую список проблем L99 , чтобы получить некоторую практику.

Даже если я пытаюсь написать что-то столь же тривиальное, как и первая проблема (которая в Goбыло бы одним оператором, принимающим последний элемент слайса), как бы я написал это как функцию, которая принимает слайс любого типа и (используя тот единственный оператор, на который я ссылался выше) возвращает последний элемент этого слайса?

Я подумал, что, хотя у языка нет параметрического полиморфизма, должен быть какой-то идиоматический способ «Go» сделать это, чтобы завсегдатаи Go могли утверждать, что они не пропускают параметрический полиморфизм.В противном случае, если пример будет более сложным, чем просто последний элемент списка, например, вам потребуется функция для выполнения вашей задачи для каждого типа.

Чего мне не хватает?

Ответы [ 4 ]

13 голосов
/ 18 октября 2011

Вы цитируете "проблемы 99 лиспов", но в Лиспе вообще нет параметрического полиморфизма или статических типов.

Многие статически типизированные языки, такие как Objective-C и Java до универсальных, не имеют параметрического полиморфизма. Решение состоит в том, чтобы просто использовать тип, который может принимать все значения, который в Go равен interface{}, и приводить, когда вам нужно получить какой-то определенный тип из него.

По вашему конкретному вопросу, как взять «любой тип ломтика»; к сожалению, нет интерфейса, который бы включал конкретно срезы, поскольку у срезов нет никаких методов; так что вы застрянете с использованием interface{}. Так как у вас есть неизвестный тип слайса, вам нужно использовать отражение (пакет reflect) для выполнения всех операций слайса, включая получение длины и емкости, добавление и доступ к элементу по определенному индексу.

Другая альтернатива состоит в том, что вместо использования «слайса любого типа», просто используйте «ломтик интерфейса {}», т.е. []interface{}, во всем вашем коде, тогда вы можете использовать обычные операторы слайса для него, и вы можете вставляйте любые элементы, но разыгрывайте их, когда вы их получаете.

8 голосов
/ 18 октября 2011

Способ возврата последнего элемента фрагмента в Go состоит в том, чтобы просто записать его в виде выражения. Например:

var a []int
...
last := a[len(a)-1]

Инкапсуляция простого выражения a[len(a)-1] в обобщенную функцию является ненужным усложнением.

В отличие от Lisp, Go не является чисто функциональным языком. Оценка Go на основе списка из 99 проблем с Лиспом может быть обманчива. Go - это «язык системного программирования» - манипулирование списками, метапрограммирование, символьный ИИ или другие задачи на Лиспе не являются сильной стороной Go.

Я рассматриваю Go как улучшенный C с сборкой мусора и параллелизмом . Иди сюда не для того, чтобы соревноваться с Лиспом.

1 голос
/ 15 сентября 2015

Это звучит очень похоже на то, что когда я обнаружил, что пишу один и тот же код несколько раз для разных массивов разных типов в других языках программирования, таких как C, fpc или delphi. Я изобрел параметрический полиморфизм для языка, который, вероятно, никогда не будет реализован, используя приемы препроцессора и назвал его «включить параметрический полиморфизм файлов» в качестве доказательства концепции того, что на самом деле можно реализовать параметрический полиморфизм в процедурном языке без необходимости ООП или какого-либо сложного система дженериков. Использование препроцессора является формой злоупотребления, но это было только для того, чтобы доказать концепцию с помощью FPC.

Поскольку Golang не использует препроцессор, вам придется использовать интерфейсы или указатели и отправлять тип в качестве параметра. Но даже использование указателей по-прежнему означает, что вам нужно написать много кода для его приведения и заставить все это работать. Интерфейсы лучше, чем указатели, потому что указатели менее безопасны.

Решения, подобные этим:

last := a[len(a)-1]

Склонны к ошибкам, потому что кто-то может забыть минус 1. В некоторых языках есть что-то немного лучше:

// return last element, the "high" of a
last := a[high(a)]
// return first element, the "low" of a
first := a[low(a)]

Вышеупомянутый код не работает в Go AFAIK (не исследовал, есть ли в go что-то похожее на это), просто то, что есть в некоторых других языках (fpc), может быть тем, что Go рассматривает.

Этот низкий и высокий способ работы с вещами абсолютно гарантирует выбор последнего и первого элемента, тогда как использование «минус один» склонно к совершению основных математических ошибок. Кто-то может забыть минус один ... потому что он запутался насчет массива на основе 1, а не массива на основе нуля. Даже если в языке нет такой вещи, как массив на основе 1, все равно можно ошибиться из-за того, что люди иногда думают на основе 1 (наши пальцы начинаются с 1, а не с 0). Некоторые умные программисты утверждают, что нет, наши пальцы начинаются с нуля, а не с одного. Ваш большой палец равен нулю. Ладно, хорошо ... но ... для большей части мира ... ;-) мы заканчиваем тем, что переключаем наш мозг с 1 на 0 в течение всего дня в реальном мире против компьютерного мира, и это вызывает многочисленные ошибки в программном обеспечении.

Но некоторые утверждают, что "Низкий" и "Высокий" - это просто синтаксический сахар, который не является необходимым на минимальном языке. Необходимо решить, стоит ли дополнительная безопасность, что во многих случаях может быть. Насколько сложность LOW () и HIGH () добавляет компилятору, я не уверен, и как это влияет на производительность ... Я не уверен на 100 процентов ... Я думаю, что компилятор может быть умным в оптимизации высоких и низких частот, но я не уверен.

0 голосов
/ 22 апреля 2018

Просто отвечая на вопрос, как получить последний (и первый) элемент массива, это правильный путь в Go:

last := a[:1] first := a[1:]

Но это не имеет ничего общего с параметрическим полиморфизмом, то есть выводом типа и вычисляется во время компиляции.

Я пытаюсь написать библиотеку бинарного дерева, и я все еще пытаюсь найти наиболее эффективный, читабельный и производительный способ абстрагирования типа данных, в частности, у меня есть хранилище, система курсоров и карта индекса. написаны и обходятся функциями, но я хочу иметь возможность переключать тип данных, который фактически хранится в узлах. Я многое узнал о композиции и встраивании в процесс, но это не делает меня полностью счастливым.

Я знаю немного о принципах функционального программирования, и Go иногда рассматривает функции как первый класс, поэтому в теории, вероятно, существует функциональное решение проблемы параметрического полиморфизма. Я нахожусь в процессе выяснения этого, потому что в основном я люблю Функциональную парадигму, но я все равно ненавижу рекурсию (я предпочитаю итерацию, в 100 раз легче для меня визуализировать).

...