切断懒惰列表生成

时间:2011-02-11 00:31:29

标签: haskell functional-programming

我想知道是否有人对如何创建一个能够获取列表的函数有任何见解,并且只返回可以在x时间内生成的术语。

例如,我有一个函数需要10分钟才能返回几个术语。我不想猜测我能够生成多少个术语(使用x),我只想将无限列表输入到我的低效函数中,并有一个单独的函数来决定何时超时。

这样的事情:[5,7,10] = takeUntilTime (10 sec) . inefficientFunction $ [1..]

我对haskell很新,但我想我可以编写该函数来在每个新术语生成后检查计时器,如果时间已经过去就停止。

然而,如果第四个任期需要永恒呢?有没有办法阻止lowfficientFunction完成第4个任期的生成,即使它已经开始了?

我对一个直截了当的答案没有寄予厚望,但我对此表示赞赏。感谢。

4 个答案:

答案 0 :(得分:10)

没有经过多次测试,但这似乎有效,至少是小规模的。

import Control.Concurrent
import Control.Exception
import Control.Monad

takeUntilTime :: Int -> [a] -> IO [a]
takeUntilTime limit list = do
    me <- myThreadId
    bracket (forkIO $ threadDelay limit >> throwTo me NonTermination) killThread
        . const $ tryTake list
  where
    (/:/) = liftM2 (:)
    tryTake list = handle (\NonTermination -> return []) $
        case list of
            [] -> return []
            x:xs -> evaluate x /:/ tryTake xs
ghci> takeUntilTime 1000000 [x | x <- [1..], x == 0]
[]
(1.02 secs, 153111264 bytes)
ghci> takeUntilTime 1000000 [maximum [y `mod` x | y <- [1..100000]] | x <- [1..]]
[0,1,2,3]
(1.00 secs, 186234264 bytes)

答案 1 :(得分:6)

我认为你想要的是:System.Timeout

它允许IO事件超时。它有一个类型为:

的函数
timeout :: Int -> IO a -> IO (Maybe a)

我认为你应该可以使用它。我这样做的方法是使用超时功能和迭代深化方法将更多内容添加到您的函数中,直到它工作。这样,当一个人终于达到超时时,你知道你已经走得太远了。

答案 2 :(得分:0)

我想你问了两个问题。 1.如何设置从无限列表中获取元素的时间限制。 2.如何终止阻塞功能,或创建非阻塞功能,如某些网络IO功能。

对于第二个,您可以参考http://twelvestone.com/forum_thread/view/32028获取一些想法。 然后对于第一个,你可以假设inefficientFunction是非阻塞的。

答案 3 :(得分:0)

受到ephemient的回答的启发,我想我会发布他的解决方案的两个概括。

解决方案1 ​​

import Control.Concurrent
import Control.Exception
import Control.Monad

doUntilTime :: Int -> IO a {- must handle NonTermination exception! -} -> IO a
doUntilTime limit action = do
  me <- myThreadId
  bracket (forkIO $ threadDelay limit >> throwTo me NonTermination) killThread
           . const $ action

实施例

(/:/) = liftM2 (:)
tryTake list = handle (\NonTermination -> return []) $
   case list of
     [] -> return []
     x:xs -> evaluate x /:/ tryTake xs

test = do
  res <- doUntilTime 1000000 (tryTake [1..])
  putStrLn (show $ length res)

解决方案2

在此解决方案中,您将默认值输入预递归函数传递给doUntilTime

预递归函数以一种样式编写,其中第一个参数,我们称之为self,用于之前使用过递归调用的地方。这样可以省去显式处理NonTermination异常的麻烦。

import Control.Concurrent
import Control.Exception
import Control.Monad

doUntilTime :: Int -> a -> b -> ((a -> IO b) -> (a -> IO b)) -> IO b
doUntilTime limit input defaultVal action = do
    me <- myThreadId
    bracket (forkIO $ threadDelay limit >> throwTo me NonTermination) killThread
        . const $ fix action input
  where
    fix f = f (\a -> handle (\NonTermination -> return defaultVal) (fix f a))

(/:/) = liftM2 (:)

-- tryTake is written in a "pre-recursive" style. There are no
-- occurrences of "tryTake" in the body only occurrences of
-- "self" (the first parameter)
tryTake :: ([Int] -> IO [Float]) -> ([Int] -> IO [Float])
tryTake self list =
   case list of
     [] -> return []
     x:xs -> evaluate (fromIntegral x) /:/ self xs

test = do
  res <- doUntilTime 1000000 [1..] [] tryTake
  print (length res)