Analyser une liste de numéros unaires signés


16

Les nombres unaires ne représentent généralement que des entiers non négatifs, mais nous pouvons les étendre pour représenter tous les entiers comme suit:

  • Un entier positif N est représenté par N 1:5 -> 11111
  • Un entier négatif -N est représenté par un 0suivi de N 1:-5 -> 011111
  • Zéro est représenté comme 0

Nous pouvons alors représenter une liste de ces nombres sans ambiguïté si nous utilisons 0comme séparateur:

3,-2,0,1
111,011,0,1
111 0 011 0 0 0 1
11100110001

Votre tâche: prendre une chaîne représentant une telle liste de nombres unaires signés et la traduire en une liste de nombres décimaux.

Détails

Vous pouvez supposer que l'entrée est une liste complète des numéros unaires signés. En particulier, votre programme n'aura pas à gérer 1) une entrée vide ou 2) une entrée qui se termine par un séparateur.

Vous pouvez supposer que la magnitude de chaque nombre ne dépassera pas 127. Pour les langues avec des tailles maximales de chaînes ou de listes, vous pouvez supposer que l'entrée et la sortie s'adapteront aux structures de données de votre langue, mais votre algorithme devrait théoriquement fonctionner pour une liste de n'importe quelle taille.

Votre programme ou fonction peut effectuer des E / S de n'importe quelle manière standard . L'entrée peut être une chaîne ou une liste de caractères, des chaînes à un seul caractère, des entiers ou des booléens. Vous pouvez utiliser deux caractères quelconques pour représenter 1et0 ; si vous n'utilisez pas 1et 0, veuillez spécifier les caractères que vous utilisez.

La sortie doit être des nombres décimaux dans n'importe quel format de liste raisonnable (en particulier, il doit y avoir une sorte de séparateur entre les nombres). Les nombres négatifs doivent être indiqués par un signe moins, bien que si votre langue a un format différent pour les entiers négatifs, je l'accepterai également. Le zéro peut être représenté dans la sortie comme0 ou -0.

Cas de test

1 -> 1
0 -> 0 (or -0, and similarly for the other test cases)
011 -> -2
1101 -> 2,1
1100 -> 2,0
11001 -> 2,-1
110001 -> 2,0,1
11100110001 -> 3,-2,0,1
00000001 -> 0,0,0,-1
01111011111111001111111111111110111111111111111100111111111111111111111110111111111111111111111111111111111111111111 -> -4,8,-15,16,-23,42
01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -> -127

2
Nitpick: Étant donné que cela contient '0's, ce n'est pas techniquement unaire. Bon défi cependant!
DJMcMayhem

4
@DJMcMayhem Nitpick au nitpick: Techniquement, je n'ai jamais dit que c'était unaire. C'est une extension de l'unaire que j'appelle «unaire signé». ;)
DLosc

@DJMcMayhem IMO, le défi est précisément que le séparateur ( 0) et le préfixe du signe négatif ( 0) sont les mêmes, bien que ce soit toujours sans ambiguïté, car vous ne pouvez pas avoir de signes négatifs au milieu d'un nombre ( 182--693-1un nombre? Non, et ni 1111011000101111pour la même raison).
Erik the Outgolfer

Est-ce correct si la liste en sortie est dans l'ordre inverse de l'entrée?
DJMcMayhem

eh bien techniquement décimal n'est pas décimal non plus car il utilise le symbole '-'
Unlambder

Réponses:


10

Python 2 , 73 70 octets

Une fonction qui prend une chaîne en entrée et renvoie une représentation sous forme de chaîne d'une liste Python. Zéro peut être représenté à la fois par 0et -0(quand il vient en dernier):

lambda s:`map(len,s.split('0'))`.replace('0, ','-').replace('--','0,')

Explication

  1. splitla chaîne d'entrée ssur les zéros.
  2. Prenez la longueur de chaque chaîne dans la liste résultante (en utilisant map).

Cela nous fait beaucoup de chemin. Les zéros étaient des séparateurs après tout. Et les nombres étaient unaires, lenconvertit donc commodément ceux en décimal. Mais maintenant, nous avons foiré toutes les utilisations non séparatrices de 0. Heureusement, toutes les utilisations sans séparateur étaient des zéros non significatifs, ils sont donc venus après un zéro séparateur et nous ont donné des chaînes de longueur nulle ( '00'.split('0') == ['', '', '']). Ces chaînes de longueur nulle sont également devenues à 0cause du len.

  1. Transformez la liste en chaîne (en utilisant des "guillemets inversés" ), afin que nous puissions réparer le désordre plus facilement.
  2. replacechaque zéro qui précède un autre nombre par un signe négatif sur ce nombre à la place. Cela corrige l'utilisation de 0comme signe mais brise les zéros littéraux. Les zéros littéraux ont également été précédés d'un séparateur, ils sont donc devenus des paires de tirets supplémentaires sur le numéro suivant.
  3. replacechacun --retourne dans un 0élément de la "liste".

1
Bienvenue chez PPCG!
Steadybox

C'est une approche vraiment créative! Vous voudrez peut-être ajouter une courte explication afin que ceux qui ne parlent pas Python puissent également apprécier votre réponse.
DLosc

@DLosc, merci, je ne connaissais pas le backtick. Une explication détaillée a également été ajoutée.
mercator du

8

Rétine , 23 21 octets

(.)0
$1 
01
-1
1+
$.&

Essayez-le en ligne!

La première étape (.)0<newline>$1<space>correspond à n'importe quel caractère suivi d'un 0. La correspondance est remplacée par le premier caractère suivi d'un espace. Cela divise la chaîne en nombres individuels.

La deuxième étape 01<newline>-1remplace 0le s avant un bloc de 1s au -signe.

La dernière étape 1+<newline>$.&correspond à tous les blocs de1 et les remplace par la longueur du groupe.

Voici un exemple avec la sortie des étapes individuelles.


Très bien - toutes mes idées semblent cadencer à 24 octets ...
Neil

1
Pourriez-vous s'il vous plaît ajouter une explication? Je ne parle pas de rétine.
Daniel

@Dopapp a ajouté une explication
ovs

7

Vim, 56 octets

:s/\v(0?1*)0?/\1\r/g|%s/0/-/|%s/1*$/\=len(submatch(0))
D

Essayez-le en ligne!

Je n'ai pas posté dans vim depuis un moment. J'utilise principalement vim parce que V est parfois une douleur. Parce que lecount commande, qui est parfaite pour obtenir le nombre de «1» sur la ligne, écrasera tout «0» sur la ligne, donc nous ne pouvons pas le nier par la suite.

Explication:

C'est un octet plus court que la méthode simple:

:s/\v(0?1*)0?/\1\r/g
:%s/0/-
:%s/1*$/\=len(submatch(0))
D

en raison du chaînage des commandes. Puisque celui-ci sépare les commandes, je vais l'utiliser pour l'explication.

:s/                     " Substitute
                        " Search for...
   \v                   "   Enable 'magic'. This determines whether certain atoms require a backslash or not.
                        "   Without it we would have: '\(0\?1*\)0\?', which is 2 bytes longer
      0?                "   An optional 0
        1*              "   Followed by any number of '1's
     (    )             "   (call that group 1)
           0?           "   Followed by another optional 0
             /          " Replace it with...
              \1        "   Subgroup 1
                \r      "   A newline
                  /g    " Do this for every match on the current line.

Maintenant, chaque numéro unaire signé est sur une ligne individuelle. En utilisant '11100110001' comme exemple, à ce stade, nous aurons:

111
011
0
1

:%s/0   " Replace every 0
     /- " With a dash  

:%s/1*$/                    " Replace every run of 1's at the end of a line
        \=len(submatch(0))  " With the length of said run

Depuis que nous avons ajouté des nouvelles lignes à la fin de chaque match, nous avions une ligne vide avant d'exécuter cela. Après avoir exécuté cela, nous aurons un «0» (car il correspondait à une série de 0 «1»). Nous appelons donc simplement Dà supprimer cette ligne, en la laissant vide


Pouah. :%s/1+$/vous obtiendrait un octet plus court si ce n'est pour la nécessité de faire une barre oblique inverse le +:(
NieDzejkob

@NieDzejkob Je ne comprends pas pourquoi ce serait plus court. Et aussi, cela donnerait -au lieu de 0ou-0
DJMcMayhem

Je voulais éliminer la dernière ligne de cette façon: P, peu importe.
NieDzejkob

7

Haskell , 68 66 octets

f(x:r)|(a,b)<-span(>0)r=([(0-),(1+)]!!x$sum a):[z|_:t<-[b],z<-f t]

Essayez-le en ligne! Prend l'entrée comme une liste de zéros et de uns. Exemple d'utilisation: f [0,0,0,1,1]rendements [0,-2].

Explication:

Le modèle correspondant dans f(x:r)|(a,b)<-span(>0)rse lie xau premier élément de l'entrée, aà une liste (potentiellement vide) des 1s suivants et bau reste de l'entrée. Étant donné une entrée [0,1,1,1,0,0,1], nous obtenons x=0, a=[1,1,1]etb=[0,0,1] .

Le nombre actuel est alors soit la somme des asi niés x=0, soit la somme de aplus un si x=1. Ceci est réalisé en indexant avec xdans une liste contenant une fonction de négation et d'incrémentation, et en appliquant la fonction résultante à la somme de a:[(0-),(1+)]!!x$sum a .

La liste de repos best soit vide, soit contient un zéro de séparation et le numéro suivant. La compréhension de la liste [z|_:t<-[b],z<-f t]essaie de correspondre bsur le modèle _:t, c'est-à-dire d'oublier l'élément head et de lier le reste de la liste t. Si best vide, cette correspondance échoue et la compréhension de la liste est évaluée [], ce qui est le cas de base pour la récursivité. Sinon, la fonction fest récursivement appliquée tet la compréhension de la liste est évaluée pour tous les éléments à zpartir du résultat de f t.


3

Wolfram Language (Mathematica) , 80 octets

StringCases[#<>"0",x_~~Shortest@y___~~"0":>(If[x=="0",-#,#+1]&)@StringLength@y]&

Essayez-le en ligne!

Abuse le mécanicien de StringCases , car il ne vérifie pas les motifs qui se chevauchent. Puisque nous recherchons de gauche à droite, sans chevauchements, nous n'obtenons toujours que les entiers dont nous avons besoin.

Explication

#<>"0"

Ajouter un zéro à la fin

StringCases

Trouvez tous les modèles suivants ...

x_~~Shortest@y___~~"0"

Un seul caractère (appelez-le x), suivi de la plus courte longueur nulle possible ou d'une chaîne plus longue (appelez-le y), suivi d'un zéro.

(If[x=="0",-#,#+1]&)@StringLength@y

Appliquer au motif correspondant: prendre la longueur de y. Six est égal à zéro, annulez la valeur. Sinon, incrémentez un.

Cela couvre 00également, car ce yserait une chaîne vide, et nous calculerions -0( == 0).


3

Brain-Flak , 94 (70?) Octets

([]){{}({(<()>)()}{}(<>)<>{<([{}]<>{})><>})
{{}<>([{}])(<>)}{}{}([])}{}<>([]){{}({}<>)<>([])}<>

Essayez-le en ligne!

C'est en fait étonnamment concis pour le brain-flak.

Voici une version commentée / lisible:

([])

{

    #Pop the Stack height
    {}

    (
        #If there isn't a leading 0, evaluate to 1...
        {
            (<()>)

            ()
        }

        #Pop the 0
        {}

        #Push a 0 onto the alternate stack
        (<>)
        <>

        #Run of '1's
        {
            #Decrement the alternate stack
            <([{}]<>{})>
            <>
        }

        #And push it here
    )

    #Was there a not leading 0?

    {
        {}

        #Invert the value on the alternate stack
        <>([{}])(<>)
    }

    #Pop 2 zeros
    {}{}


    ([])

}{}<>

#Push stack height
([])

#Reverse the stack
{

    {}

    ({}<>)

    <>([])

}<>

Si la sortie peut être inversée, nous pouvons le faire pour 70 à la place:

([]){{}({(<()>)()}{}(<>)<>{<([{}]<>{})><>}){{}<>([{}])(<>)}{}{}([])}<>

Cette astuce est presque parfaite pour cette situation. Mais cela ne fonctionne pas vraiment car nous devons pousser un 0 avant de faire l'opération (en comptant les «1»), et l'opération se déroule en boucle. Le plus court que j'ai pu trouver en utilisant cette astuce est:

([]){{}({(<()>)()}{}(<>)<>{<([{}]<>{})><>})
{{}<>([{}])(<>)}{}{}(<>())<>([])}{}<>{{}({}<>)<>}<>

qui est également de 94 octets.



3

Husk , 20 18 17 15 14 octets

Γ~:?Σṁ_Πȯ₀tΣġ/

Essayez-le en ligne!

Explication

Γ~:?Σṁ_Πȯ₀tΣġ/  Input is a list, say x = [0,1,1,0,0,0,1,1]
            ġ   Group by
             /  division.
                This splits x right before each 0: [[0,1,1],[0],[0],[0,1,1]]
Γ               Deconstruct into head y = [0,1,1] and tail z = [[0],[0],[0,1,1]]
   ?Σṁ_Π        Apply to y:
       Π         Product: 0
   ?Σ            If that is nonzero, take sum of y,
     ṁ_          else take sum of negated elements of y: u = -2
        ȯ₀tΣ    Apply to z:
           Σ     Concatenate: [0,0,0,1,1]
          t      Drop first element: [0,0,1,1]
         ₀       Recurse: [0,2]
 ~:             Tack u to the front: [-2,0,2]

Le fractionnement fonctionne comme ça. ġ/divise son argument entre chaque paire d'éléments a,bpour laquelle /a best falsifiée. /a best une division avec des arguments inversés, donc bdivisée par a. Les valeurs pertinentes de ce programme sont les suivantes:

  • /1 1donne 1(véridique).
  • /1 0donne 0(fausse).
  • /0 1donne Inf(infini positif, véridique).
  • /0 0donne Any(une valeur spéciale de type NaN, falsy).

3

Acc !! , 252 237 octets

N
Count i while _/48 {
Count n while 48/_ {
Write 45
50+N
}
_+49/_*50
Count u while _%50/49 {
_+100-_%50+N
}
_/50-1
Count h while _/200 {
Write _/200+48
_%200+1
}
Count t while _/20+_%2 {
Write _/20+48
_%20-_%2
}
Write _/2+48
Write 9
N
}

Utilise -0. Affiche les nombres séparés par des caractères de tabulation, avec un onglet de fin. Essayez-le en ligne!

Durée d'écriture de l'algorithme réel: 20 minutes. Temps de débogage de mon code de sortie décimal: 45 minutes. : ^ P

Avec commentaires

Je ne sais pas si ces commentaires expliquent très bien le code - ils sont basés sur mes notes à moi-même pendant que je l'écrivais, alors ils supposent une certaine compréhension de la façon dont Acc !! travaux. Si quelque chose a besoin de plus d'explications, faites-le moi savoir et j'essaierai de le clarifier.

# We partition the accumulator _ as [number][flag][nextchar]
# [flag] is a 2-value slot and [nextchar] a 50-value slot
# So [nextchar] is _%50, [flag] is _/50%2, [number] is _/100
# [flag] is 1 if we're in the middle of reading a number, 0 if we're between numbers
# It is also used for outputting as decimal (see below)
# Possible input characters are 0, 1, and newline, so [nextchar] is 48, 49, or 10

# Read the first character
N
# Loop while the character we just read is 0 or 1 and not newline
Count i while _/48 {
  # What we do in the loop depends on the combination of [flag] and [nextchar]:
  # 0,48 (start of number, read 0) => write minus sign, [flag] = 1, read another char
  # _,49 (read 1) => increment [number], [flag] = 1, read another char
  # 1,48 (middle of number, read 0) => write/clear [number], status = 0, read another
  #      char
  # 1,10 (middle of number, read <cr>) => ditto; the next read will be 0 for eof, which
  #      means the acc will be less than 48 and exit the loop

  # Process leading 0, if any
  Count n while 48/_ {
    # acc is 48: i.e. [number] is 0, [flag] is 0, [nextchar] is 48 (representing a 0)
    # Output minus sign
    Write 45
    # Set [flag] to 1 (thereby exiting loop) and read [nextchar]
    50+N
  }
  # If number starts with 1, then we didn't do the previous loop and [flag] is not set
  # In this case, acc is 49, so we add (50 if acc <= 49) to set [flag]
  _+49/_*50

  # Process a run of 1's
  Count u while _%50/49 {
    # [nextchar] is 49 (representing a 1)
    # Increment [number] and read another
    _+100-_%50+N
  }

  # At this stage, we know that we're at the end of a number, so write it as decimal
  # This is "easier" (ha) because the number has at most three digits
  # We shift our partitioning to [number][flag] and set [flag] to 0
  _/50-1

  # Output hundreds digit if nonzero
  # Since [number] is _/2, the hundreds digit is _/200
  Count h while _/200 {
    Write _/200+48
    # Mod 200 leaves only tens and units; also, set [flag] to 1
    _%200+1
  }
  # Output tens digit (_/20) if nonzero OR if there was a hundreds digit
  # In the latter case, [flag] is 1
  Count t while _/20+_%2 {
    Write _/20+48
    # Mod 20 leaves only units; clear [flag] if it was set
    _%20-_%2
  }
  # Write units unconditionally
  Write _/2+48

  # Write a tab for the separator
  Write 9
  # Read another character
  N
}


2

R , 119 octets

function(x){n=nchar
y=regmatches(x,regexec("(0?)(1*)0?([01]*)",x))[[1]]
cat((-1)^n(y[2])*n(y[3]),"")
if(y[4]>0)f(y[4])}

Essayez-le en ligne!

Le code utilise cette solution de stackoverflow pour un problème connexe (Merci aux jaloux pour l'idée). La sortie est une chaîne séparée par des espaces imprimée sur stdout.


2

Gelée ,  19  18 octets

Il doit y avoir une meilleure façon ...

®ḢN$Ḣ©?ṄEȧ
ṣ0L€ÇL¿

Un programme complet imprimant chaque numéro suivi d'un saut de ligne.

Essayez-le en ligne!

Comment?

®ḢN$Ḣ©?ṄEȧ - Link 1, print first number and yield next input: list of numbers, X
           -                              e.g. [8,0,15,16,...] or [0,4,8,0,15,16,...]
      ?    - if...
    Ḣ      - condition: yield head and modify  8([0,15,16,...])   0([4,8,0,15,16,...])  
     ©     -            (copy to register)     8                  0
®          - then: recall from the register    8
   $       - else: last two links as a monad:
 Ḣ         -         yield head and modify                        4([8,0,15,16,...])
  N                  negate                                      -4
       Ṅ   - print that and yield it           8                 -4
        E  - all equal (to get 0 to be truthy) 1                  1
         ȧ - AND the (modified) input          [0,15,16,...]      [8,0,15,16,...]
           -   (ready to be the input for the next call to this link)

ṣ0L€ÇL¿ - Main link: list e.g. [0,1,0,0,0,0,1,1]
ṣ0      - split at zeros       [[],[1],[],[],[],[1,1]
  L€    - length of €ach       [0,1,0,0,0,2]
      ¿ - while...
     L  - condition: length                           1  1  1  0  ([0,1,0,0,0,2], [0,0,0,2], [0,2], [])
    Ç   - action: call the last link (1) as a monad  -1  0 -2     ( - 1            - 0        - 2)

1

QBasic, 88 86 octets

1u$=INPUT$(1)
z=u$<"1
IF n*z THEN?(1-2*s)*(n-s):s=0:n=0ELSE s=s-z:n=n+1
IF"!"<u$GOTO 1

C'était amusant. Plusieurs révisions à partir d'une version de 107 octets ont abouti à l'un des bits les plus obscurs de QBasic que je pense avoir jamais écrit.(Edit: Curieusement, j'ai pu jouer au golf 2 octets en rendant le code plus clair.)

Remarque: ce programme lit l'utilisateur saisi un caractère à la fois sans l'écho à l'écran (résultat de l'utilisation INPUT$(1) au lieu de l' INPUTinstruction habituelle ). Ainsi, pendant que vous tapez, vous ne verrez pas les 1 et les 0, mais les nombres décimaux apparaîtront lorsqu'ils seront calculés. Assurez-vous de frapperEnter à la fin de l'entrée pour voir le dernier numéro et terminer le programme.

Version non golfée

sign = 0
num = 0
DO
  digit$ = INPUT$(1)
  isZero = (digit$ < "1")
  IF num > 0 AND isZero THEN
    PRINT (1 - 2 * sign) * (num - sign)
    sign = 0
    num = 0
  ELSE
    IF isZero THEN sign = 1
    num = num + 1
  END IF
LOOP WHILE "!" < digit$

Explication

(AKA "Quoi ?? Cela n'a toujours aucun sens!")

La stratégie de base consiste à exécuter une boucle qui saisit un caractère à INPUT$(1)chaque fois, fait des trucs avec elle et continue de boucler tant que le caractère a une valeur ASCII supérieure à celle de! (c.-à-d., Ce n'était pas une nouvelle ligne).

Nous suivons les nombres en cours en utilisant deux variables. numest le nombre de caractères du nombre unaire signé actuel (y compris tout zéro non significatif). signest 1si le nombre avait un zéro 0non significatif, sinon. Ces deux doivent être initialisés à 0, ce qui est idéal pour la version golfée car les variables numériques dans QBasic sont automatiquement initialisées à0 .

Chaque fois que nous lisons un caractère, la première chose à faire est de déterminer si c'est 1ou 0. Nous utiliserons ce résultat deux fois, nous le stockons donc isZero. Techniquement, ce nom est trompeur, car la valeur sera également vraie si le personnage est une nouvelle ligne. Notez que la vérité dans QBasic est -1et falsey est0 .

Maintenant, si nous sommes en train de lire un nombre ( num > 0) et que nous frappons un zéro ou une fin d'entrée ( isZero), nous devons calculer le nombre que nous avons fini de lire.

  • signstocke 0pour positif, 1pour négatif. Pour obtenir 1du positif et -1du négatif, nous avons besoin1-2*sign .
  • numstocke la magnitude correcte pour les positifs mais une de plus que la magnitude pour les négatifs (car elle inclut le marqueur de signe). Nous pouvons donc utiliser num-signpour l'ampleur.

Multipliez-les ensemble et imprimez; puis réinitialiser signet numà0 en préparation de la lecture du numéro suivant.

Sinon (si nous n'avons pas atteint un zéro, ou si nous avons atteint un zéro au début d'un nombre), nous mettons à jour signet numcomme suit:

  • signdevient 1si nous regardons un zéro de tête; sinon, si nous en regardons un, il reste à ce qu'il était déjà. Le code golfé ests=s-z , ce qui revient au même:
    • S'il s'agit d'un zéro non significatif, zest -1. Depuis sest garanti d'être 0(parce que c'est le début d'un nouveau numéro), s-zsera 1.
    • Si c'est un, zc'est 0. s-zReste ensuite à la valeur sprécédente.
  • num est incrémenté.

C'est ça!


0

JavaScript (ES6), 60 octets

Renvoie une liste d'entiers séparés par des espaces.

s=>(0+s).replace(/00?1*/g,s=>(l=s.length,+s[1]?l-1:2-l)+' ')

Cas de test


0

Lua , 58 octets

(...):gsub("(0?)(1*)0?",function(s,n)print(#n-2*#s*#n)end)

Essayez-le en ligne!

Programme complet, prend les entrées de la ligne de commande et imprime les nombres sur stdout séparés par des retours à la ligne.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.