Функция 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, который исходит из прелюдии или базы, вероятно, не будет самым эффективным способом решения проблемы.