-1

Versuch, fizzBuzz mit einer Befehlszeile mit optparse-applicative zu implementieren.Wie passe ich Typen in Haskell an, sodass der erwartete Typ dem tatsächlichen Typ entspricht?

import Options.Applicative 
data Args = Args { firstDivisor :: Int, 
       secondDivisor :: Int, 
       upperBound :: Int } 
fizzBuzz :: Args -> IO() 
fizzBuzz opts i 
    | i `mod` firstDivisor opts == 0 && i `mod` secondDivisor opts == 0 = "fizzBuzz" 
    | i `mod` firstDivisor opts == 0 = "fizz" 
    | i `mod` secondDivisor opts == 0 = "buzz" 
    | otherwise = show i 
main :: IO() 
main = print fizzBuzz 

Ich habe es bis in drei Befehlszeilenargumente zu übernehmen; zwei Teilern (in FizzBuzz in der Regel 3 und 5), und die dritte die obere Grenze (in der Regel 100), aber wenn ich zu kompilieren gehen erhalte ich eine Fehlermeldung, dass:

Couldn't match expected type ‘Int -> [Char]’ 
      with actual type ‘IO()’ 
The equation(s) for ‘fizzBuzz’ have two arguments, 
but its type ‘Args -> IO()’ has only one 

Mein Hauptziel ist es einfach auszudrucken Die fizzBuzz-Serie mit den drei Befehlszeilenargumenten. Von dem, was ich verstehe, ist es nicht so, dass ich fizzBuzz mit einem zusätzlichen Parameter versorgt habe. Versuchen zu verstehen, warum 'ich' hier nicht funktionieren würde.


UPDATE:

Dieser Code Ich fühle mich näher, da sie die Kommandozeile mit getArgs statt optparse Adressen. Außerdem wurde eine Liste hinzugefügt, die gegen fizzBuzz ausgeführt werden kann.

import System.Environment 

main :: IO() 
main = do 
    [s1,s2,s3] <- getArgs 
    print m 
m = [ fizzBuzz i| i <-[1..s3]] 
fizzBuzz i 
    | i `mod` s1 == 0 && s1 `mod` s2 == 0 = "fizzBuzz" 
    | i `mod` s1 == 0 = "fizz" 
    | i `mod` s2 == 0 = "buzz" 
    | otherwise = show i 

Also mein Problem ist, dass ich nicht die s1 und s2 Variablen, um meine FizzBuzz goin zu erhalten zugreifen kann. Wie kann ich auf diese Argumente außerhalb des Hauptbereichs zugreifen? Vielleicht gibt es eine andere Funktion, die helfen kann?

+5

'FizzBuzz :: Args - > IO() 'sagt, dass' fizzBuzz' eine Funktion ist, die einen 'Args' als Parameter nimmt und ein' IO() 'zurückgibt. Aber dann erklärt 'fizzBuzz opts i' zwei Argumente. Und in allen Fällen versucht es eine Zeichenfolge zurückzugeben. – immibis

+1

Wenn Sie ein Anfänger sind, würde ich für einen einfacheren Ansatz gehen und optarse-applicative vermeiden. Verwenden Sie einfach '[s1, s2, s3] <- getArgs', um drei Strings aus den Argumenten zu erhalten, wandeln Sie sie in Zahlen mit' read' um und gehen Sie weiter. Für Bonuspunkte behandeln Sie später den falschen Argumentfall (nicht drei/nicht numerisch) mit 'reads' oder' readMaybe'. Aber zuerst, lernen Sie, wie Sie die IO-Monade mit einem guten Tutorial verwenden. – chi

+0

Sie müssen es auch irgendwie tun, um all das für alle Zahlen in '[1..upperBound]' zu tun. – Gurkenglas

Antwort

1

Wie kann ich auf diese Argumente außerhalb des Bereichs von Haupt zugreifen?

Sie können nicht. Das ist genau der Punkt eines Scopes: Variablen innerhalb bleiben schön eingeschlossen, was schwer zu verfolgende Datenabhängigkeiten verhindert.

Der einfachste Weg, um das Ziel des Zugriffs auf s1s2s3 ist einfach zu erreichen, um fizzbuzzin den Umfang der Haupt zu definieren:

main :: IO() 
main = do 
    [s1,s2,s3] <- map read<$>getArgs -- more on this later 
    let fizzBuzz i 
     | i `mod` s1 == 0 && s1 `mod` s2 == 0 = "fizzBuzz" 
     | i `mod` s1 == 0 = "fizz" 
     | i `mod` s2 == 0 = "buzz" 
     | otherwise = show i 
     m = [fizzBuzz i| i <-[1..s3]] 
    print m 

aber das ist nicht unbedingt der beste Ansatz - Sie im Wesentlichen schaffen würden ein großer pseudo-globaler IO-Bereich. Denken Sie daran, das ist nirgendwo so gefährlich wie globale Variablen in Imperativsprachen, aber zumindest wenn Sie auch in einem anderen Kontext fizzBuzz verwenden möchten, ist dies eindeutig nicht optimal.

Der korrekte Weg ist natürlich, diese Variablen als Funktion Argumente übergeben. Ganz wie du es schon hattest - ich finde es seltsam, dass du das wieder entfernt hast.

Also, zunächst einmal die Signatur von fizzBuzz herausfinden. Was braucht es und was gibt es? Nun, es braucht all diese Teiler. Daher machte es sehr viel Sinn, ihm ein Args Argument zu geben. Aber es braucht auch die Nummer i, die einfach eine Int ist. Was das Ergebnis angeht ... warum wäre es IO()? fizzBuzz ist eine vollkommen gute pure Funktion, die eine Saite ergibt. So ...

fizzBuzz :: Args -> Int -> String 

Nun, jetzt geht es Ihnen gut. Die Definition sieht tatsächlich völlig richtig aus, wie Sie es ursprünglich hatten.(Aber im Allgemeinen ist es viel sinnvoller zuerst erhalten Sie die Art Unterschrift rechts, dann sorgen Sie sich darüber, wie Sie es tatsächlich implementieren.)

Jetzt müssen Sie nur diese Funktion aufrufen. Ich würde zuerst mit ihm in GHCi ein bisschen vor dem Schreiben jede main Funktion überhaupt spielen, um:

$ ghci FizzBuzz.hs 
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help 
[1 of 1] Compiling Main    (FizzBuzz.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> fizzBuzz (Args 3 4 9) 3 
"fizz" 
*Main> fizzBuzz (Args 3 4 9) 5 
"5" 
... 

Wenn Sie denken, dass es richtig funktioniert, können Sie es in ein tatsächliches Programm wickeln kann.

Beachten Sie, dass die Args Datenstruktur nicht wirklich etwas mit der OptParse Bibliothek zu tun hat. Diese Bibliothek ist für die richtigen Befehlszeilenprogramme sinnvoll, aber hier ist es zu viel. Dennoch ist es sehr sinnvoll, den Typ Args zu verwenden, um diese Teiler in die Funktion zu überführen, wie ich bereits gezeigt habe.

Ich würde jetzt verwenden nur getArgs die Argumente für das Abrufen und sie manuell wickeln in die Args Struktur:

module Main where 

import System.Environment (getArgs) 

main :: IO() 
main = do 
    [s₁,s₂,s₃] <- getArgs 
    let divisors = Args (read s₁) (read s₂) (read s₃) 
    m = [fizzBuzz divisors i | i<-[1..read s₃]] 
    print m 

Sie mögen sich fragen, was das alles ist los mit read. Nun, getArgs gibt Ihnen die Befehlszeilenparameter, wie sie aus der Shell kommen: als Strings. Aber Sie müssen lesen diese Zeichenfolgen als Ganzzahlen interpretieren. Beachten Sie, dass read ziemlich unsicher ist: Wenn die Eingabe kein gültiges Integer-Literal ist, stürzt das Programm ab. Wahrscheinlich kein Problem für Sie jetzt, aber das ist eines der Dinge, die optparse würde schön für Sie lösen.

Da Sie nicht wirklich brauchen, um die Parameter in String-Form überhaupt, könnte man genauso gut readalle von ihnen, bevor auch die einzelnen Parameter passend aus:

main :: IO() 
main = do 
    [s₁,s₂,s₃] <- map read <$> getArgs 
    let divisors = Args s₁ s₂ s₃ 
    m = [fizzBuzz divisors i | i<-[1..s₃]] 
    print m 
+0

Ist es absichtlich, dass 'lesen' nicht in der ersten Lösung erscheint? – chi

+0

Nein, ich habe es am Anfang übersehen. – leftaroundabout

+0

danke für die Klärung einiger Dinge, die ich fragte, aber nicht @leftaroundabout fragen. Die Typensignatur für Fizzbuzz herauszufinden, war definitiv in meiner Art. Jetzt sagen wir, ich gehe nicht mit meinen zweiten Lösungen arbeiten und gehe mit dem ersten.Nehmen wir an, ich habe jetzt den richtigen Typ sig bekommen, ich bekomme eine 'No instance für (Show (Args -> Int -> String))'. Ich denke, ich brauche etwas wie 'Instanz zeigen (einige Code)' – selfresonator

Verwandte Themen