2012-04-06 34 views
8

Poniższe dwie funkcje są bardzo podobne. Czytają z elementów [String] n, [Int] lub [Float]. Jak mogę wykluczyć wspólny kod? Nie znam żadnego mechanizmu w Haskell, który obsługuje przekazywanie typów jako argumentów.Typy przekazywania jako argumenty funkcji w Haskell?

readInts n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x :: Int 

readFloats n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x :: Float 

Jestem na poziomie początkującego Haskella, więc wszelkie uwagi na mój kod są mile widziane.

+2

Nie trzeba złożyć tutaj, można dostać się z prostego mapie. na przykład 'map read stream :: [Int]' Możesz również sprawdzić, dlaczego chcesz używać foldr w Haskell zamiast foldl. –

+1

@EdwardKmett Dzięki za sugestię. Naprawdę chcę tylko odczytać pierwsze n elementów i zwrócić listę oraz resztę strumienia. Wczoraj byłem bardzo śpiący i nie mogłem się zastanowić. Myślę, że chcesz powiedzieć, że z foldr mogę używać konstruktora: bezpośrednio w prawo? Później przerobiłem go na '' (map read firstn, rest), gdzie (firstn, rest) = splitAt n stream', całkiem podobny do tego, co zasugerowałeś. –

+0

Nie musisz zagnieżdżać 'gdzie'; możesz umieścić 'next (lst, x: xs) _ = ...' i 'v = ...' w kolejnych wierszach. – sdcvvc

Odpowiedz

14

Haskell obsługuje wysoki stopień polimorfizmu. W szczególności

readAny n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x 

ma typ

readAny :: (Enum b, Num b, Read a) => b -> [String] -> ([a], [String]) 

zatem

readInts :: (Enum b, Num b, Read a) => [String] -> ([Int], [String]) 
readInts = readAny 

readFloats :: (Enum b, Num b, Read a) => [String] -> ([Float], [String]) 
readFloats = readAny 

nie trzeba się specjalizować typ. Haskell automatycznie określi najbardziej ogólny typ, a tutaj readAny zrobi to, co chcesz.

Nie można przekazywać typów jako argumentów w Haskell. Rzadko byś tego potrzebował. W tych nielicznych przypadkach, w których jest to konieczne, można symulować zachowanie, przekazując wartość żądanemu typowi.

Haskell ma "polimorfizm typu powrotu", więc naprawdę nie powinieneś się martwić o "przekazywanie typu" - szanse są takie, że funkcje będą robić to, co chcesz, bez powiadamiania ich.

+3

Jak na ironię * przekazywanie typu * jest w pewnym stopniu zależne od implementacji, co dzieje się za kulisami, stąd pytanie nie jest aż tak dalekie od ... – Ingo

+1

Wiele implementacji tak bardzo nie przepuszcza typu, jak przekazuje spójny świadek typu dla wartości klasy typu. Coś takiego jak "id" nie musi w ogóle znać typu swoich argumentów (o ile wszystko ma ten sam rozmiar). –

+1

To właśnie miałem na myśli * zależny od implementacji sposób *. Ale * id *, bez żadnych ograniczeń, w ogóle nic nie przekazuje, tak myślę. Typ gwarantuje, że id nie zrobi nic z jego argumentem, który wymagałby więcej informacji niż tylko "jest to pewna wartość", ale powinno to być dorozumiane. – Ingo

9

Zasadniczo nie chcesz jawnie zadeklarować typu. Zamiast tego odrzuć deklarację typu i pozwól, aby silnik wnioskowania przejął za Ciebie. Poza tym myślę, że fałszujesz mapę z mapą. W ten sposób podchodzę do tego.

readList' :: Read a => [String] -> [a] 
readList' = map read 


ints = readList' ["1", "2"] :: [Int] -- [1, 2] 

floats = readList' ["1.0", "2.0"] :: [Float] -- [1.0, 2.0] 

Aby odczytać tylko n Things ze strumienia, należy take

Powiązane problemy