web-dev-qa-db-fra.com

Divisez un nombre en ses chiffres avec Haskell

Étant donné un nombre arbitraire, comment puis-je traiter chaque chiffre du numéro individuellement?

Edit J'ai ajouté un exemple de base du genre de chose que Foo pourrait faire.

Par exemple, en C #, je pourrais faire quelque chose comme ceci:

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);
}
33
Greg B

Avez-vous entendu parler de div et mod ?

Vous voudrez probablement inverser la liste des nombres si vous souhaitez d'abord traiter le chiffre le plus significatif. La conversion du nombre en chaîne est une manière altérée de faire les choses.

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

Généraliser en fonction:

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

Ou en sens inverse:

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

Cela traite 0 comme n'ayant pas de chiffres. Une fonction wrapper simple peut traiter ce cas spécial si vous le souhaitez.

Notez que cette solution ne fonctionne pas pour les nombres négatifs (l'entrée x doit être intégrale, c'est-à-dire un nombre entier).

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

ou vous pouvez le renvoyer dans []:

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

ou, avec Data.Char.digitToInt:

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

le même que celui de Daniel, mais sans point et utilise Int, car un chiffre ne doit pas vraiment dépasser maxBound :: Int.

19
muhmuhten

Vous pouvez également simplement réutiliser digits de Hackage.

13
hammar

En utilisant la même technique utilisée dans votre message, vous pouvez faire:

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

Voyez-le en action:

Prelude> digits 123
[1,2,3]

Est ce que ça aide?

12
Daniel

Déplier le manuel

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

Vous pouvez utiliser

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

ou pour l'ordre inverse

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

La partie itérée génère une liste infinie divisant l'argument à chaque étape par 10, donc 12345 devient [12345,1234,123,12,1,0,0 ..]. La partie takeWhile ne prend que la partie non nulle intéressante de la liste. Ensuite, nous inversons (si nous le voulons) et prenons le dernier chiffre de chaque numéro de la liste.

J'ai utilisé un style sans point ici, vous pouvez donc imaginer un argument invisible n des deux côtés de "l'équation". Cependant, si vous voulez l'écrire de cette façon, vous devez remplacer le niveau supérieur . par $:

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

Voici une amélioration par rapport à une réponse ci-dessus. Cela évite le 0 supplémentaire au début (Exemples: [0,1,0] pour 10, [0,1] pour 1). Utilisez la correspondance de motifs pour gérer les cas où x <10 différemment:

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

J'aurais mis ceci dans une réponse à cette réponse, mais je n'ai pas les points de réputation nécessaires :(

2
granmoe

Applicatif . sans point . Origami . Soigné.

Prendre plaisir:

import Data.List                                                                
import Data.Tuple                                                               
import Data.Bool                                                                
import Control.Applicative 

digits = unfoldr $ liftA2 (bool Nothing) (Just . swap . (`divMod` 10)) (> 0) 
2
Scarabyte

Via la compréhension de la liste:

import Data.Char

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

production:

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

J'étais paresseux pour écrire ma fonction personnalisée alors je l'ai googlé et tbh j'ai été surpris qu'aucune des réponses sur ce site Web n'ait fourni une très bonne solution - hautes performances et type sûr. Alors voilà, peut-être que quelqu'un voudrait l'utiliser. Fondamentalement:

  1. Il est de type sécurisé - il renvoie une liste non vide de chiffres Word8 vérifiée par type (toutes les solutions ci-dessus renvoient une liste de nombres, mais il ne peut pas arriver que nous obtenions [] droite?)
  2. Celui-ci est optimisé pour les performances avec une optimisation des appels de queue, une concaténation rapide et aucun besoin de faire une inversion des valeurs finales.
  3. Il utilise une syntaxe d'affectation spéciale qui, en liaison avec -XStrict permet à Haskell d'effectuer une analyse de rigueur complète et d'optimiser la boucle interne.

Prendre plaisir:

{-# LANGUAGE Strict #-}

digits :: Integral a => a -> NonEmpty Word8
digits = go [] where
    go s x = loop (head :| s) tail where
        head = fromIntegral (x `mod` 10)
        tail = x `div` 10
    loop s@(r :| rs) = \case
        0 -> s
        x -> go (r : rs) x
1
Wojciech Danilo

Pour avoir renvoyé une liste de [Entier]

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

La réponse acceptée est excellente mais échoue en cas de nombres négatifs car mod (-1) 10 est évalué à 9. Si vous souhaitez que cela gère correctement les nombres négatifs ... ce qui n'est peut-être pas le cas, le code suivant le permettra .

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

La réponse acceptée est correcte, sauf qu'elle affichera une liste vide lorsque l'entrée est 0, mais je pense que la sortie doit être [0] lorsque l'entrée est nulle.

Et je ne pense pas que cela traite du cas où l'entrée est négative. Voici ma mise en œuvre, qui résout les deux problèmes ci-dessus.

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" 
0
Leonard Ge