2014-10-15 2 views
0

정수를 생성하는 하스켈 함수를 작성하고 싶습니다. 이 함수는 처음 호출 될 때 1을 반환 한 다음 함수가 호출 될 때마다 다음 정수를 반환합니다.하스켈의 상태 생성 정수

파이썬에서 나는 발전기의 아이디어를 사용했습니다 - 정수 생성기는 다음과 같이 보일 수 있습니다 :

def intGen(): 
    x = 1 
    while True: 
     yield x 
     x += 1 

integer = intGen() 

# Use the generator 
next(integer) # 1 
next(integer) # 2 
next(integer) # 3 

어떻게 하스켈에서이 같은 작업을 수행합니까? 나는 아마도 모나드 모나드가 필요할 것임을 알고 있지만, 그것을 설정하는 방법에 대해서는 잘 모르겠습니다. 나는 모나드에 상당히 새로운 사람이다. IORef의 사용

+7

보통 하나가 바로'[1 ..]'같은 것을 사용하고 그 위에지도 것은 다른 값을 압축, 또는 인덱스를 이후 당신의'intGen'은 기본적으로 1에서 시작하는 게으른 무한한 정수리스트와 같습니다. 당신은 상태 모나드를 사용할 수 있습니다,하지만 나는 그것이 과도 할 것이라고 생각합니다. 당신의 '다음'사용은 가변적 인 상태를 의미합니다. 그리고 당신은 국가 모나드로 그것을 할 수 있습니다. 만약 당신이 IO 모나드에서 그것을 원한다면 IORefs를 사용할 수 있습니다. 정말 응용 프로그램에 따라 다릅니다. – bheklilr

+4

이것은 X/Y 문제처럼 보입니다. 달성하고자하는 최종 목표는 무엇입니까? 나는 두 번째 @bheklilr; 아마도이를 수행하는 일반적인 방법은 정수의 무한한 목록을 반환하는 함수 하나와 그 목록을 소비하고 그 함수를 사용하는 함수를 갖는 것입니다. 전체 프로그램이 의미하는 바를 모른 채 말하기는 어렵습니다. – MathematicalOrchid

답변

0

, 당신은

import Data.IORef 
import Control.Monad ((=<<)) 

type IntGen = IORef Int 

intGen :: IO IntGen 
intGen = newIORef 1 

next :: IntGen -> IO Int 
next gen = do 
    val <- readIORef gen 
    writeIORef gen $ val + 1 
    return val 

main :: IO() 
main = do 
    integer <- intGen 
    -- f =<< m == m >>= f 
    print =<< next integer 
    print =<< next integer 
    print =<< next integer 

처럼 뭔가를 할 수 아니면 순수하게이 작업을 수행 할 State 모나드를 사용할 수 있습니다 : 두 번째에서

import Control.Monad.State 

next :: Monad m => StateT Int m Int 
next = do 
    val <- get 
    put $ val + 1 
    return val 

app :: StateT Int IO() 
app = do 
    let stprint = liftIO . print 
    stprint =<< next 
    stprint =<< next 
    stprint =<< next 

main :: IO() 
main = void $ runStateT app 1 

을 초기 값은 1입니다 runStateT에 제공되므로 다른 값에서 시작할 수 있다는 점에서보다 유연합니다.


일반적으로 느리게 생성 된 정수 값이 필요할 때 목록이 이동하는 방법입니다. 예를 들어, 내가

def processFile(directory): 
    integer = intGen() 
    for fname in os.listdir(directory): 
     full_fname = os.path.join(directory, fname) 
     if os.path.isfile(full_fname): 
      i = next(integer) 
      new_fname = '{}-{}'.format(i, fname) 
      os.rename(full_fname, os.path.join(directory, new_fname)) 

같은 것을 가질 수 있지만 하스켈에서 내가

import Control.Monad 
import System.Directory 
import System.FilePath 

processFiles :: FilePath -> IO() 
processFiles directory = do 
    contents <- getDirectoryContents directory 
    files <- filterM doesFileExist $ map (directory </>) contents 
    forM_ (zip [1..] files) $ \(i, fname) -> do 
     let newFName = show i ++ "-" ++ fname 
     renameFile (directory </> fname) (directory </> newFName) 
+0

'next'에 대한 당신의 타입 시그니처는'Monad m' 컨텍스트를 빠뜨려서 타입 에러를냅니다. – dfeuer

+0

@dfeuer 고마워요. 이것은 먼저 테스트하지 않고 코드를 게시하기위한 것입니다. – bheklilr

1

하스켈 같은 것을 쓸 원합니다 것은 순수 함수는 같은 인수에 다른 데이터를 생성 할 수 없습니다.

실제로 사용하려면 IORef aSTRef a과 같이 IO a 또는 ST a 데이터를 사용해야합니다.

하지만 다른 방법으로 순수 방법을 사용할 수 있습니다

intGen = [1 .. 10] 

intRes = map next intGen