2012-04-06 17 views
8

Die folgenden zwei Funktionen sind sehr ähnlich. Sie lesen aus [String] n Elementen, entweder [Int] oder [Float]. Wie kann ich den gemeinsamen Code ausschließen? Ich kenne keinen Mechanismus in Haskell, der Übergabetypen als Argumente unterstützt.Übergeben Sie Typen als Argumente für eine Funktion in Haskell?

Ich bin auf einem Anfänger Niveau von Haskell, so dass alle Kommentare zu meinem Code willkommen sind.

+2

Sie verwenden nicht hier falten müssen, können Sie mit einer einfachen Karte auszukommen. z.B. 'map read stream :: [Int]' Vielleicht sollten Sie auch nachschauen, warum Sie foldr in Haskell statt foull verwenden wollen. –

+1

@EdwardKmett Vielen Dank für Ihren Vorschlag. Was ich wirklich will, ist nur die ersten n Elemente zu lesen und die Liste und den Rest des Streams zurückzugeben. Ich war gestern sehr müde und konnte nicht durchdenken. Ich denke du willst sagen das ich mit foldr den Konstruktor benutzen kann: direkt richtig? Ich schrieb es später als '(Karte gelesen firstn, Ruhe) wo (firstn, Ruhe) = splitAt n stream', ziemlich ähnlich zu dem, was Sie vorgeschlagen. –

+0

Sie müssen nicht 'where' verschachteln; Sie können 'next (lst, x: xs) _ = ...' und 'v = ...' in aufeinander folgende Zeilen setzen. – sdcvvc

Antwort

14

Haskell unterstützt einen hohen Grad an Polymorphie. Insbesondere hat

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

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

so

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

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

Sie brauchen nicht zu spezialisieren, die Art geben. Haskell wird automatisch den allgemeinsten möglichen Typ ableiten, und der readAny hier wird tun, was Sie wollen.

Es ist nicht möglich, Typen als Argumente in Haskell zu übergeben. Selten würden Sie brauchen. Für die wenigen Fälle, in denen es notwendig ist, können Sie das Verhalten simulieren, indem Sie einen Wert mit dem gewünschten Typ übergeben.

Haskell hat "Rückgabetyp Polymorphismus", also sollten Sie sich wirklich keine Sorgen machen über "den Typ zu übergeben" - Chancen sind, dass Funktionen tun, was Sie wollen, ohne dass Sie es ihnen sagen.

+3

Ironischerweise ist * die Übergabe des Typs * in einiger implementierungsabhängiger Weise, was hinter den Kulissen geschieht, daher ist die Frage nicht so weit daneben ... – Ingo

+1

Viele Implementierungen passieren nicht so sehr den Typ, als einen Typ konsistenten Zeugen zu übergeben die Typklassenwerte. Etwas wie "id" muss den Typ seiner Argumente überhaupt nicht kennen (solange alles die gleiche Größe hat) –

+1

Das meinte ich mit * implementierungsabhängiger Methode *. Aber * id *, ohne Einschränkungen, wird überhaupt nicht bestanden, denke ich. Der Typ garantiert, dass id nichts mit dem Argument tun wird, dass mehr Informationen benötigt werden als nur "es ist ein Wert", aber dies sollte impliziert werden. – Ingo

9

Grundsätzlich ist es wichtig, den Typ nicht explizit zu deklarieren. Lassen Sie statt dessen den Typ deklarieren und lassen Sie die Inferenz-Engine für sich übernehmen. Außerdem glaube ich, dass Sie die Karte zusammenfalten. So würde ich es angehen.

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] 

Um nur n Dinge aus dem Stream zu lesen, take

Verwandte Themen