Analizar una lista de números unarios firmados

16

Los números unarios generalmente solo representan enteros no negativos, pero podemos extenderlos para representar todos los enteros de la siguiente manera:

  • Un entero positivo N se representa como N 1's:5 -> 11111
  • Un entero negativo -N se representa como 0seguido por N 1's:-5 -> 011111
  • Cero se representa como 0

Entonces podemos representar una lista de estos números sin ambigüedades si usamos 0como separador:

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

Su tarea: tomar una cadena que represente dicha lista de números unarios con signo y traducirla en una lista de números decimales.

Detalles

Puede suponer que la entrada es una lista completa de números unarios con signo. En particular, su programa no tendrá que manejar 1) entrada vacía o 2) entrada que termina con un separador.

Puede suponer que la magnitud de cada número no excederá 127. Para los idiomas con tamaños máximos de cadenas o listas, puede suponer que la entrada y la salida encajarán en las estructuras de datos de su idioma, pero su algoritmo debería funcionar teóricamente para una lista de cualquier tamaño.

Su programa o función puede realizar E / S de cualquiera de las formas estándar . La entrada puede ser una cadena o una lista de caracteres, cadenas de un solo carácter, enteros o booleanos. Puede usar cualquiera de los dos caracteres para representar 1y 0; si no usa 1y 0, especifique qué caracteres está usando.

La salida debe ser números decimales en cualquier formato de lista razonable (en particular, debe haber algún tipo de separador entre números). Los números negativos deben indicarse con un signo menos, aunque si su idioma tiene un formato diferente para enteros negativos, también lo aceptaré. Cero puede representarse en la salida como 0o -0.

Casos de prueba

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
DLosc
fuente
2
Nitpick: dado que esto contiene '0's, no es técnicamente unario. Buen desafío sin embargo!
DJMcMayhem
44
@DJMcMayhem Nitpick al nitpick: técnicamente nunca dije que fuera unario. Es una extensión de unario que llamo "unario firmado". ;)
DLosc
@DJMcMayhem IMO, el desafío es específicamente que el separador ( 0) y el prefijo de signo negativo ( 0) son iguales, aunque todavía no es ambiguo, ya que no puede tener signos negativos en el medio de un número (¿es 182--693-1un número? No, y tampoco lo es 1111011000101111exactamente por la misma razón).
Erik the Outgolfer
¿Está bien si la lista de salida está en el orden inverso de la entrada?
DJMcMayhem
bueno, técnicamente el decimal tampoco es decimal ya que usa el símbolo '-'
Desenlazar

Respuestas:

10

Python 2 , 73 70 bytes

Una función que toma una cadena como entrada y devuelve una representación de cadena de una lista de Python. Cero puede representarse tanto por 0y -0(cuando llega el último):

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

Explicación

  1. splitla cadena de entrada sen ceros.
  2. Tome la longitud de cada cadena en la lista resultante (usando map).

Eso nos lleva un largo camino. Los ceros eran separadores después de todo. Y los números eran unarios, por lo que lenconvenientemente los convierte a decimales. Pero ahora hemos estropeado todos los usos sin separador de 0. Afortunadamente, todos los usos sin separador fueron ceros iniciales, por lo que vinieron después de un separador cero y nos dieron cadenas de longitud cero ( '00'.split('0') == ['', '', '']). Esas cadenas de longitud cero también se convirtieron 0por el len.

  1. Convierta la lista en una cadena ( usando "comillas inversas" ), para que podamos arreglar el desastre más fácilmente.
  2. replacecada cero que precede a otro número por un signo negativo en ese número en su lugar. Eso corrige el uso de 0como un signo pero rompe los ceros literales. Los ceros literales también fueron precedidos por un separador, por lo que ahora se han convertido en pares de guiones adicionales en el siguiente número.
  3. replacecada uno de --nuevo en un 0elemento en la "lista".
mercator
fuente
1
Bienvenido a PPCG!
Steadybox
¡Este es un enfoque realmente creativo! Es posible que desee agregar una breve explicación para que aquellos que no hablan Python puedan apreciar su respuesta también.
DLosc
@DLosc, gracias, no sabía sobre el backtick. Explicación verbal también añadida.
mercator
8

Retina , 23 21 bytes

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

Pruébalo en línea!

La primera etapa (.)0<newline>$1<space>coincide con cualquier personaje seguido de a 0. El partido se reemplaza por el primer personaje seguido de un espacio. Esto divide la cadena en los números individuales.

La segunda etapa 01<newline>-1reemplaza 0's antes de un bloque de 1' s al -letrero.

La última etapa 1+<newline>$.&coincide con todos los bloques de 1's y los reemplaza con la longitud del grupo.

Aquí hay un ejemplo con la salida de las etapas individuales.

ovs
fuente
Muy agradable: todas mis ideas parecen tener 24 bytes ...
Neil
1
¿Podría por favor agregar una explicación? No hablo retina.
Daniel
@Dopapp agregó una explicación
ovs
7

Vim, 56 bytes

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

Pruébalo en línea!

No he publicado en vim en mucho tiempo. Principalmente estoy usando vim porque V es un dolor a veces. Porque elcount comando, que es perfecto para obtener el número de '1 en la línea, sobrescribirá cualquier' 0 en la línea, por lo que no podemos negarlo después.

Explicación:

Este es un byte más corto que la forma directa:

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

debido al encadenamiento de comandos. Como ese separa los comandos, lo usaré para la explicación.

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

Ahora, cada número unario firmado está en una línea individual. Usando '11100110001' como ejemplo, en este punto tendremos:

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

Como agregamos nuevas líneas al final de cada partido, teníamos una línea vacía antes de ejecutarla. Después de ejecutar eso, tendremos un '0' (porque coincidió con una ejecución de 0 '1's). Entonces llamamos Dpara eliminar esta línea, dejándola en blanco

DJMcMayhem
fuente
Ugh :%s/1+$/le daría un byte más corto si no fuera por la necesidad de +
reducir
@NieDzejkob No entiendo por qué eso sería más corto. Y también, eso daría en -lugar de 0o-0
DJMcMayhem
Quería eliminar la última línea de esa manera: P, no importa.
NieDzejkob
7

Haskell , 68 66 bytes

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

Pruébalo en línea! Toma datos como una lista de ceros y unos. Ejemplo de uso: f [0,0,0,1,1]rendimientos [0,-2].

Explicación:

La coincidencia de patrones se f(x:r)|(a,b)<-span(>0)rune xal primer elemento de la entrada, aa una lista (potencialmente vacía) de los siguientes 1s, y bal resto de la entrada. Dada una entrada [0,1,1,1,0,0,1], obtenemos x=0,a=[1,1,1] yb=[0,0,1] .

El número actual es entonces la suma de anegado if x=0o la suma de amás uno if x=1. Esto se logra indexando con xuna lista que contiene una función de negación e incremento, y aplicando la función resultante a la suma de a:[(0-),(1+)]!!x$sum a .

La lista de descanso bestá vacía o contiene un cero de separación y el siguiente número. La comprensión de la lista [z|_:t<-[b],z<-f t]intenta coincidir bcon el patrón _:t, es decir, olvidar el elemento principal y vincular el resto de la lista t. Si bestá vacío, esta coincidencia falla y la comprensión de la lista se evalúa como [], que es el caso base para la recursividad. De lo contrario, la función fse aplica de forma recursiva ty la comprensión de la lista se evalúa en todos los elementos a zpartir del resultado de f t.

Laikoni
fuente
3

Wolfram Language (Mathematica) , 80 bytes

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

Pruébalo en línea!

Abusa de la mecánica de StringCases, ya que no comprueba patrones superpuestos. Como buscamos de izquierda a derecha, sin superposiciones, siempre obtenemos solo los enteros que necesitamos.

Explicación

#<>"0"

Añadir un cero al final

StringCases

Encuentra todos los siguientes patrones ...

x_~~Shortest@y___~~"0"

Un solo carácter (llámelo x), seguido de la cadena más corta posible de longitud cero o más larga (llámelo y), seguido de un cero.

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

Aplicar al patrón correspondiente: tome la longitud de y. Si xes cero, entonces niega el valor. De lo contrario, incremente uno.

Esto también cubre 00, ya yque sería una cadena vacía, y calcularíamos -0( == 0).

JungHwan Min
fuente
3

Brain-Flak , 94 (70?) Bytes

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

Pruébalo en línea!

En realidad, esto es sorprendentemente breve para el ataque cerebral.

Aquí hay una versión comentada / legible:

([])

{

    #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 salida puede ser inversa, podemos hacer esto para 70 en su lugar:

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

Este consejo mío es casi perfecto para esta situación. Pero no funciona del todo, ya que tenemos que presionar un 0 antes de realizar la operación (contando los '1'), y la operación ocurre en un bucle. Lo más corto que se me ocurre utilizar este consejo es:

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

que también es de 94 bytes.

DJMcMayhem
fuente
3

Casco , 20 18 17 15 14 bytes

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

Pruébalo en línea!

Explicación

Γ~:?Σṁ_Πȯ₀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 división funciona así. ġ/divide su argumento entre cada par de elementos a,bpara los cuales /a bes falso. /a bes una división con argumentos invertidos, bdividida por a. Los valores relevantes en este programa son estos:

  • /1 1 da 1 (verdad)
  • /1 0da 0(falso).
  • /0 1da Inf(infinito positivo, verdad).
  • /0 0da Any(un valor especial similar a NaN, falsedad).
Zgarb
fuente
3

Acc !! , 252 237 bytes

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
}

Usos -0. Produce números separados por tabuladores, con una tabulación final.Pruébalo en línea!

Cantidad de tiempo que escribe el algoritmo real: 20 minutos. Cantidad de tiempo de depuración de mi código de salida decimal: 45 minutos. : ^ P

Con comentarios

No sé si estos comentarios explican muy bien el código: se basan en mis notas para mí mientras lo escribía, por lo que asumen cierta comprensión de cómo Acc !! trabajos. Si algo necesita más explicación, hágamelo saber y trataré de aclararlo.

# 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
}
DLosc
fuente
2

Python 2 , 96 92 bytes

lambda s:[[len(t),-len(t)+1]['1'>t]for t in s.replace('10','1 ').replace('00','0 ').split()]

Pruébalo en línea!

Thx a ovs y DLosc para 2 bytes cada uno.

Chas Brown
fuente
2

R , 119 bytes

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

Pruébalo en línea!

El código usa esta solución de stackoverflow para un problema relacionado (Gracias a jeales por la idea). La salida es una cadena separada por espacios impresa en stdout.

NofP
fuente
2

Jalea ,  19  18 bytes

Tiene que haber una mejor manera...

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

Un programa completo que imprime cada número seguido de un salto de línea.

Pruébalo en línea!

¿Cómo?

®Ḣ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)
Jonathan Allan
fuente
1

QBasic, 88 86 bytes

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

Esto fue divertido. Múltiples revisiones a partir de una versión de 107 bytes resultaron en uno de los bits más ofuscados de QBasic que creo que jamás haya escrito.(Editar: Curiosamente, pude jugar al golf 2 bytes al aclarar el código).

Nota: este programa lee la entrada del usuario de un carácter a la vez sin hacer eco en la pantalla (como resultado de usar en INPUT$(1)lugar de la INPUTdeclaración habitual ). Entonces, mientras escribe, no verá los 1 y los 0, pero los números decimales aparecerán a medida que se calculan. Asegúrese de presionar Enteral final de la entrada para ver el último número y finalizar el programa.

Versión sin golf

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$

Explicación

(AKA "¿Qué? ¡Eso todavía no tiene sentido!")

La estrategia base es ejecutar un bucle que tome un personaje de INPUT$(1)cada vez, haga cosas con él y siga haciendo bucles siempre que el personaje tenga un valor ASCII mayor que el de! (es decir, no era una nueva línea).

Hacemos un seguimiento de los números en progreso utilizando dos variables. numes el número de caracteres en el número unario actual firmado (incluido cualquier cero inicial). signes1 si el número tenía un cero a la izquierda, 0si no. Ambos deben inicializarse 0, lo cual es excelente para la versión de golf porque las variables numéricas en QBasic se inicializan automáticamente 0.

Cada vez que leemos un personaje, lo primero es determinar si es 1o0 . Usaremos este resultado dos veces, por lo que lo almacenaremos isZero. Técnicamente, ese nombre es engañoso, ya que el valor también será verdadero si el personaje es una nueva línea. Tenga en cuenta que la verdad en QBasic es -1y falsey es 0.

Ahora, si estamos en el medio de leer un número ( num > 0) y llegamos a cero o al final de la entrada (isZero ), necesitamos calcular qué número hemos terminado de leer.

  • signalmacena 0para positivo, 1para negativo. Para obtener 1positivo y-1 negativo, necesitamos 1-2*sign.
  • numalmacena la magnitud correcta para los positivos pero una más que la magnitud para los negativos (ya que incluye el marcador de signo). Entonces podemos usarnum-sign para la magnitud.

Multiplique estos juntos e imprima; luego reiniciar signynum que 0en la preparación para la lectura del siguiente número.

De lo contrario (si no hemos alcanzado un cero, o si hemos alcanzado un cero al comienzo de un número), actualizamos signy de la numsiguiente manera:

  • signse convierte 1si estamos viendo un cero a la izquierda; de lo contrario, si estamos viendo uno, se queda en lo que ya era. El código de golf es s=s-z, lo que equivale a lo mismo:
    • Si este es un cero inicial, zes -1. Como sse garantiza que es 0(porque este es el comienzo de un nuevo número), s-zserá1 .
    • Si este es uno, zes 0. Luego se s-zqueda en cualquier valor que stenía previamente.
  • num se incrementa

¡Eso es!

DLosc
fuente
0

JavaScript (ES6), 60 bytes

Devuelve una lista de enteros separados por espacios.

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

Casos de prueba

Arnauld
fuente
0

Lua , 58 bytes

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

Pruébalo en línea!

Programa completo, toma la entrada de la línea de comando e imprime los números en stdout separados por nuevas líneas.

Jonathan S.
fuente