Как реализовать функции, работающие с UnionM [UnionM SymInteger]?

Речь идет о библиотеке Гризетта.

Позвольте мне сначала объяснить мое понимание того, зачем нам нужно это чудовище. По сути, я хочу работать со списками 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, кажется подозрительным.

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

Кажется, речь идет о конкретной библиотеке. Какая библиотека?

Li-yao Xia 02.05.2024 17:48

Речь идет о Гризетте (github.com/lsrcz/grisette). Извините, у меня это было в заголовке, но кто-то удалил. Я добавил его в тело.

Stefanos Baziotis 02.05.2024 22:25

Вероятно, вам больше повезет, если вы спросите их в системе отслеживания проблем или отправите авторам электронное письмо.

Li-yao Xia 03.05.2024 00:13

@Li-yaoXia, может быть, но вопрос о том, как функции в библиотеках, здесь не является не по теме, даже если библиотека немного нишевая.

Cubic 03.05.2024 10:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Редактировать после получения комментариев

После получения комментариев я думаю, что вам нужна функция 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].

Stefanos Baziotis 04.05.2024 01:13

Кстати, если вам действительно нужно работать с UnionM [SymInteger], то я не могу понять, как даже сделать простую функцию, которая находит минимальное значение UnionM [SymInteger] и возвращает SymInteger, а не Union SymInteger.

Stefanos Baziotis 04.05.2024 01:15

Позвольте мне прочитать ваши обновления. До этого для работы с UnionM SymInteger есть еще одна полезная функция — simpleMerge. Он объединяет UnionM просто объединяемого типа с одним значением.

Sirui Lu 04.05.2024 01:45

Кроме того, еще один совет по написанию кода с помощью Grisette заключается в том, что обычно вам нужно обернуть только результат, а не аргументы функции в UnionM или другую монаду. Выполнение такой распаковки на месте вызова обычно облегчает чтение кода. Это означает, что, на мой взгляд, symListDrop :: [SymInteger] -> SymInteger -> UnionM [SymInteger] — лучший выбор.

Sirui Lu 04.05.2024 01:49

Еще раз спасибо за все ответы! Я узнал достаточно, чтобы понять, как должна работать библиотека. Последний комментарий имеет большой смысл. Я подумал, что если вам где-то нужен UnionM [SymInteger], то вся библиотека должна работать над этим. Но вы можете просто вернуть это и распаковать, как вы сказали. Вот, например, капля на капле: firstDrop = series_drop_first mySymList (head mySymList) dropOnDrop :: UnionM [SymInteger] dropOnDrop = do unpk <- firstDrop series_drop_first unpk (head unpk)

Stefanos Baziotis 04.05.2024 04:22

Другие вопросы по теме