2012-08-15 54 views
6

在Haskell,下面的代碼打印 「[1,2,3,4,5」:表達渴望在弗雷格但在Haskell懶惰?

foo = take 10 $ show $ numbersFrom 1 where 
    numbersFrom start = start : numbersFrom (start + 1) -- could use [1..] 

但在弗雷格,它拋出OutOfMemoryError用下面的代碼:

foo = take 10 $ unpacked $ show $ numbersFrom 1 where 
    numbersFrom start = start : numbersFrom (start + 1) 

這裏唯一不同的是unpacked函數需要將String轉換爲[Char]和FWIW,unpacked函數是渴望的。爲什麼整個表達式不像Haskell那樣是懶惰的?這裏有可能在弗雷格實現類似Haskell的東西嗎?

回答

6

我還沒有使用過弗雷格,但在我看來,如果unpacked是嚴格的,那麼它的參數不應該是一個無限的列表。嘗試unpacked $ take 10而不是take 10 $ unpacked

+3

Frege中的字符串不是列表。所以'take 10'不能應用於'show'的結果。因此,'unpacked'用於首先從'String'轉換爲'[Char]',然後在列表中應用'10'。 – 2012-08-15 01:29:30

+3

那麼什麼是弗雷格的字符串?看起來它們是'java.lang.String'(請參閱Frege語言定義)。你永遠不會考慮評估'unpack',因爲它永遠無法構建字符串! – 2012-08-15 01:41:33

7

我不知道弗雷格,而是根據語言定義Stringjava.lang.String所以你不能建立無限長的字符串(內存不足問題可能有無關unpack是渴望)。

因爲你知道的numbersFrom 1每個元素將被顯示爲至少1個字符,那麼您可以過度接近列表的大小來顯示,然後解壓縮,然後取所希望的字符數:

foo = take 10 $ unpacked $ show $ take 10 $ numbersFrom 1 where 
    numbersFrom start = start : numbersFrom (start + 1) 

或者更一般地說:

n = 10 -- number of characters to show 
m = 1 -- minimum (map (length . show) xs) for some type of xs 
foo :: a -> [Char] 
foo = take n . unpack . show . take ((n+m-1) `div` m) . someEnumeration 
    where 
    someEnumeration :: a -> [a] 
    someEnumeration = undefined 

如果枚舉是昂貴的,那麼你就可以開始考慮逗號,空格等的數量和參數減少到第二take,但你的想法。

4

由於(不)由show生成的無限字符串,這將不起作用。 您需要一些幫助功能將列表轉換爲列表Char

對此有一個標準函數可能會很好。


編輯2013年2月22日

Show現在有一個新方法:

{-- 
    'showChars' addresses the problem of 'show'ing infinite values. 
    Because 'show' has type 'String' and 'String' is atomic, this would 
    try to create a string with infinite length, and hence is doomed to fail. 

    The default definition is 

    > showChars = String.toList . show 

    This is ok for all finite values. But instances for recursive types 
    should implement it in a way that produces a lazy list of characters. 

    Here is an example for the list instance: 

    > showChars [] = ['[', ']'] 
    > showChars xs = '[' : (tail [ c | x <- xs, c <- ',' : showChars x ] ++ [']']) 

-} 
showChars :: show -> [Char] 
showChars = String.toList . show 

Haskell的代碼,導致OutOfMemoryError現在可以寫爲:

(println . packed . take 10 . showChars) [1..] 
4

添加到其他答案,

由於show返回java.lang.String,因此無法顯示無限列表。所以我想我可以寫一個不同版本的節目來代替返回[Char]。這是我想出來的,它正在工作。

frege> :paste 
class AltShow a where 
    altshow :: a -> [Char] 

instance AltShow AltShow a => [a] where 
    altshow [] = [] 
    altshow xs = concat $ (['['] : intersperse [','] ys) ++ [[']']] where 
    ys = map altshow xs 

instance AltShow Int where 
    altshow = unpacked <~ show 

intersperse :: a -> [a] -> [a] 
intersperse _ [] = [] 
intersperse _ (x:[]) = [x] 
intersperse sep (x : y : []) = 
    x : sep : y : [] 
intersperse sep (x : y : rest) = 
    x : sep : y : sep : intersperse sep rest 
:q 
Interpreting... 

frege> altshow [1, 10, 2, 234] 
res3 = ['[', '1', ',', '1', '0', ',', '2', ',', '2', '3', '4', ']'] 
frege> :t res3 
res5 :: [Char] 
frege> packed res3 
res6 = [1,10,2,234] 
frege> :t res6 
res7 :: String 

現在問題的代碼變得類似於Haskell和它不與的OutOfMemoryError爆炸:

frege> :paste 
foo = take 10 $ altshow $ numbersFrom 1 where 
    numbersFrom start = start : numbersFrom (start + 1) 
:q 
Interpreting... 

frege> foo 
res9 = ['[', '1', ',', '2', ',', '3', ',', '4', ',', '5'] 
frege> packed foo 
res11 = [1,2,3,4,5 
+1

我認爲我們可以添加一個類操作到Show這樣做,它的默認實現是toList <〜show' – Ingo 2012-08-16 07:04:53

+0

是的,這是一個更好的方法。謝謝。 – 2012-08-16 14:48:23

2

簡短的回答:字符串是嚴格的弗雷格,但在Haskell懶。

列表在這兩種語言中都是懶惰的。但是在弗雷格中,字符串不是列表。