Haskell: одноместный takeWhile?

Я думаю, что клиент похож на нормальный асинхронный запрос Ajax, но Вы ожидаете, что он займет много времени для возвращения.

сервер тогда похож на это.

while (!hasNewData())
    usleep(50);

outputNewData();

Так, запрос Ajax переходит к серверу, вероятно, включая метку времени того, когда это было последнее обновление так, чтобы Ваш hasNewData() знал, какие данные Вы уже получили. Сервер тогда находится в цикле, спя, пока новые данные не доступны. Все время Ваш запрос Ajax все еще соединен, просто зависнув там ожидающий данных. Наконец, когда новые данные доступны, сервер дает их Вашему запросу Ajax и закрывает соединение.

12
задан dave4420 15 March 2012 в 22:47
поделиться

4 ответа

Вы можете определить последовательность как

sequence xs = foldr (liftM2 (:)) (return []) xs

Проблема с liftM2 , которую вы видели у вас нет возможности остановить m2 , что может быть launchTheMissiles !

liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
liftM2 f m1 m2 = do
    x1 <- m1
    x2 <- m2
    return (f x1 x2)

Использование охранника , как показано ниже, кажется привлекательным:

sequenceUntil p xs = foldr (myLiftM2 p (:)) (return []) xs
  where myLiftM2 p f m1 m2 = do
            x1 <- m1
            guard $ p x1
            x2 <- m2
            return (f x1 x2)

Приведенный выше код не будет работать в вашем приложении, потому что монада ввода-вывода не является экземпляром MonadPlus .

Так что держите его за руку еще немного

module Main where

import Control.Monad

printx :: Int -> IO Int
printx x = do
    print x
    return x

sequenceUntil :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
sequenceUntil p xs = foldr (myLiftM2 (:) []) (return []) xs
  where myLiftM2 f z m1 m2 = do
            x1 <- m1
            if p x1 then do x2 <- m2
                            return $ f x1 x2
                    else return z

main :: IO ()
main = do
  let as :: [IO Int]
      as = map printx [1..10]
  ys <- sequenceUntil (< 4) as
  print ys

Хотя как - это список действий от 1 до 10, вывод будет

1
2
3
4
[1,2,3]

Отбрасывание результатов тогда тривиально:

sequenceUntil_ :: (Monad m) => (a -> Bool) -> [m a] -> m ()
sequenceUntil_ p xs = sequenceUntil p xs >> return ()

main :: IO ()
main = do
  let as :: [IO Int]
      as = map printx [1..]
  sequenceUntil_ (< 4) as

Обратите внимание на использование [1 .. ] , который показывает, что новый комбинатор поддерживает лень .


Вы можете предпочесть spanM :

spanM :: (Monad m) => (a -> Bool) -> [m a] -> m ([a], [m a])
spanM _ [] = return ([], [])
spanM p (a:as) = do
  x <- a
  if p x then do (xs,bs) <- spanM p as
                 return (x:xs, bs)
         else return ([x], as)

Обратите внимание, что он немного отличается от span тем, что он включает неисправный элемент в список результатов. Вторая пара - это оставшиеся действия. Например:

*Main> (xs,bs) <- spanM (< 4) as
1
2
3
4
*Main> xs  
[1,2,3,4]
*Main> sequence bs
5
6
7
8
9
10
[5,6,7,8,9,10]

Еще одна альтернатива:

untilM :: Monad m => (a -> Bool) -> [m a] -> m ()
untilM p (x:xs) = do
  y <- x
  unless (p y) $ untilM p xs

Обратите внимание, что смысл предиката дополняется:

*Main> untilM (>= 4) as
1
2
3
4
15
ответ дан 2 December 2019 в 03:14
поделиться

Я не думаю, что в стандартной библиотеке есть что-то вроде takeWhileM , но вы могли бы написать его самостоятельно, чтобы выполнялось столько операций ввода-вывода, сколько необходимо:

takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
takeWhileM _ [] = return []
takeWhileM p (a:as) =
   do v <- a
      if p v
         then do vs <- takeWhileM p as
                 return (v:vs)
         else return []

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

*Main> takeWhileM (<4) (map f [1..5])
1
2
3
4
[1,2,3]
14
ответ дан 2 December 2019 в 03:14
поделиться

Правка: Теперь я понимаю, что вы ищете.

gbacon опубликовал красивую функцию sequenceWhile , которая является почти «примитивом», который вам нужен.

На самом деле, поскольку вас интересуют только побочные эффекты, последовательности , хотя_ должно быть достаточно. Вот определение (опять же, вдохновленное gbacon, проголосуйте за него!):

sequenceWhile_ :: (Monad m) => (a -> Bool) -> [m a] -> m ()
sequenceWhile_ p xs = foldr (\mx my -> mx >>= \x -> when (p x) my)
                            (return ()) xs

Вы называете это так:

Prelude Control.Monad> sequenceWhile (<4) $ map f [1..]

Исходный ответ:

Вы не можете просто «снять» значения из IO Монада для использования с takeWile , но вы можете «поднять» takeWhile для использования в монаде!

Функция liftM примет функцию (a -> b) в функцию (ma -> mb) , где m - монада.

(В качестве примечания, вы можете найти такую ​​функцию, выполнив поиск по ее типу в Hoogle , в данном случае выполнив поиск по: Monad m => (a -> b) -> (ma -> mb) )

С помощью liftM вы можете сделать это:

Prelude> :m + Control.Monad
Prelude Control.Monad> let f x = print x >> return x
Prelude Control.Monad> liftM (takeWhile (<4)) $ mapM f [0..5]
0
1
2
3
4
5
[0,1,2,3]

Возможно, это не то, что вам нужно. mapM применяет функцию f ко всему списку последовательно перед возвратом списка. Полученный список затем передается поднятой функции takeWhile .

Если вы хотите остановить печать после третьего элемента, вам придется прекратить вызов print. Это означает, что не применяйте f к такому элементу. Таким образом, вы получите что-то простое, например:

Prelude> mapM_ f (takeWhile (<4) [0..5])

Кстати, стоит ли вам задаться вопросом , почему mapM сначала напечатает все, а затем вернет список.

10
ответ дан 2 December 2019 в 03:14
поделиться

Вы можете использовать один из пакета «Список» .

import Control.Monad.ListT (ListT)
import Data.List.Class (execute, fromList, joinM, takeWhile)
import Prelude hiding (takeWhile)

f x = print x >> return x
main =
  execute . takeWhile (< 4) .
  joinM $ fmap f (fromList [0..5] :: ListT IO Int)
  • fromList [0..5] создает монадический список, содержащий 0..5, который не выполняет монадических действий
  • fmap f с этим списком приводит к ListT IO (IO Int) , который по-прежнему не выполняет монадических действий, только содержит их .
  • joinM превращает это в ListT IO Int . каждое содержащееся действие будет выполнено, когда элемент будет потреблен, и его результатом будет значение в списке.
  • takeWhile обобщен для любого списка . И [] , и « Monad m => ListT m » являются экземплярами List .
  • execute использует монадический список, выполняя все его действия.
  • Если вас интересуют результаты, вы можете использовать «toList :: List m => ma -> ItemM m [a]» ItemM (ListT IO) » равно IO ). поэтому в данном случае это « toList :: ListT IO a -> IO [a] ». Еще лучше вы можете продолжать использовать функции высшего порядка, такие как scanl и т. Д. Для обработки монадического списка во время его выполнения.
6
ответ дан 2 December 2019 в 03:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: