Dividere un numero nelle sue cifre con Haskell

Dato un numero arbitrario, come posso elaborare ogni cifra del numero singolarmente?

Modifica Ho aggiunto un esempio di base del tipo di cosa che Foo potrebbe fare.

Ad esempio, in C # potrei fare qualcosa del genere:

 static void Main(string[] args) { int number = 1234567890; string numberAsString = number.ToString(); foreach(char x in numberAsString) { string y = x.ToString(); int z = int.Parse(y); Foo(z); } } void Foo(int n) { Console.WriteLine(n*n); } 

Hai mai sentito parlare di div e mod ?

Probabilmente vorrai invertire la lista dei numeri se prima vuoi trattare la cifra più significativa. Convertire il numero in una stringa è un modo alterato di fare le cose.

 135 `div` 10 = 13 135 `mod` 10 = 5 

Generalizza in una funzione:

 digs :: Integral x => x -> [x] digs 0 = [] digs x = digs (x `div` 10) ++ [x `mod` 10] 

Oppure al contrario:

 digs :: Integral x => x -> [x] digs 0 = [] digs x = x `mod` 10 : digs (x `div` 10) 

Questo considera 0 come privo di cifre. Una semplice funzione wrapper può occuparsi di quel caso speciale se lo si desidera.

Si noti che questa soluzione non funziona con numeri negativi (l’input x deve essere integrale, cioè un numero intero).

 digits :: Integer -> [Int] digits = map (read . (:[])) . show 

oppure puoi restituirlo in [] :

 digits :: Integer -> [Int] digits = map (read . return) . show 

oppure, con Data.Char.digitToInt:

 digits :: Integer -> [Int] digits = map digitToInt . show 

lo stesso di Daniel è davvero, ma inutile e usa Int, perché una cifra non dovrebbe superare veramente maxBound :: Int .

Potresti anche solo riutilizzare digits da Hackage.

Usando la stessa tecnica usata nel tuo post, puoi fare:

 digits :: Integer -> [Int] digits n = map (\x -> read [x] :: Int) (show n) 

Guardalo in azione:

 Prelude> digits 123 [1,2,3] 

Questo aiuta?

Puoi usare

 digits = map (`mod` 10) . reverse . takeWhile (> 0) . iterate (`div` 10) 

o per ordine inverso

 rev_digits = map (`mod` 10) . takeWhile (> 0) . iterate (`div` 10) 

La parte iterata genera un elenco infinito dividendo l’argomento in ogni passaggio per 10, quindi 12345 diventa [12345,1234,123,12,1,0,0 ..]. La parte takeWhile prende solo l’interessante parte non nulla della lista. Quindi invertiamo (se vogliamo) e prendiamo l’ultima cifra di ogni numero della lista.

Ho usato lo stile point-free qui, quindi puoi immaginare un argomento invisibile n su entrambi i lati dell ‘”equazione”. Tuttavia, se vuoi scriverlo in questo modo, devi sostituire il livello più alto . da $ :

 digits n = map(`mod` 10) $ reverse $ takeWhile (> 0) $ iterate (`div`10) n 

Il libro di testo si sviluppa

 import qualified Data.List as L digits = reverse . L.unfoldr (\x -> if x == 0 then Nothing else Just (mod x 10, div x 10)) 

La risposta accettata è ottima ma fallisce nei casi di numeri negativi poiché mod (-1) 10 restituisce 9. Se si desidera che questo gestisca correttamente i numeri negativi … il che potrebbe non essere il caso che il seguente codice lo consenta.

 digs :: Int -> [Int] digs 0 = [] digs x | x < 0 = digs ((-1) * x) | x > 0 = digs (div x 10) ++ [mod x 10] 

Tramite la comprensione delle liste:

 import Data.Char digits :: Integer -> [Integer] digits n = [toInteger (digitToInt x) | x <- show n] 

produzione:

 > digits 1234567890 [1,2,3,4,5,6,7,8,9,0] 

Ecco un miglioramento su una risposta sopra. Questo evita lo 0 in più all’inizio (Esempi: [0,1,0] per 10, [0,1] per 1). Usa la corrispondenza del modello per gestire casi in cui x <10 in modo diverso:

 toDigits :: Integer -> [Integer] -- 12 -> [1,2], 0 -> [0], 10 -> [1,0] toDigits x | x < 10 = [x] | otherwise = toDigits (div x 10) ++ [mod x 10] 

Avrei messo questa risposta a quella risposta, ma non ho i necessari punti di reputazione 🙁

Applicativo . Senza punti . Origami . Neat.

Godere:

 import Data.List import Data.Tuple import Data.Bool import Control.Applicative digits = unfoldr $ liftA2 (bool Nothing) (Just . swap . (`divMod` 10)) (> 0) 

Per restituire un elenco di [Intero]

 import Data.Char toDigits :: Integer -> [Integer] toDigits n = map (\x -> toInteger (digitToInt x)) (show n) 

Ho provato a continuare a usare la ricorsione della coda

 toDigits :: Integer -> [Integer] toDigits x = reverse $ toDigitsRev x toDigitsRev :: Integer -> [Integer] toDigitsRev x | x <= 0 = [] | otherwise = x `rem` 10 : toDigitsRev (x `quot` 10) 

La risposta accettata è corretta eccetto che produrrà una lista vuota quando l’input è 0, tuttavia ritengo che l’output dovrebbe essere [0] quando l’input è zero.

E non penso che riguardi il caso quando l’input è negativo. Di seguito è la mia implementazione, che risolve i due problemi di cui sopra.

 toDigits :: Integer -> [Integer] toDigits n | n >=0 && n < 10 = [n] | n >= 10 = toDigits (n`div`10) ++ [n`mod`10] | otherwise = error "make sure your input is greater than 0" 

Ero pigro a scrivere la mia funzione personalizzata, quindi l’ho cercato su google e sono rimasto sorpreso che nessuna delle risposte su questo sito fornisse una soluzione davvero valida: alte prestazioni e sicurezza del testo. Quindi eccolo qui, forse qualcuno vorrebbe usarlo. Fondamentalmente:

  1. È sicuro dal tipo: restituisce un elenco non vuoto di caratteri Word8 a quadratini controllati (tutte le soluzioni sopra riportate restituiscono un elenco di numeri, ma non può succedere che otteniamo [] giusto?)
  2. Questo è ottimizzato per le prestazioni con ottimizzazione della chiamata di coda, concatenazione rapida e nessuna necessità di invertire i valori finali.
  3. Utilizza la syntax di assegnazione speciale che in connessione a -XStrict consente a Haskell di eseguire completamente l’analisi della severità e ottimizzare il ciclo interno.

Godere:

 {-# LANGUAGE Strict #-} digits :: Integral a => a -> NonEmpty Word8 digits = go [] where go sx = loop (head :| s) tail where head = fromIntegral (x `mod` 10) tail = x `div` 10 loop [email protected](r :| rs) = \case 0 -> s x -> go (r : rs) x 
 digits = reverse . unfoldr go where go = uncurry (*>) . (&&&) (guard . (>0)) (Just . swap . (`quotRem` 10))