Как выполнить фильтрацию на основе совпадения с монадическим образцом?

У меня такая проблема:

Учитывая тип data T = T (Maybe Int), как я могу отфильтровать список, получая значения, отличные от Nothing?

Вход

a = [T (Just 3), T Nothing, T (Just 4)]

Желаемый результат

b = [T (Just 3), T (Just 4)]

Я пробовал что-то вроде:

b = filter (\x-> x@(Just t)) a 

... думая, что я могу фильтровать по совпадению с образцом, но получаю сообщение об ошибке:

Pattern syntax in expression context: x@(Just t)
    Did you mean to enable TypeApplications?

Позже я хочу сможет распаковать внутреннее значение (в Just) и использовать его соответственно.

вы можете использовать filter (\x -> case x of (Just _) -> True ; _ -> False) a или даже filter (\case (Just _) -> True ; _ -> False) (последний с расширением LambdaCase).

Will Ness 14.09.2018 11:54
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
258
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Я думаю, что здесь мы можем лучше использовать семантику сопоставления с образцом для понимания списка:

result = [ e | e@(T (Just _)) <- a]

Таким образом, здесь мы перечисляем элементы e в a, и если сопоставление с образцом с T (Just _) успешно, мы выводим его в список результатов.

Однако, если вы хотите распаковать значений, инкапсулированных в T (Just x), мы, таким образом, можем выполнить сопоставление с образцом и получить обернутый элемент:

result = [ e | T (Just e) <- a]

Таким образом, это будет не только «фильтровать» значения, но и одновременно распаковывать их. Таким образом, T Nothing игнорируются, сохраняются только упакованные T (Just e), а соответствующие e попадают в список.

Это отвечает на букву вопроса, но я уверен, что [e | T (Just e) <- a] лучше отвечает духу вопроса.

Daniel Wagner 13.09.2018 16:08

Да, это именно то, что я хотел позже распаковать внутреннее значение и использовать его соответственно.

Bercovici Adrian 13.09.2018 16:14

@BercoviciAdrian: см. Обновление.

Willem Van Onsem 13.09.2018 16:14

Не могли бы вы рассказать мне, почему я получаю Pattern bindings not allowed in instance declaration вместо instance Show File where ; (Dfile ls)=Data.List.intercalate ',' [show e|T (Just e) <- ls]. Может, заслуживает отдельного вопроса, но я не уверен. хотя.

Bercovici Adrian 13.09.2018 16:49

@BercoviciAdrian Вы, наверное, имеете в виду ... where show (Dfile ls)= ...

chi 13.09.2018 18:01

Да, извините, я забыл это добавить.

Bercovici Adrian 13.09.2018 18:02

Сопоставление с образцом работает только в аргумент функции, но не в теле. Шаблон, который вам нужно сопоставить, - это T с катаморфизмом, подобным maybe, для преобразования обернутого значения в логическое.

Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (\(T x) -> maybe False (const True) x) a
[T (Just 3),T (Just 4)]

Однако обратите внимание, что maybe False (const True) уже определен как Data.Maybe.isJust.

Prelude> import Data.Maybe
Prelude> filter (\(T x) -> isJust x) a
[T (Just 3),T (Just 4)]

Вы можете упростить предикат, если у вас есть функция типа T -> Maybe Int для компоновки с isJust. Например:

Prelude> data T = T { getT :: Maybe Int } deriving Show
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (isJust . getT) a
[T {getT = Just 3},T {getT = Just 4}]

Если вы хотите получить список значений Int в вашем списке T (набранный таким образом, как [T] -> [Int]), тогда mapMaybe из Data.Maybe уже делает почти то, что вы хотите. Все, что вам нужно, кроме этого, - это функция распаковки типа T -> Maybe Int.

import Data.Maybe ( mapMaybe )

data T = T (Maybe Int)
  deriving (Eq, Show)

unT :: T -> Maybe Int
unT (T x) = x

filterTs = mapMaybe unT

Потом:

λ a = [T (Just 3), T Nothing, T (Just 4)]
a :: [T]

λ filterTs a
[3,4]
it :: [Int]

На мой взгляд, использование этой операции фильтра типа [T] -> [Int] более полезно, чем возвращение значений T, содержащих значения, отличные от Nothing; причина в том, что даже если вы отфильтруете a до [T (Just 3), T (Just 4)], тогда код, который имеет дело с этим более поздним еще, должен соответствовать шаблону на Just, чтобы получить значения Int, даже если вы знаете, что Nothing1 никогда не будет, потому что T все еще жестко запрограммирован, чтобы содержать Nothing.

Как правило, если вы выполняете фильтрацию (или по умолчанию, и т. д.), Чтобы гарантировать отсутствие случая, вам следует подумать о преобразовании в тип, который больше не имеет случая. Обычно это упрощает работу с результирующими данными (например, нет необходимости в сопоставлении с шаблоном или fmap, чтобы попасть внутрь избыточных слоев), а также помогает избежать ошибок.

Также есть catMaybes :: [Maybe a] -> [a], который выполняет эту «фильтрацию Nothing без отображения», но, поскольку вы отображаете для распаковки, конструктор T лучше подходит.


1 И эта ситуация «Я знаю, что здесь никогда не будет mapMaybe, поэтому мне не нужно с этим справляться» - очень богатый источник ошибок, которые могут сломаться, когда что-то изменит этот скрытый инвариант в будущем. Так что написание кода, использующего преимущества этого знания о том, что Nothing «не может» быть, даже не лучшая идея; вы должны еще обрабатывать оба случая!

Спасибо за ваши наблюдения, они очень ценны для меня как новичка. Я хотел знать на практике, что всякий раз, когда у меня есть вложенные maybe-ы в свои типы, как в примере oyu, поставляемом с T, я должен создавать методы, соответствующие шаблону -сопоставить и получить значение поля? (в вашем примере unT)?

Bercovici Adrian 14.09.2018 07:32

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