Фильтрация списка по состоянию в Haskell

Я изучал реализации ListT, чтобы найти хороший способ сделать что-то вроде этого:

func list = do
  set <- get -- Data.Set
  el <- list
  guard $ Set.notMember el set
  return el

Я знаю, что мог бы использовать ListT.fromFoldable, но я хочу иметь возможность использовать этот поток как часть более крупного конвейера обработки без преобразования из списка в поток и обратно на каждом этапе. Возможно, что-то вроде этого:

func' list = (ListT.toReverseList . ListT.take 5 . func . ListT.fromFoldable) list

Насколько я понимаю, здесь следует использовать потоковый подход. Но я не понимаю, как это сделать, например. пакет list-t. Может ли обход каким-то образом отфильтровать результаты из потока? Я не вижу, чтобы люди спрашивали об этом, так что, может быть, сам подход ошибочен?

Я добавил ListT решение к своему ответу.

effectfully 13.04.2024 13:12
Стоит ли изучать 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
72
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Отвечая на исходный вопрос перед редактированием:

Насколько я понимаю, здесь следует использовать потоковый подход. Но я не понимаю, как это сделать, например. пакет list-t.

Вы можете использовать обычные списки, если ваша монада достаточно ленива:

import Control.Monad (filterM)
import Control.Monad.Trans.State.Lazy (State, get)
import Data.Set (Set, member)

filterStateLazy :: Ord a => [a] -> State (Set a) [a]
filterStateLazy = filterM $ \x -> do
    s <- get
    pure $ x `member` s

-- >>> import Control.Monad.Trans.State.Lazy (evalState)
-- >>> import qualified Data.Set as Set
-- >>> take 5 . evalState (filterStateLazy [1..]) $ Set.fromList [1, 6, 22, 39, 54]
-- [1,6,22,39,54]

Если вы действительно хотите ListT, вы также можете использовать его, и вам не понадобится ленивая монада:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}

import ListT
import Control.Monad.State.Strict
import Data.Set (Set, member)

filterStateLazy :: (MonadState (Set a) m, Ord a) => ListT m a -> ListT m a
filterStateLazy (ListT run) = ListT $ run >>= \case
    Nothing -> return Nothing
    Just (x, rest) -> do
        s <- get
        if x `member` s
            then pure $ Just (x, filterStateLazy rest)
            else uncons $ filterStateLazy rest

-- >>> import Control.Monad.Trans.State.Lazy (evalState)
-- >>> import qualified ListT as ListT
-- >>> import qualified Data.Set as Set
-- >>> evalState (ListT.toList . ListT.take 5 . filterStateLazy $ ListT.fromFoldable [1..]) $ Set.fromList [1, 6, 22, 39, 54]
-- [1,6,22,39,54]

Хотя ответ @effectfully работает нормально, вы, вероятно, ищете mfilter от Control.Monad:

func :: (MonadPlus m, MonadState (Set a) m, Ord a) => m a -> m a
func act = do
  st <- get
  mfilter (`Set.notMember` st) act

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

mfilterM :: (MonadPlus m) => (a -> m Bool) -> m a -> m a
mfilterM f act = do
  a <- act
  b <- f a
  if b then pure a else empty

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

unique :: (MonadPlus m, MonadState (Set a) m, Ord a) => m a -> m a
unique = mfilterM firstTime
  where firstTime x = do
          isnew <- gets (Set.notMember x)
          when isnew $ modify (Set.insert x)
          pure isnew

В контексте и заимствовании примера @effectfull:

import ListT
import Control.Monad
import Control.Monad.State
import Control.Applicative
import Data.Set (Set)
import qualified Data.Set as Set

func1 :: (MonadPlus m, MonadState (Set a) m, Ord a) => m a -> m a
func1 act = do
  st <- get
  mfilter (`Set.notMember` st) act

mfilterM :: (MonadPlus m) => (a -> m Bool) -> m a -> m a
mfilterM f act = do
  a <- act
  b <- f a
  if b then pure a else empty

unique :: (MonadPlus m, MonadState (Set a) m, Ord a) => m a -> m a
unique = mfilterM firstTime
  where firstTime x = do
          isnew <- gets (Set.notMember x)
          when isnew $ modify (Set.insert x)
          pure isnew

runM :: ListT (State (Set a)) a -> Set a -> [a]
runM act state0 = evalState (ListT.toList act) state0

main :: IO ()
main = do
  print $ runM (ListT.take 5 . func1 . ListT.fromFoldable $ [1..]) (Set.fromList [1,6,22,39,54])
  print $ runM (unique . ListT.fromFoldable $ [1,2,3,4,5,1,3,6,5,7]) (Set.empty)

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

effectfully 13.04.2024 23:12

Истинный. Я добавил новую функцию mfilterM и пример для решения этой проблемы.

K. A. Buhr 14.04.2024 21:50

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

effectfully 14.04.2024 21:57

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