簡體   English   中英

使用可變狀態在Haskell中實現主循環

[英]Implement main loop in haskell using mutable state

我正在嘗試在Haskell中實現排序的主循環,在CI中將這樣編寫:

EntityInteraction *frame(Entity *eList, EntityInteraction *iList) {
    parseInteractions(eList, iList);
    return simulateEntities(eList);
}

int main() {
    Entity eList[] = {...}
    EntityInteraction *iList = NULL;
    while(true) {
        iList = frame(eList, iList);
    }
}

因此,我嘗試通過將框架設為遞歸函數來在haskell中復制此代碼:

frame :: [Entity] -> [EntityInteraction] -> IO ()
frame eList iList = do
    frame (parseInteractions iList eList) (simulateEntities eList)

main :: IO ()
main = do
    let entList = [...]
    frame entList []

但這只會導致堆棧溢出,因此我的問題是在haskell中使用可變狀態進行主循環的正確方法是什么?

(我從事C語言編程已有4年了,現在才開始學習haskell)

這是一個很有趣的現象,在這個空洞的例子中只有您遇到了。

首先,這是一個最小的示例:

frame :: [a] -> IO ()
frame eList = do    
    frame (id eList) 

main :: IO ()
main = do        
    frame [] 

如果我使用runghc運行它, runghc收到內存不足的錯誤。 但是,以下兩種方法均有效:(如果使用ghc -O2進行編譯,則實際上可能會得到輸出<<loop>>並終止程序runghc不會檢測到循環,並且您可以看到程序以恆定的方式運行空間。)

一種)

frame :: [a] -> IO ()
frame eList = do   
    frame eList

main :: IO ()
main = do        
    frame [] 

B)

frame :: [a] -> IO ()
frame eList = do    
    print eList
    frame (id eList) 

main :: IO ()
main = do        
    frame [] 

C)

frame :: [a] -> IO ()
frame eList = do   
    eList `seq` frame (id eList) 

main :: IO ()
main = do        
    frame [] 

這是什么原因? 好吧,尾遞歸本身不是問題。 沒有堆棧溢出,而是內存不足錯誤。 為什么,如果您的列表在每次循環迭代中都沒有實際更改?

好吧,他們! 函數應用程序本身會累積未經評估的重擊,因為您從未使用過這些值! 在所有工作示例中,唯一的區別是實際評估了值,並刪除了thunk。

因此,在錯誤的示例中,函數調用序列看起來像這樣:

frame []
frame (id [])
frame (id (id []))
frame (id (id (id []))) -- the argument takes more memory with every iteration
...

但是在工作示例中是這樣的:

frame []
frame []
frame []
frame []
frame []
frame []                -- argument stays the same as 'id' is evaluated away.

即使這些混音對於他們自己來說並不太昂貴,但如果有足夠的時間,它們將在無限循環中不可避免地耗盡您的所有內存。

我認為您需要這個:

frame :: [Entity] -> [EntityInteraction] -> IO ()
frame eList iList = do
    parseInteractions iList eList
    simulateEntities eList

main :: IO ()
main = do
    let entList = [...]
    forever $ frame entList []

盡管似乎沒有多大意義,例如, elist始終是空列表,因此可以省略。 但是,如果您的C解決方案中的parseInteractions產生/填充eList ,則可能

eList <- parseInteractions iList

在這種情況下,問題是parseInteractions真的需要進行IO?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM