簡體   English   中英

Haskell中的無限列表

[英]Infinite list in Haskell

我正在嘗試定義一個函數,該函數在我的簡單語法中創建所有可能單詞的無限列表。 但是當我輸入head (generate [] []) ,ghci會凍結,盡管head (generate' [] [])可以正常工作(但是我仍然想要一個無限列表)。 有什么問題?

data Start = Start deriving(Show)
data MyExpr = MyExprStart Start | MyExpr1 MyExpr | MyExpr2 MyExpr deriving(Show)

generate :: [MyExpr] -> [MyExpr] -> [MyExpr]
generate [] []         = generate [MyExprStart Start] []
generate current prev  = generate (concatMap nextExprs current) (prev ++ current)

generate' :: [MyExpr] -> [MyExpr] -> [MyExpr]
generate' [] []         = generate' [MyExprStart Start] []
generate' current prev  =
  if (length current + length prev) < 5 then
    generate' (concatMap nextExprs current) (prev ++ current)
  else prev ++ current

nextExprs :: MyExpr -> [MyExpr]
nextExprs expr = [MyExpr1 expr, MyExpr2 expr]

UPD:謝謝,我明白了。 我想我應該這樣定義我的功能:

generate :: [MyExpr] -> [MyExpr]
generate []         = generate [MyExprStart Start]
generate current    = current ++ generate (concatMap nextExprs current)

generate函數凍結,因為它對無限遞歸進行編碼。 看:它的每種情況都只是調用函數本身。 當它實際返回的不是調用自身的內容時,沒有條件。 因此,當您嘗試運行它時,它只會一直調用它自己。

另一方面,當兩個輸入列表的總長度超過5時, generate'函數將立即退出。這樣就可以返回結果。

為了使此函數在完全評估之前開始返回數據(它永遠做不到),您需要首先返回非遞歸用例,然后通過遞歸調用generate來將遞歸用例附加到它們上:

generate = (MyExprStart Start) : [recur e | e <- generate, recur <- [MyExpr1, MyExpr2]]

GHCI:

> head generate
MyExprStart Start
> take 10 generate
[MyExprStart Start, MyExpr1 (MyExprStart Start),
 MyExpr2 (MyExprStart Start), MyExpr1 (MyExpr1 (MyExprStart Start)),
 MyExpr2 (MyExpr1 (MyExprStart Start)),
 MyExpr1 (MyExpr2 (MyExprStart Start)),
 MyExpr2 (MyExpr2 (MyExprStart Start)),
 MyExpr1 (MyExpr1 (MyExpr1 (MyExprStart Start))), 
 MyExpr2 (MyExpr1 (MyExpr1 (MyExprStart Start))),
 MyExpr1 (MyExpr2 (MyExpr1 (MyExprStart Start)))]

注意:列表理解中的生成器順序很重要。 如果交換它們,執行將永遠不會到達MyExpr2 ,因為它將需要首先耗盡generate列表,並且該列表是無限的。

generate :: [MyExpr] -> [MyExpr] -> [MyExpr]
generate ... = generate ...
generate ... = generate ...

這種形式的任何定義都將在產生任何輸出之前始終調用自身。 這種歸納結構只有在至少在某個點上在遞歸之前返回輸出的某些部分(例如發出輸出列表的第一個元素)時才能定義。

generate' :: [MyExpr] -> [MyExpr] -> [MyExpr]
generate' [] []         = generate' [MyExprStart Start] []
generate' current prev  =
  if (length current + length prev) < 5 then
    generate' (concatMap nextExprs current) (prev ++ current)
  else prev ++ current

相比之下,在generate'else分支不會遞歸,因此它將產生輸出。 即使我們有類似的事情

else someNonEmptyList ++ generate' ...

這將起作用,因為將產生前幾個列表元素。

此外,請記住,生成將可能的無限列表與++合並的所有可能的表達式不是一個好主意。 確實,如果xs是無限的,則xs ++ ys等於xs ,因為ys在有限的時間內永遠不會達到。 xsys無限時,您可能希望公平地交織它們。

一個類似的問題使用了omega monad來實現完整的枚舉。

暫無
暫無

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

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