пример альтернативного ZipList в Haskell?

ZipList поставляется с функтором и прикладным экземпляром (управление.Applicative) а почему не альтернатива?

  • нет ли хорошего примера?
  • как насчет того, что предлагается ниже?
    • он испорчен?
    • это бесполезно?
    • существуют ли другие разумные возможности (например,Bool может быть моноидом двумя способами) и поэтому ни один не должен быть the пример

я искал " экземпляр альтернативного ZipList "(с кавычками, чтобы найти код первым) и только нашел библиотеку, некоторые учебники, конспекты лекций, но не фактический экземпляр.

Мэтт Фенвик сказал, что ZipList A будет только моноидом, если A есть. посмотреть здесь. Однако списки являются моноидами, независимо от типа элемента.

этот и другие ответы по AndrewC к тому же вопросу обсуждает, как Альтернативный экземпляр может выглядеть так. Он говорит

есть два разумных варианта для Zip [1,3,4] <|> Zip [10,20,30,40]:

  1. Zip [1,3,4] потому что это первое-согласуется с Maybe
  2. Zip [10,20,30,40] потому что он самый длинный - в соответствии с Zip [] отбрасываются

где Zip в основном ZipList. Я думаю, что ответ должен быть Zip [1,3,4,40]. Давайте посмотрим пример:

instance Aternative Zip where
  empty = Zip []
  Zip xs <|> Zip ys = Zip (go xs ys) where
    go [] ys = ys
    go (x:xs) ys = x : go xs (drop 1 ys)

только Zip a мы можем производить, не зная аргумента типа a и Zip [] :: Zip a, так что есть небольшой выбор для empty. Если пустой список является нейтральным элементом моноида, у нас может возникнуть соблазн использовать конкатенацию списка. Однако,go не (++) из-за drop 1. Каждый раз, когда мы используем одну запись первого списка аргументов, мы отбрасываем одну из вторых. Таким образом, у нас есть своего рода наложение: левый список аргументов скрывает начало правого (или все).

[ 1, 3, 4,40]   [10,20,30,40]   [ 1, 3, 4]   [ 1, 3, 4]
  ^  ^  ^  ^      ^  ^  ^  ^      ^  ^  ^      ^  ^  ^
  |  |  |  |      |  |  |  |      |  |  |      |  |  |
[ 1, 3, 4] |    [10,20,30,40]   []|  |  |    [ 1, 3, 4]
[10,20,30,40]   [ 1, 3, 4]      [ 1, 3, 4]   []

один интуиция за ziplists-это процессы: конечный или бесконечный поток результатов. При сжатии мы объединяем потоки, что отражается в прикладном экземпляре. Когда достигнут конец списка, поток не создает дополнительных элементов. Здесь пригодится альтернативный экземпляр: мы можем назвать замену, заняв ее, как только завершится процесс по умолчанию.

например, мы могли бы написать fmap Just foo <|> pure Nothing обернуть каждый элемент ziplist foo в a Just и далее с Nothing далее. Полученный ziplist бесконечен, возвращаясь к значению по умолчанию после того, как все (реальные) значения были использованы. Конечно, это можно сделать вручную, добавив бесконечный список внутри Zip конструктор. Тем не менее, вышеизложенное является более элегантным и не предполагает знания конструкторов, что приводит к более высокому повторному использованию кода.

нам не нужно какое-либо предположение о типе элемента (например, быть самим моноидом). В то же время определение не тривиально (as (<|>) = const будет). Он использует структуру списка путем сопоставления шаблонов в первом аргументе.

определение <|> приведенное выше ассоциативно, и пустой список действительно является пустым элементом. У нас есть

Zip [] <*> xs = fs <*> Zip [] = Zip []
(fs <|> gs) <*> xs = fs <*> xs <|> gs <*> xs
fs <*> (xs <|> ys) = fs <*> xs <|> fs <*> ys

таким образом, все законы, которые вы могли бы попросить, удовлетворены (что неверно для конкатенации списка).

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

zipToMaybe :: Zip a -> Maybe a
zipToMaybe (Zip []) = Nothing
zipToMaybe (Zip (x:_)) = Just x

maybeToZip :: Maybe a -> Zip a
maybeToZip Nothing = Zip []
maybeToZip (Just x) = Zip (repeat x)

являются морфизмами альтернатив (значение psi x <|> psi y = psi (x <|> y) и psi x <*> psi y = psi (x <*> y)).

Edit: для some/many методы, которые я бы предположил

some (Zip z) = Zip (map repeat z)
many (Zip z) = Zip (map repeat z ++ repeat [])

4 ответов


Метки / Indeces

интересные. Не совсем несвязанная мысль: ZipLists можно рассматривать как обычные списки с элементами, помеченными их (увеличивающимся) индексом позиции в списке. Приложение Zipping объединяет два списка путем сопряжения одинаково индексированных элементов.

представьте себе списки с элементами, помеченными (не уменьшающимися)Ord значения. Zippery приложение будет пара одинаково помеченных элементов, отбрасывая все несоответствия (это имеет свое применение); zippery альтернатива смогл выполнить приказ-сохранение Лев-предпочитая Союз на значения тегов (на регулярные списки тоже вроде союза).

это полностью согласуется с тем, что вы предлагаете для индексированных списков (он же ZipLists).

так что да, имеет смысл.

потоки

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

С этой интерпретацией потока вы не думаете с точки зрения всего списка, поэтому выбор самого длинного потока явно обманывает, и правильная интерпретация отказа от первого ZipList ко второму в определении <|> было бы сделать это на лету, как первый финиш, как вы говорите в своем примере.

сжатие двух списков вместе не делает этого просто из-за подписи типа, но это правильная интерпретация <|>.

Самый Длинный Список

когда вы zip два списка вместе, результат является минимальным из двух длин. Это потому, что это самый длинный список, который соответствует сигнатуре типа без использования ⊥. Ошибочно думать об этом как о выборе более короткой из двух длин - это самая длинная возможная.

аналогично <|> должен генерировать как можно дольше список, и он должен предпочесть левый список. Ясно, что он должен занять весь левый список и занять правый список, где левый левый, чтобы сохранить синхронизацию/быстроту.


ваш экземпляр в порядке, но он делает то, что ZipList не делает
(a) стремясь к самому длинному списку, и
(b) смешивание элементов между списками источников.

Zipping как операция останавливается на длине самого короткого списка.

вот почему я заключил в своем ответе:

таким образом, единственным разумным альтернативным экземпляром является:

instance Alternative Zip where
   empty = Zip []
   Zip [] <|> x = x
   Zip xs <|> _ = Zip xs

Это согласуется с альтернативными экземплярами для Maybe и анализаторы говорят, что вы должны сделать a если он не терпит неудачу и идет с b если он делает. Вы можете сказать, что короткий список менее успешен, чем длинный, но я не думаю, что вы можете сказать, что непустой список-это полный провал.

empty = Zip [] выбрал потому что он должен быть полиморфным типом элемента списка, и только такой список []

для баланса, я не думаю, что ваш пример ужасен, я думаю, что это чище, но эй-Хо, сверните свой собственный, Как вам нужно это!


моя руководящая интуиция для Alternative исходит из парсеров, которые предполагают, что если одна ветвь вашей альтернативы терпит неудачу, она должна быть искоренена, что приводит к Longest - style Alternative Это, вероятно, не очень полезно. Это было бы беспристрастно (в отличие от синтаксических анализаторов), но не в бесконечных списках.

опять же, все они должны сделать, как вы предлагаете, - это сформировать Monoid. Ваш остается предвзятым таким образом, что ZipList обычно не воплощает, хотя-вы могли бы четко сформировать отражены версию Alternative экземпляр так же легко. Как вы указываете, это соглашение с Maybe также, но я не уверен, что есть какая-либо причина для ZipList следовать этой конвенции.

нет никакого толкового some или many Я не верю, хотя несколько Alternatives на самом деле есть те-возможно, они были бы лучше изолированы в подкласс Alternative.

честно говоря, я не думаю, что ваше предложение-это плохой пример, но у меня нет уверенность в том, что это "альтернативный" экземпляр, подразумеваемый наличием ZipList. Возможно, было бы лучше посмотреть, где еще может применяться этот экземпляр "расширения" (деревья?) и написать его как библиотеку.


есть на самом деле разумный Alternative экземпляр для ZipList. Это происходит от бумага о свободных ближних полукольцах (где MonadPlus и Alternative примеры):

instance Alternative ZipList where
  empty = ZipList []

  ZipList xs <|> ZipList ys = ZipList $ go xs ys where
    go [] bs = bs
    go as [] = as
    go (a:as) (_:bs) = a:go as bs

Это более работоспособная версия исходного кода для него, которая была

ZipList xs <|> ZipList ys = ZipList $ xs ++ drop (length xs) ys