Речь идет о библиотеке Гризетта.
Позвольте мне сначала объяснить мое понимание того, зачем нам нужно это чудовище. По сути, я хочу работать со списками SymInteger
. Но, AFAIU, вам нужно обернуть SymInteger
в UnionM
всякий раз, когда результат операции не является простым значением (например, 2
или "a"
), а, например, условием (например, результатом mrgIf
).
Теперь предположим, что мы хотим создать функцию, которая принимает два [SymInteger]
и возвращает [SymInteger]
, имеющее поэлементный максимум из двух списков. Мы не можем сделать следующее:
pickLargest :: [SymInteger] -> [SymInteger] -> [SymInteger]
-- skip simple cases
pickLargest (x:xs) (y:ys) = (mrgIf (x .> y) x y) : (sumSym xs ys)
потому что mrgIf
возвращает UnionM SymInteger
, а не SymInteger
. Итак, нам нужно [UnionM SymInteger]
, чтобы наши функции были общими, например:
pickLargest :: [UnionM SymInteger] -> [UnionM SymInteger] -> [UnionM SymInteger]
-- ...
pickLargest (x:xs) (y:ys) = (mrgIf (x .> y) x y) : (pickLargest xs ys)
Теперь предположим, что я также хочу создать функцию, которая удаляет элемент из списка, если он существует. Я не могу использовать symDrop, потому что для этого требуется SymInteger
, а не UnionM SymInteger
. Итак, следующее не будет работать:
myVar :: [UnionM SymInteger]
myVar = pickLargest ["a", "b"] ["c", "d"]
dropOnSym = print (symDrop (head myVar) myVar)
Примечание. Использование head myVar — это просто выбор элемента, который наверняка существует. В идеале мне нужна функция, которая удаляет элемент из [SymInteger], который может существовать, а может и не существовать.
потому что head myVar
имеет тип UnionM SymInteger
(и AFAIU, это тоже не сработает
если я хочу вызвать symDrop
по результату symDrop
). Наконец, поскольку мы не знаем длину полученного списка, имеет смысл заключить его в UnionM
.
Таким образом, единственный способ реализовать symListDrop, который я нашел, это:
symListDrop :: UnionM [UnionM SymInteger] -> UnionM SymInteger -> UnionM [UnionM SymInteger]
symListDrop m_ls el = do
ls <- m_ls
case ls of
[] -> mrgReturn []
(x:xs) -> mrgIf (x .== el)
(symListDrop (mrgReturn xs) el)
(do
res <- (symListDrop (mrgReturn xs) el)
(mrgReturn (x:res)))
Боюсь, это просто я очень-очень стараюсь... В частности, тот факт, что мне приходится распаковывать UnionM
, кажется подозрительным.
Итак, есть ли у вас предложение о том, как реализовать эту функцию или, альтернативно, другой способ моделирования этих проблем?
Речь идет о Гризетте (github.com/lsrcz/grisette). Извините, у меня это было в заголовке, но кто-то удалил. Я добавил его в тело.
Вероятно, вам больше повезет, если вы спросите их в системе отслеживания проблем или отправите авторам электронное письмо.
@Li-yaoXia, может быть, но вопрос о том, как функции в библиотеках, здесь не является не по теме, даже если библиотека немного нишевая.
После получения комментариев я думаю, что вам нужна функция deleteFirst
, подобная той, которая обсуждается в этом ТАК ответьте.
Чтобы сделать это символическим, вам не обязательно заключать аргументы функции в UnionM
, вместо этого вам нужно просто обернуть в них результат.
symDeleteFirst :: [SymInteger] -> SymInteger -> UnionM [SymInteger]
symDeleteFirst [] _ = mrgReturn []
symDeleteFirst (x:xs) v = mrgIf (x .== v) (mrgReturn xs) (mrgFmap (x:) $ symDeleteFirst xs v)
Эту функцию все еще можно использовать на UnionM [SymInteger]
следующим образом:
a :: UnionM [SymInteger]
a = mrgIf "x" (return ["a", "b"]) (return ["c", "d", "e"])
n :: SymInteger
n = "n"
do
v <- a
symDeleteFirst v n
Результат будет следующим. Это немного сложно, хотя, вероятно, это один из самых простых результатов, поскольку в программе много ветвей.
{If (&& x (|| (= a n) (= b n)))
[(ite (= a n) b a)]
(If (|| x (|| (= c n) (|| (= d n) (= e n))))
[(ite x a (ite (= c n) d c)),(ite x b (ite (|| (= c n) (= d n)) e d))]
[c,d,e])
}
Для функции минимума, упомянутой в комментарии, есть функция symMinimum, которая работает на [SymInteger]
.
Чтобы использовать его на UnionM [SymInteger]
, вы можете использовать fmap для получения результата UnionM SymInteger
, а затем объединить UnionM SymInteger
с SymInteger
с помощью simpleMerge:
a :: UnionM [SymInteger]
a = mrgIf "x" (return ["a", "b"]) (return ["c", "d", "e"])
simpleMerge $ symMinimum <$> a
Это даст вам
(ite x (ite (<= b a) b a) (ite (<= e (ite (<= d c) d c)) e (ite (<= d c) d c)))
Спасибо за интерес к библиотеке Гризетт, автором которой являюсь я. Я только что создал обсуждения GitHub, чтобы дальнейшие обсуждения могли проходить там.
Если вы еще не ознакомились, недавно для Grisette был создан небольшой туториал.
В большинстве случаев вам не обязательно использовать UnionM SymInteger
, а ваш pickLargest
можно реализовать с помощью mrgIte.
pickLargest :: [SymInteger] -> [SymInteger] -> [SymInteger]
-- skip simple cases
pickLargest (x:xs) (y:ys) = (mrgIte (x .> y) x y) : (sumSym xs ys)
symDrop
это не то, что вам нужно. Возможно, вы захотите обновить Grisette до последней версии и использовать mrgDelete . symMax там же доступен.
С symMax
существует еще более простое решение:
pickLargest :: [SymInteger] -> [SymInteger] -> [SymInteger]
pickLargest = zipWith symMax
Ваш symListDrop
вызывает себя с теми же операндами, и поэтому он бесконечно рекурсивно.
SymInteger
У меня к вам первый вопрос: нужна ли symDrop
функция? Его семантика аналогична drop
в base
, где второй SymInteger
— это количество элементов, которые нужно удалить из головы списка, а не сам элемент.
Я предлагаю вам обновиться до последней версии пакета Grisette (v0.5.0.1 на сегодняшний день), который доступен на ночных снимках Hackage или Stackage. Почти все функции из Data.List
имеют символическую версию, и функция mrgDelete может оказаться тем, что вам нужно. Ожидайте некоторой несовместимости между версиями, поскольку некоторые функции переименованы с использованием более последовательной схемы именования, например, symDrop
был переименован в mrgDrop, поскольку он возвращает объединение. (Оценочные) сложности для операторов также доступны в документации.
SimpleMergeable
и mrgIte
Контейнер UnionM
представлен для обработки многопутевого выполнения сложных структур данных. Эти структуры данных не способны самостоятельно обрабатывать условия пути, поэтому мы храним их в нескольких ветвях контейнера UnionM
.
Однако некоторые структуры данных, например, символические примитивные типы (SymBool
, SymInteger
и т. д.), кортежи символических примитивных типов ((SymBool, SymInteger)
), способны самостоятельно содержать условия пути. Эти структуры данных реализуют класс типа SimpleMergeable, который предоставляет функцию mrgIte.
mrgIte :: (SimpleMergeable a) => SymBool -> a -> a -> a
На первый взгляд это может сбить с толку, потому что у нас есть две разные версии «разделения путей», но на самом деле это сделано намеренно, а mrgIf
и mrgIte
имеют разную семантику. mrgIf
аналогичен структуре потока управления, например, оператору if
в C, тогда как mrgIte
является тернарным оператором для построения выражения, например, ?:
в C.
UnionM SymInteger
...Иногда невозможно избежать использования UnionM
просто объединяемых типов, например, когда у вас есть общая структура данных, которая использует UnionM
внутри себя. Grisette предоставляет помощники для работы с ними (например, (.#) и onUnion), а сигнатура типа следующая:
onUnion :: (SimpleMergeable r, Mergeable a) => (a -> r) -> UnionM a -> r
Ваша функция вызывает саму себя (symListDrop m_ls m_el
) с точно такими же операндами в последней строке.
Теперь предположим, что мы хотим создать функцию, которая принимает два
[SymInteger]
и возвращает[SymInteger]
, имеющее поэлементный максимум из двух списков.
Это можно реализовать с помощью zipWith
и symMax с последней версией Grisette:
pickLargest :: [SymInteger] -> [SymInteger] -> [SymInteger]
pickLargest = zipWith symMax
Привет, Сируи, спасибо! Что ж, думаю, я слишком много боролся с системой типов, чтобы понять очевидную бесконечную рекурсию. Я обновлю пост. Короче говоря, в идеале мне нужна функция, которая удаляет элемент из списка, который может существовать, а может и не существовать. Использование результата head
выше было простым вариантом элемента, который наверняка существует. Я добавил обновленную реализацию, которая не бесконечна, но она по-прежнему кажется неправильной, опять же потому, что мне нужно распаковать монаду. Наконец, AFAIU, вы не можете избежать возврата UnionM [SymInteger]
, что заставляет остальную часть системы работать с UnionM [SymInteger]
.
Кстати, если вам действительно нужно работать с UnionM [SymInteger]
, то я не могу понять, как даже сделать простую функцию, которая находит минимальное значение UnionM [SymInteger]
и возвращает SymInteger
, а не Union SymInteger
.
Позвольте мне прочитать ваши обновления. До этого для работы с UnionM SymInteger
есть еще одна полезная функция — simpleMerge. Он объединяет UnionM
просто объединяемого типа с одним значением.
Кроме того, еще один совет по написанию кода с помощью Grisette заключается в том, что обычно вам нужно обернуть только результат, а не аргументы функции в UnionM
или другую монаду. Выполнение такой распаковки на месте вызова обычно облегчает чтение кода. Это означает, что, на мой взгляд, symListDrop :: [SymInteger] -> SymInteger -> UnionM [SymInteger]
— лучший выбор.
Еще раз спасибо за все ответы! Я узнал достаточно, чтобы понять, как должна работать библиотека. Последний комментарий имеет большой смысл. Я подумал, что если вам где-то нужен UnionM [SymInteger]
, то вся библиотека должна работать над этим. Но вы можете просто вернуть это и распаковать, как вы сказали. Вот, например, капля на капле: firstDrop = series_drop_first mySymList (head mySymList) dropOnDrop :: UnionM [SymInteger] dropOnDrop = do unpk <- firstDrop series_drop_first unpk (head unpk)
Кажется, речь идет о конкретной библиотеке. Какая библиотека?