2012-08-15 9 views
6

In Haskell, der folgende Code druckt "[1,2,3,4,5":Ausdruck eifrig in Frege aber faul in Haskell?

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

Aber in Frege Es wirft OutOfMemoryError mit dem folgenden Code:

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

Hier ist die einziger Unterschied ist die unpacked Funktion, die notwendig ist, um von String zu [Char] und FWIW zu konvertieren, die unpacked Funktion ist eifrig. Warum kann der ganze Ausdruck nicht so faul sein wie in Haskell? Kann man hier in Frege etwas Ähnliches wie Haskell erreichen?

Antwort

6

Ich habe Frege nicht verwendet, aber es scheint mir, wenn unpacked ist streng, dann sollte sein Argument nicht eine unendliche Liste sein. Versuchen Sie unpacked $ take 10 anstelle von take 10 $ unpacked.

+3

Strings in Frege sind keine Listen. So kann 'take 10' nicht auf das Ergebnis von' show' angewendet werden. Daher wird "entpackt" verwendet, um zuerst von "String" in "[Char]" umzuwandeln, und dann wird "Take 10" auf die Liste angewendet. –

+3

Was sind 'Saiten' in Frege? Es scheint, dass sie 'java.lang.String' sind (siehe Definition der Frege-Sprache). Sie werden es nie schaffen, "Entpacken" auszuwerten, da es niemals in der Lage ist, die Zeichenfolge zu erstellen! –

7

Ich weiß Frege nicht, aber nach der Sprachdefinition String ist java.lang.String so kann man nicht unendlich lange Ketten bauen (die aus der Erinnerung Problem wahrscheinlich nichts mit unpack sind begierig zu tun hat).

Weil Sie wissen, jedes Element von numbersFrom 1 wird als mindestens 1 Zeichen dargestellt werden, dann können Sie über annähernd die Größe der Liste entpacken zu zeigen, dann nehmen Sie dann die Anzahl der gewünschten Zeichen:

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

oder allgemeiner:

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 

Wenn Ihre Aufzählung ist teuer, dann können Sie die Anzahl der Kommas unter Berücksichtigung beginnen, Leerzeichen, etc. und verringern das Argument mit dem zweiten take, aber Sie bekommen die Idee.

4

Dies funktioniert nicht wegen der unendlichen Zeichenkette, die von show erzeugt wird. Sie benötigen eine Hilfsfunktion, die eine Liste in eine Liste von Char konvertiert.

Es wäre wahrscheinlich gut, eine Standardfunktion dafür zu haben.


bearbeiten 22. Februar 2013

Klasse Show hat nun eine neue Methode:

{-- 
    '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 

Der Code Haskell, die dem OutOfMemoryError führte jetzt geschrieben werden kann als:

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

Hinzufügen zu anderen Antworten,

Da show eine java.lang.String zurückgibt, können keine unendlichen Listen angezeigt werden. Also dachte ich, ich könnte eine andere Version der Show schreiben, um stattdessen eine [Char] zurückzugeben. Das ist, was ich mir ausgedacht habe und es funktioniert.

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 

Jetzt ist der Code in der Frage wird zu Haskell ähnlich und es explodiert nicht mit 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

Ich denke, wir könnten 'Show' eine Klassenoperation hinzufügen, die es so macht und deren Standardimplementierung' toList <~ show' ist. – Ingo

+0

Ja, das ist ein besserer Weg. Vielen Dank. –

2

Kurze Antwort: Strings sind streng in Frege, aber faul in Haskell.

Listen sind in beiden Sprachen faul. Aber in Frege sind Strings keine Listen.