2014-09-05 18 views
6

Ho scritto quanto segue per aiutare i nipoti con il loro lavoro a scuola a casa e per mantenere il lavoro mentale imparando come programmare (pensavo che haskell suonasse fantastico).C'è un modo migliore per farlo in Haskell?

main :: IO() 
main = do 
    putStrLn "Please enter the dividend :" 
    inputx <- getLine 
    putStrLn "Please enter the divisor :" 
    inputy <- getLine 
    let x = (read inputx) :: Int 
    let y = (read inputy) :: Int 
    let z = x `div` y 
    let remain = x `mod` y 
    putStrLn ("Result: " ++ show x ++ "/" ++ show y ++ " = " ++ show z ++ " remainder " ++ show remain) 

    putStrLn ("Proof: (" ++ show y ++ " x " ++ show z ++ ") = " ++ show (y * z) ++ " + " ++ show remain ++ " = " ++ show ((y * z) + remain)) 
    putStrLn ("Is this what you had? ") 

È un modo più ordinato/più bello/migliore/più compatto di farlo?

+0

Penso che vada bene - potresti accorciare alcune parti (per esempio * unione * 'lettura' e' getLine' - usando ['<$>'] (http://haddocks.fpcomplete.com/fp/7.4.2/ 20130829-168/base/Data-Functor.html # v: -60--36--62-) e 'div' /' mod' utilizzando ['divMod'] (http://haddocks.fpcomplete.com/fp /7.4.2/20130829-168/base/Prelude.html#v:divMod) - ma penso che sia perfetto per un programma così semplice. – Carsten

+0

Lo stile è principalmente una preferenza, ma farei quanto segue: 'input <- putStrLn" prompt ">> getLine'; le istruzioni sequenziali let non hanno bisogno di una let su ogni riga (cioè sostituiscono tutte le opzioni dopo la prima con 3 spazi), invece di avere' putStrLn' in sequenza, vorrei scrivere la riga 'putStrLn $" 1 "++" line 2 "++" line 3 "' (ogni stringa può essere sulla propria linea - finché le righe seguenti sono rientrate, haskell conosce la sua parte della stessa istruzione). – user2407038

+1

@ user2407038 il tuo 'putStrLn' mancherà alcune newline;) – Carsten

risposta

8

Trarrebbe vantaggio da un principio chiave: separa il tuo codice puro dal tuo IO il più possibile. Ciò consentirà ai tuoi programmi di scalare e mantenere main breif. Un gran numero di let in un grande main non è un approccio molto funzionale e tende a diventare molto più complicato man mano che il codice cresce.

L'utilizzo di un tipo di firma e readLn che è essenzialmente fmap read getLine aiuta a ridurre alcuni cruft. (Se non hai familiarità con fmap, visitare la questione How do functors work in haskell?. fmap è uno strumento molto flessibile in effetti.)

getInts :: IO (Int, Int) 
getInts = do 
    putStrLn "Please enter the dividend :" 
    x <- readLn 
    putStrLn " Please enter the divisor :" 
    y <- readLn 
    return (x,y) 

Ora l'elaborazione. Se dovessi fare di più con questo tipo di dati, o più frequentemente, userei un tipo di record per archiviare il dividendo, il divisore, il quoziente e il resto, quindi tienilo a mente per il futuro, ma qui è un eccesso.

che sto tornando hackishly un elenco piuttosto che una tupla, così posso usare map per show tutti:

sums :: (Int, Int) -> [Int] 
sums (x,y) = [x, y, q, r, y * q, y * q + r] where 
      q = x `div` y 
      r = x `mod` y 

L'ultimo pezzo del puzzle è l'uscita. Ancora una volta preferisco generare questo IO esterno e quindi posso semplicemente scrivere su mapM_ putStrLn in seguito per stampare ogni riga. Preferirei che prendesse il tipo di record, ma sto tollerando un elenco di stringhe come input, dal momento che presumo di avere già show n.

explain :: [String] -> [String] 
explain [x,y,q,r,yq,yq_r] = 
    [ concat ["Result: ", x, "/", y, " = ", q, " remainder ", r] 
    , concat ["Proof: (", y, " x ", q, ") + ", r, " = ", yq, " + ", r, " = ", yq_r] 
    , "Is this what you had? "] 

Ora possiamo scrivere main come

main = do (x,y) <- getInts 
      let ns = map show (sums (x,y)) 
       es = explain ns 
      mapM_ putStrLn es 

o anche più brevemente, convogliando insieme le funzioni explain . map show . sums, e applicando che all'uscita di getInts usando fmap:

main :: IO() 
main = fmap (explain . map show . sums) getInts 
     >>= mapM_ putStrLn 

Si potrebbe notare che ho aggiunto un +r nella prova per rendere = significa sempre =, che è l'uso matematico corretto e il significato di Haskell del mirror per =.

+0

Grazie a @WillNess, le tue modifiche sono sempre buone e _always_ benvenuto. – AndrewC

+1

Grazie, Andrew, non lo chiederò più, allora. :) :) –

Problemi correlati