2012-04-06 14 views
8

Le seguenti due funzioni sono estremamente simili. Leggono da [String] n elementi, [Int] o [Float]. Come posso calcolare il codice comune? Non conosco alcun meccanismo in Haskell che supporti i tipi di passaggio come argomenti.Passa i tipi come argomenti a una funzione in 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 

Sono ad un livello principiante di Haskell, quindi qualsiasi commento sul mio codice è benvenuto.

+2

Non è necessario piegare qui, si può ottenere con una semplice mappa. per esempio. 'map read stream :: [Int]' Potresti anche voler capire perché vuoi usare foldr in Haskell piuttosto che foldl. –

+1

@EdwardKmett Grazie per il suggerimento. Quello che voglio veramente è leggere solo i primi n elementi e restituire la lista e il resto del flusso. Ero molto assonnato ieri e non riuscivo a pensare. Penso che tu voglia dire che con foldr posso usare il costruttore: direttamente giusto? In seguito l'ho riscritto come '(map leggi firstn, rest) dove (firstn, rest) = splitAt n stream', abbastanza simile a quello che hai suggerito. –

+0

Non è necessario nidificare 'where'; puoi mettere 'next (lst, x: xs) _ = ...' e 'v = ...' in righe consecutive. – sdcvvc

risposta

14

Haskell supporta un alto grado di polimorfismo. In particolare

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

ha digitare

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

così

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

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

non avete bisogno di specializzarsi tipo. Haskell dedurrà automaticamente il tipo più generale possibile e lo readAny qui farà ciò che desideri.

Non è possibile passare tipi come argomenti in Haskell. Raramente avresti bisogno di Per quei pochi casi in cui è necessario è possibile simulare il comportamento passando un valore con il tipo desiderato.

Haskell ha "polimorfismo del tipo di ritorno", quindi non dovresti preoccuparti di "passare il tipo" - le probabilità sono che le funzioni faranno ciò che vuoi senza che tu glielo dica.

+3

Ironia della sorte, * il passaggio del tipo * dipende in qualche modo dall'implementazione di ciò che accade dietro le quinte, quindi la domanda non è poi così lontana ... – Ingo

+1

Molte implementazioni non passano così tanto il tipo come passano un testimone coerente di tipo per i valori della classe tipo. Qualcosa come 'id' non ha bisogno di conoscere il tipo dei suoi argomenti (purché tutto abbia le stesse dimensioni) –

+1

Questo è quello che intendevo con * modo dipendente dall'implementazione *. Ma * id *, non avendo vincoli, non viene passato niente, credo. Il tipo garantisce che id non sta andando a fare nulla con la sua argomentazione che avrebbe bisogno di più informazioni che solo "è un valore", ma questo dovrebbe essere implicito. – Ingo

9

Fondamentalmente quello che vuoi è non dichiarare esplicitamente il tipo. Invece, rimanda la dichiarazione del tipo e lascia che il motore di inferenza prenda il posto a te. Inoltre, penso che tu stia combinando la piega con la mappa. Questo è il modo in cui mi avvicinerei.

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] 

leggere solo n cose dal flusso, utilizzare take

Problemi correlati