Analizza un elenco di numeri unari firmati


16

I numeri unari in genere rappresentano solo numeri interi non negativi, ma possiamo estenderli per rappresentare tutti i numeri interi come segue:

  • Un numero intero positivo N è rappresentato come N 1:5 -> 11111
  • Un numero intero negativo -N è rappresentato come 0seguito da N 1's:-5 -> 011111
  • Zero è rappresentato come 0

Possiamo quindi rappresentare un elenco di questi numeri senza ambiguità se utilizziamo 0come separatore:

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

Il tuo compito: prendi una stringa che rappresenta un tale elenco di numeri unari firmati e traducilo in un elenco di numeri decimali.

Dettagli

Si può presumere che l'input sia un elenco completo di numeri unari firmati. In particolare, il tuo programma non dovrà gestire 1) input vuoto o 2) input che termina con un separatore.

Puoi supporre che l'entità di ciascun numero non superi 127. Per le lingue con dimensioni massime di stringhe o elenchi, puoi supporre che l'input e l'output si adattino alle strutture di dati della tua lingua, ma l'algoritmo dovrebbe teoricamente funzionare per un elenco di qualsiasi dimensione.

Il programma o la funzione può eseguire l'I / O in uno dei modi standard . L'input può essere una stringa o un elenco di caratteri, stringhe a carattere singolo, numeri interi o valori booleani. È possibile utilizzare due caratteri qualsiasi per rappresentare 1e 0; se non usi 1e 0, specifica quali caratteri stai usando.

L'output deve essere composto da numeri decimali in qualsiasi formato di elenco ragionevole (in particolare, deve esserci una sorta di separatore tra numeri). I numeri negativi dovrebbero essere indicati con un segno meno, anche se se la tua lingua ha un formato diverso per gli interi negativi accetterò anche quello. Lo zero può essere rappresentato nell'output come 0o -0.

Casi 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: poiché contiene '0's, non è tecnicamente unario. Buona sfida però!
DJMcMayhem

4
@DJMcMayhem Nitpick al nitpick: tecnicamente non ho mai detto che fosse unario. È un'estensione di unario che chiamo "firmata unaria". ;)
DLosc il

@DJMcMayhem IMO, la sfida è nello specifico che il separatore ( 0) e il prefisso del segno negativo ( 0) sono gli stessi, anche se è ancora inequivocabile, dal momento che non è possibile avere segni negativi nel mezzo di un numero (è 182--693-1un numero? No, e nessuno dei due è 1111011000101111per lo stesso identico motivo).
Erik the Outgolfer,

Va bene se l'elenco di output è in ordine inverso rispetto all'input?
DJMcMayhem

bene tecnicamente il decimale non è neanche decimale poiché utilizza il simbolo '-'
Unlambder

Risposte:


10

Python 2 , 73 70 byte

Una funzione che accetta una stringa come input e restituisce una rappresentazione in formato stringa di un elenco Python. Lo zero può essere rappresentato da 0e -0(quando arriva per ultimo):

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

Spiegazione

  1. splitla stringa di input ssu zero.
  2. Prendi la lunghezza di ogni stringa nell'elenco risultante (usando map).

Ci vuole molta strada. Gli zeri erano dei separatori dopo tutto. E i numeri erano unari, così lenconvenientemente li converte in decimali. Ma ora abbiamo incasinato tutti gli usi del non separatore 0. Fortunatamente, tutti gli usi non separatori erano zeri iniziali, quindi sono arrivati ​​dopo uno zero separatore e ci hanno dato stringhe di lunghezza zero ( '00'.split('0') == ['', '', '']). Quelle stringhe di lunghezza zero diventarono quindi anche a 0causa del len.

  1. Trasforma l'elenco in una stringa ( usando "virgolette inverse" ), così possiamo sistemare il pasticcio più facilmente.
  2. replaceogni zero che precede un altro numero con un segno negativo su quel numero. Ciò risolve l'uso di 0come segno ma rompe gli zero letterali. Anche gli zero letterali sono stati preceduti da un separatore, quindi ora sono diventati coppie di trattini aggiuntivi sul numero successivo.
  3. replaceognuno --torna in un 0elemento nella "lista".

1
Benvenuti in PPCG!
Steadybox,

Questo è un approccio davvero creativo! Potresti voler aggiungere una breve spiegazione in modo che anche coloro che non parlano Python possano apprezzare la tua risposta.
DLosc,

@DLosc, grazie, non sapevo del backtick. Aggiunta anche una spiegazione volubile.
mercatore,

8

Retina , 23 21 byte

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

Provalo online!

Il primo livello (.)0<newline>$1<space>corrisponde a qualsiasi personaggio seguito da a 0. La partita è sostituita dal primo personaggio seguito da uno spazio. Questo divide la stringa nei singoli numeri.

Il secondo stadio 01<newline>-1sostituisce 0'prima di un blocco di 1' con il -segno.

L'ultima fase 1+<newline>$.&corrisponde a tutti i blocchi di 1's e li sostituisce con la lunghezza del gruppo.

Ecco un esempio con l'output delle singole fasi.


Molto bello - tutte le mie idee sembrano arrivare a 24 byte ...
Neil

1
Potresti per favore aggiungere una spiegazione? Non parlo Retina.
Daniel,

@Dopapp ha aggiunto una spiegazione
ovs

7

Vim, 56 byte

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

Provalo online!

Non ho pubblicato in vim per un po '. Sto usando principalmente Vim perché V è un dolore a volte. Perché il countcomando, che è perfetto per ottenere il numero di '1 sulla linea, sovrascriverà tutti gli' 0 sulla linea, quindi non possiamo negarlo in seguito.

Spiegazione:

Questo è un byte più breve del modo più semplice:

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

a causa del comando a catena. Dal momento che quello separa i comandi, lo userò per la spiegazione.

: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.

Ora, ogni numero unario firmato è su una singola riga. Usando '11100110001' come esempio, a questo punto avremo:

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

Da quando abbiamo aggiunto nuove righe alla fine di ogni partita, avevamo una riga vuota prima di eseguirla. Dopo averlo eseguito, avremo uno '0' (perché corrisponde a una corsa di 0 '1). Quindi chiamiamo solo Dper eliminare questa linea, lasciandola vuota


Ugh. :%s/1+$/ti ridurrebbe di un byte se non per la necessità di eseguire il backslash di +:(
NieDzejkob il

@NieDzejkob Non capisco perché sia ​​più breve. E inoltre, ciò darebbe -invece 0o-0
DJMcMayhem

Volevo eliminare l'ultima riga in quel modo: P, non importa.
NieDzejkob,

7

Haskell , 68 66 byte

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

Provalo online! Prende l'input come un elenco di zero e uno. Esempio di utilizzo: f [0,0,0,1,1]rese [0,-2].

Spiegazione:

Il pattern matching in si f(x:r)|(a,b)<-span(>0)rlega xal primo elemento dell'input, aa un elenco (potenzialmente vuoto) dei seguenti 1s, e bal resto dell'input. Dato un input [0,1,1,1,0,0,1], otteniamo x=0, a=[1,1,1]e b=[0,0,1].

Il numero corrente è quindi la somma di ase negato x=0o la somma di apiù uno se x=1. Ciò si ottiene con indicizzazione xin un elenco contenente una funzione negazione e di incremento, e applicando la funzione risultante alla somma di a: [(0-),(1+)]!!x$sum a.

L'elenco di riposo bè vuoto o contiene uno zero di separazione e il numero successivo. La comprensione dell'elenco [z|_:t<-[b],z<-f t]cerca di corrispondere bal modello _:t, ovvero dimenticare l'elemento head e associare il resto dell'elenco t. Se bè vuoto, questa corrispondenza ha esito negativo e viene valutata la comprensione dell'elenco [], che è il caso base per la ricorsione. Altrimenti la funzione fviene applicata in modo ricorsivo te la comprensione dell'elenco valuta tutti gli elementi zdal risultato di f t.


3

Wolfram Language (Mathematica) , 80 byte

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

Provalo online!

Abusa il meccanico di StringCases, dal momento che non controlla i modelli sovrapposti. Poiché cerchiamo da sinistra a destra, senza sovrapposizioni, otteniamo sempre solo gli interi di cui abbiamo bisogno.

Spiegazione

#<>"0"

Aggiungi uno zero alla fine

StringCases

Trova tutti i seguenti schemi ...

x_~~Shortest@y___~~"0"

Un singolo carattere (chiamalo x), seguito dalla stringa di lunghezza zero o più corta possibile (chiamalo y), seguito da uno zero.

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

Applica allo schema corrispondente: prendi la lunghezza di y. Se xè zero, quindi negare il valore. Altrimenti, incrementane uno.

Anche questo copre 00, dato che ysarebbe una stringa vuota, e dovremmo calcolare -0( == 0).


3

Brain-Flak , 94 (70?) Byte

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

Provalo online!

Questo è in realtà sorprendentemente conciso per i difetti del cervello.

Ecco una versione commentata / leggibile:

([])

{

    #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
{

    {}

    ({}<>)

    <>([])

}<>

Se l'output può essere al contrario, possiamo invece farlo per 70:

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

Questo mio suggerimento è quasi perfetto per questa situazione. Ma non funziona del tutto poiché dobbiamo premere uno 0 prima di eseguire l'operazione (contando gli "1") e l'operazione avviene in un ciclo. Il più breve che ho potuto trovare utilizzando questo suggerimento è:

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

che è anche 94 byte.



3

Buccia , 20 18 17 15 14 byte

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

Provalo online!

Spiegazione

Γ~:?Σṁ_Πȯ₀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]

La divisione funziona in questo modo. ġ/divide il suo argomento tra ogni coppia di elementi a,bper i quali /a bè falsa. /a bè divisione con argomenti capovolti, così bdivisa per a. I valori rilevanti in questo programma sono questi:

  • /1 11(verità).
  • /1 00(falsa).
  • /0 1Inf(infinito positivo, verità).
  • /0 0Any(uno speciale valore simile a NaN, falsy).

3

Acc !! , 252 237 byte

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
}

Usi -0. Emette numeri separati da caratteri di tabulazione, con una scheda finale. Provalo online!

Tempo di scrittura dell'algoritmo effettivo: 20 minuti. Intervallo di tempo per il debug del mio codice di output decimale: 45 minuti. : ^ P

Con commenti

Non so se questi commenti spieghino molto bene il codice - si basano sulle mie note mentre lo stavo scrivendo, quindi assumono una certa comprensione di come Acc !! lavori. Se qualcosa ha bisogno di più spiegazioni, fammi sapere e proverò a renderlo più chiaro.

# 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 byte

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])}

Provalo online!

Il codice utilizza questa soluzione da StackOverflow per un problema correlato (grazie ai jeales per l'idea). L'output è una stringa separata da spazio stampata su stdout.


2

Gelatina ,  19  18 byte

Deve esserci un modo migliore ...

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

Un programma completo che stampa ogni numero seguito da un avanzamento riga.

Provalo online!

Come?

®Ḣ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 byte

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

È stato divertente. Revisioni multiple a partire da una versione di 107 byte hanno portato a uno dei bit più offuscati di QBasic che penso di aver mai scritto.(Modifica: stranamente, sono stato in grado di golf 2 byte rendendo il codice più chiaro.)

Nota: questo programma legge l'input dell'utente di un carattere alla volta senza riecheggiarlo sullo schermo (risultato dell'uso INPUT$(1)invece della solita INPUTistruzione). Quindi, mentre stai digitando, non vedrai 1 e 0, ma i numeri decimali appariranno mentre vengono calcolati. Assicurati di colpireEnter alla fine dell'ingresso per vedere l'ultimo numero e terminare il programma.

Versione Ungolfed

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$

Spiegazione

(AKA "Cosa ?? Non ha ancora senso!")

La strategia di base è quella di eseguire un ciclo che afferra un personaggio INPUT$(1)ogni volta, fa cose con esso e continua a girare fino a quando il personaggio ha un valore ASCII maggiore di quello di! (cioè, non era una newline).

Teniamo traccia dei numeri in corso utilizzando due variabili. numè il numero di caratteri nel numero unario con segno corrente (incluso qualsiasi zero iniziale). signè 1se il numero aveva uno zero iniziale, in 0caso contrario. Entrambi devono essere inizializzati 0, il che è ottimo per la versione golfata perché le variabili numeriche in QBasic vengono inizializzate automaticamente su0 .

Ogni volta che leggiamo un personaggio, la prima cosa è determinare se è 1o 0. Utilizzeremo questo risultato due volte, quindi lo memorizzeremo isZero. Tecnicamente, quel nome è fuorviante, poiché il valore sarà anche vero se il personaggio è una nuova riga. Nota che la verità in QBasic è -1e la falsità è0 .

Ora, se siamo nel mezzo della lettura di un numero ( num > 0) e raggiungiamo lo zero o la fine dell'input ( isZero), dobbiamo calcolare quale numero abbiamo finito di leggere.

  • signnegozi 0per positivo, 1per negativo. Per ottenere il 1positivo e il -1negativo, abbiamo bisogno 1-2*sign.
  • nummemorizza la grandezza corretta per i positivi ma una in più rispetto alla grandezza per i negativi (poiché include il marker). Quindi possiamo usare num-signper la grandezza.

Moltiplicali insieme e stampali; quindi resettare signe numto 0in preparazione per la lettura del numero successivo.

Altrimenti (se non abbiamo raggiunto uno zero o se abbiamo raggiunto uno zero all'inizio di un numero), aggiorniamo signe numcome segue:

  • signdiventa 1se stiamo osservando uno zero iniziale; altrimenti, se ne stiamo osservando uno, rimarrà qualunque cosa fosse già. Il codice di golf è s=s-z, che equivale alla stessa cosa:
    • Se questo è uno zero iniziale, lo zè -1. Dal momento che sè garantito per essere 0(perché questo è l'inizio di un nuovo numero), s-zsarà1 .
    • Se questo è uno, lo zè 0. Quindi s-zrimane a qualsiasi valore sprecedentemente avuto.
  • num è incrementato.

Questo è tutto!


0

JavaScript (ES6), 60 byte

Restituisce un elenco di numeri interi separati da spazio.

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

Casi test


0

Lua , 58 byte

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

Provalo online!

Programma completo, accetta input dalla riga di comando e stampa i numeri su stdout separati da nuove righe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.