Списки различий в функциональном программировании

13

Вопрос Что нового в чисто функциональных структурах данных со времен Окасаки? и эпический ответ jbapple, упомянутый с использованием списков различий в функциональном программировании (в отличие от логического программирования), что меня недавно интересовало. Это привело меня к поиску реализации списка различий для Haskell. У меня есть два вопроса (простите / исправьте меня, если я должен задать им два разных вопроса в StackExchange).

Простой вопрос заключается в том, знает ли кто-либо об академическом рассмотрении списков различий в функциональном программировании и / или реализациях, помимо списка в библиотеке Haskell? Ответ jbapple не дал цитаты для списков различий (списки различий в логическом программировании существуют в знаниях и в нескольких источниках, которые у меня есть Around Here Somewhere (TM)). До того, как найти реализацию на Haskell, я не знал, что идея перешла от логики к функциональному программированию. Конечно, списки различий в Haskell являются чем-то вроде естественного использования функций более высокого порядка и работают совершенно иначе, чем в логическом программировании, но интерфейс, безусловно, схож.

Более интересная (и не очень понятная) вещь, о которой я хотел спросить, заключается в том, является ли заявленная асимптотическая верхняя граница для вышеупомянутой библиотеки разностных списков на языке Haskell верной / правдоподобной. Моя путаница может быть из-за того, что я упускаю что-то из очевидного в логике сложности, но заявленные границы имеют смысл для меня, только если замена на большую структуру данных (или формирование замыкания, или поиск переменной, или что-то ) всегда занимает постоянное время. Или «подвох» заключается просто в том, что нет ограничений по времени выполнения для «головы» и «хвоста» именно потому, что этим операциям, возможно, придется пахать через произвольную кучу отложенных вычислений / замен?

Роб Симмонс
источник
1
Сначала меня смутили «функциональные языки программирования (в отличие от функциональных языков программирования)», но вы хотели написать «(в отличие от языков логического программирования)»?
Цуёси Ито
Ой ой - да, это то, что я имел в виду, это исправлено сейчас.
Роб Симмонс
Мне кажется, что второй вопрос более уместен в отношении переполнения стека, но теперь, когда вы задали его здесь, может быть, лучше подождать, чтобы узнать, может ли кто-нибудь ответить здесь. Лично я не могу найти никаких оснований сомневаться в заявленных границах при чтении исходного кода, но я не следовал вашим соображениям, чтобы сомневаться в них, а также я могу что-то упустить.
Цуёси Ито

Ответы:

8

Или «подвох» заключается просто в том, что нет ограничений на время выполнения для «головы» и «хвоста» именно потому, что эти операции могут пахать через произвольную кучу отложенных вычислений / замен?

Я думаю, что это более или менее правильно. ЭБ только действительно быстрые операции сборки, хотя, так что вспашка , где мΘ(м)м - количество операций, используемых для построения DL.

О(1) fromList

{-# LANGUAGE NoMonomorphismRestriction #-}

data DL a = Id
          | Cons a
          | Compose (DL a) (DL a)

fromList [] = Id
fromList (x:xs) = Compose (Cons x) (fromList xs)

toList x = help x []
    where help Id r = r
          help (Cons a) r = a:r
          help (Compose f g) r = help f $ help g r

empty = Id

singleton = Cons

cons x = append (singleton x)

append = Compose

snoc xs x = append xs (singleton x)

Θ(N)headtail[a] -> [a]toList

jbapple
источник
Итак, что вы получаете от лени, так это просто то, что запрос списка хвостов дважды не будет выполнять дорогостоящую операцию дважды, что приятно.
Роб Симмонс
@ Роб, я не понимаю, что ты имеешь в виду под этим.
jbapple
Я думаю, что мысль, которую я пытался сделать (плохо), проиллюстрирована этим примером: у меня есть чрезвычайно длинный список различий "xs", который я сделал, неоднократно "подсовывая" вещи в исходный список. При первом вызове «head xs» я ожидаю, что для выполнения отложенных вычислений потребуется O (n) времени; однако, поскольку это вычисление должно быть запомнено, второй вызов «head xs» (для тех же самых «xs») должен занять O (1) время.
Роб Симмонс
1
Ну, я согласен с этим, но лень, на которую я ссылался в своем ответе, был от fromList, который не используется в snoc или head. Так что, как бы педантично это ни было, меня смутило «что» в вашем утверждении «что вы получаете от лени». Я бы сказал, что ваш пример и мой - это две вещи, которые вы получаете от лени.
jbapple
Хорошо, и это разъяснение помогает мне лучше понять вашу предыдущую точку зрения.
Роб Симмонс
11

Да, границы зависят от предположения, что состав функции занимает постоянное время. В основном, если у вас есть список присоединения:

datatype 'a join = Nil | Cons of 'a * 'a join | Join of 'a join * 'a join

Очевидно, что конкатенация является постоянным временем, и это О(N)чтобы превратить это в список против. Если вы подумаете об обычном представлении замыканий, вы увидите, что это в основном то же представление указателя, что и обычное представление типов данных. (В качестве альтернативы вы можете просмотреть этот тип как нефункционализированный список различий.)

Нил Кришнасвами
источник