Функция Haskell nub неэффективна
меня смущает реализация функции " nub " (select unique values) в стандартной библиотеке Haskell данные.Список. Реализация GHC
nub l = nub' l []
where
nub' [] _ = []
nub' (x:xs) ls
| x `elem` ls = nub' xs ls
| otherwise = x : nub' xs (x:ls)
насколько я могу судить, это имеет наихудшую временную сложность O(n^2), так как для списка уникальных значений он должен сравнить их все один раз, чтобы увидеть, что они на самом деле уникальны.
если используется хэш-таблица, сложность может быть уменьшена до O(n) для построения таблицы + O (1) для проверка каждого значения на соответствие предыдущим значениям в хэш-таблице. Конечно, это не приведет к созданию упорядоченного списка, но это также будет возможно в O (N log n) с использованием собственных упорядоченных данных GHC.Карта, если это необходимо.
зачем выбирать такую неэффективную реализацию для важной библиотечной функции? Я понимаю, что эффективность не является главной проблемой в Haskell, но, по крайней мере, стандартная библиотека может попытаться выбрать (асимптотически) лучшую структуру данных для работы.
3 ответов
эффективность довольно беспокоит в Haskell, ведь язык работает наравне с Java и бьет его с точки зрения потребления памяти, но, конечно, это не C.
ответ на ваш вопрос довольно прост: прелюдия это nub
требует только Eq
ограничение, в то время как любая реализация на основе Map
или Set
также потребуется либо Ord
или Hashable
.
вы абсолютно правы - nub
является алгоритмом O(n^2). Однако есть еще причины, по которым вы можете использовать его вместо использования hashmap:
- для небольших списков, он все еще может быть быстрее
-
nub
требует толькоEq
ограничение; по сравнениюData.Map
требуетсяOrd
ограничение на ключи иData.HashMap
требуется тип ключа с обоимиHashable
иOrd
классы типа - это лениво - вам не нужно бежать через весь список ввода, чтобы начать получать результаты
Edit: небольшая коррекция в третьем пункте - вам не нужно обрабатывать весь список, чтобы начать получать результаты; вам все равно придется изучить каждый элемент входного списка (so nub
не будет работать в бесконечных списках), но вы начнете возвращать результаты, как только найдете уникальный элемент.
https://groups.google.com/forum/m/#!msg/haskell-cafe/4UJBbwVEacg/ieMzlWHUT_IJ
по моему опыту, "новичок" Haskell (включая прелюдию и плохие пакеты) просто игнорирует производительность во многих случаях, в пользу простоты.
Haskell performance-сложная проблема для решения, поэтому, если вы недостаточно опытны, чтобы искать через платформу или хакерство альтернативы простому nub
(и особенно, если ваш вход находится в списке только потому что вы не думали об альтернативных структурах), то Data.List.nub
вероятно,это не единственная серьезная проблема производительности, а также вы, вероятно, пишете код для игрушечного проекта, где производительность не имеет значения.
вы просто должны верить, что когда вы доберетесь до создания большого (в коде или данных) проекта, вы будете более опытными и знаете, как настроить свои программы более эффективно.
другими словами, Не беспокойтесь об этом и предположим, что что-нибудь в Haskell 98, который исходит из прелюдии или базы, вероятно, не будет самым эффективным способом решения проблемы.