2012-08-15 13 views
6

W Haskell, następujące drukuje kod "[1,2,3,4,5":Ekspresja chętna w Frege, ale leniwa w Haskell?

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

Ale w Frege, rzuca OutOfMemoryError z następującego kodu:

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

Tutaj jedyną różnicą jest funkcja unpacked, która jest niezbędna do konwersji z String na [Char], a funkcja FWIW, funkcja unpacked jest gorliwa. Dlaczego całe wyrażenie nie może być leniwy, jak w Haskell? Czy jest możliwe osiągnięcie czegoś podobnego do Haskella w Frege?

Odpowiedz

6

Nie użyłem Fregego, ale wydaje mi się, że jeśli unpacked jest ścisłe, to jego argument nie powinien być nieskończoną listą. Wypróbuj unpacked $ take 10 zamiast take 10 $ unpacked.

+3

Łańcuchy w języku FREGE nie są listami. Więc 'take 10' nie może być zastosowane do wyniku' show'. Dlatego "unpacked" jest używane do pierwszej konwersji z 'String' na' [Char] ', a następnie' take 10' jest stosowane na liście. –

+3

Więc czym są 'String's w Frege? Wygląda na to, że są to 'java.lang.String' (zobacz definicję języka Frege). Nigdy nie dostaniesz się do oceny "rozpakuj", ponieważ nigdy nie będzie w stanie zbudować struny! –

7

Nie wiem Frege, ale zgodnie z definicją języka String jest java.lang.String, więc nie można zbudować nieskończenie długich ciągów (problem z braku pamięci prawdopodobnie nie ma nic wspólnego z unpack).

Bo wiesz każdy element numbersFrom 1 zostaną pokazane jako co najmniej 1 znak, a następnie można nadmierne zbliżenie wielkości listy, aby pokazać, następnie rozpakować, a następnie podjąć szereg pożądanych znaków:

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

lub bardziej ogólnie:

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 

Jeśli wyliczenie jest drogie wtedy można zacząć biorąc pod uwagę liczbę przecinkami, spacjami, itp i zmniejszyć argument drugiej take, ale masz pomysł.

4

To nie zadziała z powodu nieskończonego łańcucha, który nie jest (produkowany) przez show. Potrzebowałbyś jakiejś funkcji pomocnika, która zamienia listę na listę Char.

Prawdopodobnie dobrze byłoby mieć do tego standardową funkcję.


Edycja 22 lutego 2013

Klasa Show ma teraz nową metodę:

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

Kod Haskell, który doprowadził do OutOfMemoryError Teraz można zapisać jako:

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

Dodawanie do innych odpowiedzi,

Od show zwraca java.lang.String, nie można wyświetlić nieskończonej listy. Pomyślałem więc, że mógłbym napisać inną wersję programu, aby zamiast tego zwrócić numer [Char]. Oto, co wymyśliłem i działa.

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 

Teraz kod w pytaniu staje się podobny do Haskell i nie eksploduje z 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

Myślę, że możemy dodać operację klasy do 'Show', która robi to w ten sposób i której domyślną implementacją jest' toList <~ show' – Ingo

+0

Tak, to jest lepszy sposób. Dzięki. –

2

Krótka odpowiedź: Struny są surowe w Frege, ale leniwy w Haskell.

Listy są leniwe w obu językach. Ale w Fregacie ciągi nie są listami.