Eliminación de código muerto

20

El código muerto se queda allí sin hacer nada, mirándonos sabiendo que nunca se ejecutará ... pero hoy podemos vengarnos.

Especificación

La entrada será una cadena multilínea.

Cada línea puede ser una asignación o una expresión .

Asignación

Una asignación es de la forma <name> = numberen que nombre es una secuencia de letras, guiones bajos y números, pero no comienza con un número.

Las variables se pueden asignar cualquier cantidad de veces.

Expresión

Una expresión es de la forma <var_name OR number> <operation> <var_name OR number> ...

Una expresión puede ser cualquier combinación de:

  • Variables ya definidas
  • Operadores aritméticos básicos +-*/
  • Números (enteros)

Rendimiento esperado

Debe generar la cadena con asignaciones redundantes , asignaciones que nunca son utilizadas por ninguna de las expresiones que le siguen, eliminadas. Tenga en cuenta que las asignaciones también pueden hacerse redundantes si se realiza una asignación adicional a la misma variable antes de que se ejecute una expresión usando la variable.

Casos de prueba

en

a = 10
a * 3

fuera

a = 10
a * 3

en

foo = 8
2 - 1
a = 18

fuera

2 - 1

en

a = 10
a = 8
b = 4
ab = 72  
b / 6
b + 1

fuera

b = 4
b / 6
b + 1

en

a = 1
a = 2
a + 1

fuera

a = 2
a + 1

en

FooBar1 = 0
Fuz__ = 8
Fuz__ / 1

fuera

Fuz__ = 8
Fuz__ / 1

en

a = 1
a + 1
a = 2
a + 1

fuera

a = 1
a + 1
a = 2
a + 1

en

a = 1
1 / 5 * 8 + 4

fuera

1 / 5 * 8 + 4

en

a = 1
a + 1
a = 1
a + 1

fuera

a = 1
a + 1
a = 1
a + 1

en

a = 7
5 / a

fuera

a = 7
5 / a
Caridorc
fuente
1
¿Debería agregar este caso particularmente difícil a = 1; a + 1; a = 1; a + 1;:? Donde el segundo a = 1se puede descartar solo porque ase estableció previamente en el mismo valor ( 1).
flodel
3
@flodel No, no es necesario mirar los valores
Caridorc
@flodel testcase incorporated
Caridorc
Debe agregar un caso de prueba donde se usa una variable en una expresión, pero no como el primer elemento de la expresión. Especialmente importante es como el último miembro de la expresión.
isaacg
@isaacg sin código muerto, la var puede estar en cualquier lugar, se agregó un caso de
prueba

Respuestas:

9

PHP - 197 bytes

La función funciona analizando cada línea, en el orden inverso y una después de la otra, y manteniendo una matriz de las variables utilizadas.

  • Si hay un carácter igual =en la línea, es una tarea.
    • Si se utiliza la variable, la asignación es útil y la línea se imprime, pero la variable ya no se considera utilizada.
    • De lo contrario, no hagas nada.
  • De lo contrario, la línea es una expresión. Dividimos la línea después de cada espacio y agregamos cada símbolo a la lista de variables utilizadas. Números (1 , 2, ...) y operadores ( +, -, ...) se añadirán también, pero ya que no son nombres de variables válidas, no es un problema. La línea es, por supuesto, impresa.
function($c){$u=[];foreach(array_reverse(split('
',$c))as$l){if($p=strpos($l,'=')){if(!isset($u[$x=substr($l,0,$p-1)]))continue;
unset($u[$x]);}else$u+=array_flip(split(' ',$l));$f="
$l$f";}echo$f;}

Aquí está la versión sin golf:

function removeDeadCode($code)
{
    $usedVariables = [];
    $finalCode = '';

    foreach (array_reverse(explode("\n", $code)) as $line)
    {
        if ($equalPosition = strpos($line, '='))
        {
            $variable = substr($line, 0, $equalPosition - 1);
            if (isset($usedVariables[$variable]))
            {
                $finalCode = "\n" . $line . $finalCode;
                unset($usedVariables[$variable]);
            }
        }
        else
        {
            $usedVariables += array_flip(explode(' ', $line));
            $finalCode = "\n" . $line . $finalCode;
        }
    }

    echo $finalCode;
}
Agujero negro
fuente
7

Retina , 45 bytes

m`^(\w+) =.*\n(?=((?!\b\1\b)[^!])*(^\1 =|\Z))
<empty>

Para fines de conteo, cada línea va en un archivo separado (donde <empty>es un archivo vacío) y \ndebe reemplazarse con un avance de línea real (0x0A).

Esto supone que la cadena siempre terminará con un avance de línea.

Como esta expresión regular no utiliza ninguna característica específica de .NET, puede probarla en regex101 .

La idea es bastante simple: eliminar todas las asignaciones de las cuales podemos encontrar (buscando hacia adelante) otra asignación a la misma variable o al final de la cadena sin pasar otro uso de la variable.

Martin Ender
fuente
6

Pyth, 40 bytes

eMf|!}K\=eT&Jf}+dhceTK++dYdPT!}KeJ_.__.z

Parece un poco largo. Tal vez pueda guardar uno o dos bytes mañana.

Pruébelo en línea: Demostración o conjunto de pruebas

Explicación:

_.__.zda todos los postfixes de las líneas de entrada en orden inverso. Por ejemplo, la entrada FooBar1 = 0; Fuz__ = 8; Fuz__ / 1da la lista:

[['Fuz__ / 1', 'Fuz__ = 8', 'FooBar1 = 0'], 
 ['Fuz__ / 1', 'Fuz__ = 8']
 ['Fuz__ / 1']]

Luego filtro por elementos de la lista T, en los que =no está en el último elemento de T(expresión) o (asignación) el último elemento T, que contiene el nombre de la variable, es una expresión. Luego imprima el último elemento de cada uno de los elementos restantes en una línea separada.

eMf|!}K\=eT&Jf}+dhceTK++dYdPT!}KeJ_.__.z
                                      .z  all input lines
                                     _    reverse order
                                   ._     all prefixes
                                  _       reverse order
  f                                       filter for elements T, which satisfy:
      K\=                                   K = '='
    !}K  eT                                 '=' is not in T[-1]
   |                                        or
             f             PT                 filter for elements Y in T[:-1],
                                              which satisfy:
                 hceTK                          extract variable name of T[-1]
                                                with an additional space at the end
               +d                               and at the beginning
              }       ++dYd                     ^ in space+Y+space
            J                                 assign these list to J
           &                                  J not empty and
                             !KeJ             '=' is not in J[-1]
eM                                        take the last element of each and print
Jakube
fuente
8
Aww es tan lindo ....__.
orlp
Este código falla en pyth.herokuapp.com/…
isaacg
@isaacg Fijo.
Jakube
4

CJam, 49 bytes

LqN/W%{_'=#_){(<:V1$\e=_{\Va-\}&}{;S/+1}?},\;W%N*

Pruébalo en línea

El enfoque aquí es que se mantiene una lista de variables no asignadas mientras se procesan las líneas de entrada de atrás hacia adelante:

  • Si la línea es una expresión, todas las variables en la expresión se agregan a la lista. En realidad, en la implementación, todos los tokens se agregan a la lista, ya que ahorra código y tener números y operadores en la lista no hace ningún daño.

  • Si la línea es una asignación, prueba si el nombre de la variable asignada está en la lista. Si es así, la asignación se acepta y el nombre de la variable se elimina de la lista. De lo contrario, se omite la asignación.

Explicacion:

L     Start with empty list.
qN/   Get input and split at newlines.
W%    Reverse to process lines back to front.
{     Start of filter block.
  _     Copy line.
  '=#   Find equal sign.
  _     Copy position of equal sign, will use original later to extract
        variable name from assignment.
  )     Increment to produce truthy/falsy value (-1 from find means not found).
  {     Start if-block that processes assignments.
    (<    Slice off everything but variable name.
    :V    Save in variable V for later re-use.
    1$\   Place copy of unassigned variable list and new variable name at
          top of stack.
    e=    Count occurrences. This tells us if variable name was in list.
    _     Copy the condition value because it will also be used as the
          overall filter result.
    {     Start of block that removes variable name from list.
      \V    Bring list to top, and push variable name.
      a-    Remove the variable name from list.
      \     Swap to get variable list to bottom of stack for next iteration,
            and filter result to top.
    }&    End of conditional block to remove variable name.
  }     End of if-block for assignment.
  {     Start of else-block for expression.
    ;     Pop result of find operation.
    S/    Split expression at spaces.
    +     Concatenate the new variables with the existing ones in the list.
    1     Filter result, expressions are always accepted.
  }?    End of if for assignment vs. expression.
},    End of filter.
\;    Get rid of variable list at bottom of stack.
W%    Reverse order of filtered result, since we worked back to front.
N*    Join with newlines.
Reto Koradi
fuente
4

Python 2, 270 267 bytes

import sys,re
d={}
s=list(enumerate(sys.stdin))
for n,l in s:
 try:v,_=l.split('=');v=v.strip();d[v]=d.get(v,[])+[[0,n]]
 except:
  for v in re.findall('[a-zA-Z_]\w*',l):d[v][-1][0]+=1
print''.join(l for n,l in s if n not in[n for x in d.values()for c,n in x if c==0])

La sangría es: 1. Espacio 2. Pestaña

¡Guardado 3 bytes gracias a @Kamehameha!

kirbyfan64sos
fuente
Se puede eliminar el espacio después de imprimir adentro print ''.joiny inadentro in [n.
Kamehameha
Además, puede usar este truco usando un en tablugar del doble espacio después de la exceptlínea y guardando un byte.
Kamehameha
2

R 144

Q=R=c()
for(L in rev(scan(,"",,,"\n"))){W=strsplit(L," ")[[1]]
if("="%in%W)if(W[1]%in%R)R=R[R!=W[1]]else next else R=c(R,W)
Q=c(L,Q)}write(Q,"")

dónde

  • L es una línea de la entrada (a partir de la última)
  • W son los símbolos (variables, operadores, números) en una línea
  • Res un vector de símbolos que se imprimirán. Incluye variables cuya asignación es necesaria.
  • Q es el vector de líneas en la salida
flodel
fuente
Se puede reemplazar scan(what="",sep="\n")con scan(,"",sep="\n"). También puede reemplazar el separgumento nombrado con su equivalente posicional, pero no recuerdo dónde irían las comas para eso.
Alex A.
... ahorrando 6. Muy agradable. Gracias Alex!
flodel
2

JavaScript (ES6) 164 177

Usando cadenas de plantilla, todas las nuevas líneas son significativas y contadas.

Pruebe a ejecutar el fragmento en FireFox (requerido para la compatibilidad con ES6, incluidas las funciones de flecha)

f=s=>(k=>{s=s.split`
`,s.map((t,n)=>(r=t.match(/\w+/g)).map(v=>k[v]=f,~t.search`=`?k[s[k[v=r[0]]]=r[0]=0,v]=n:0))
for(v in k)s[k[v]]=0})([])||s.filter(r=>r).join`
`

ungolfed=s=>
{
  k={} // list line defining variables, by name, until used
  s=s.split`\n`
  s.forEach((t,n)=>
  {
    r=t.match(/\w+/g); // list variables in the line, operators are lost
    if (t.search`=` >= 0) // if it's an assignment
    {
      v=r[0] // new variable
      s[k[v]]=r[0]=0 // kill previous definition if not used
      k[v]=n
    }
    r.forEach(v=>k[v]='') // for each used variable, forget its definition line
  })
  for(v in k)s[k[v]]=0; // kill all remaining unused definitions
  return s.filter(r=>r).join`\n`
}

// TEST
out=x=>O.innerHTML+=x+'\n';


;[['a = 10\na * 3', 'a = 10\na * 3']
 ,['foo = 8\n2 - 1\na = 18','2 - 1'] 
 ,['a = 10\na = 8\nb = 4\nab = 72\nb / 6\nb + 1','b = 4\nb / 6\nb + 1'] 
 ,['a = 1\na = 2\na + 1','a = 2\na + 1'] 
 ,['FooBar1 = 0\nFuz__ = 8\nFuz__ / 1','Fuz__ = 8\nFuz__ / 1'] 
 ,['a = 1\na + 1\na = 2\na + 1','a = 1\na + 1\na = 2\na + 1']
 ,['a = 1\na + a\na = 2', 'a = 1\na + a']
 ,['a = 1\n1 / 5 * 8 + 4', '1 / 5 * 8 + 4']
 ,['a = 1\na + a\na = 2', 'a = 1\na + a']
 ,['a = 1\na + 1\na = 1\na + 1', 'a = 1\na + 1\na = 1\na + 1']
 ,['a = 7\n5 / a', 'a = 7\n5 / a']
]
.forEach(([i,k])=>(r=f(i),
  out('Test '+(r==k?'OK':'Fail')+'\nInput:  '+i.replace(/\n/g,',')
      +'\nResult: '+r.replace(/\n/g,',')
      +'\nCheck:  '+k.replace(/\n/g,',')+'\n')
));
Note: newlines trasformed to commas to save space in output
<pre id=O></pre>

edc65
fuente
¡Oye, eso no es 164 bytes!
Cyphase
@ Línea cifrada 1:20 + 1 nueva línea, línea 2; 92 + 1 nueva línea, línea 3:48 + 1 nueva línea, línea 4: 1. 21 + 93 + 49 + 1 => 164. La ungolfedparte es solo para explicación. La TESTparte es ... uhm solo adivina ...
edc65
Lo sé. Solo estaba bromeando. Lo siento :).
Cyphase
1

JavaScript ES6, 79 75 118 bytes

s=>s.split`
`.filter((l,i,a)=>(q=l.split`=`)[1]?!~(a.slice(i+1)+0).search(q[0]+'=')&&~s.search(q[0]+'[^=]'):1).join`
`

Dime si esto no funciona para un caso. Cualquier idea para jugar al golf es bienvenida.


Explicación

s=>          // Function with argument "s"
  s.split`   // Splits each line
  `
  .filter(   // Filters through each line,
    (item,index,array)=>
      (q=l.split`=`)[1]? // If there is something after the equal sign
        !~ // XOR (~) will  essentially make -1, 0. NOT (!) will make 0, 1, vice-versa
         (a.slice(i+1)+0) // Gets following lines
         .search`^${z=q[0]}=` // Checks if following lines have the same variable name and then =
        && // AND...
         ~s.search(z+'[^=]') // Check if there is an expression with the variable
        :1) // If there is no equal sign, return 1 (true)
  .join` // Join everything back, seperated by newlines
  `

Probado en Safari Nightly. Versión amigable de Firefox:

s=>s.split`
`.filter((l,i,a)=>(q=l.split`=`)[1]?!~(a.slice(i+1)+0).search(`^${z=q[0]}=`)&&~s.search(z+'[^=]'):1).join`
`

Puede ingresar en babeljs para obtener una versión ES5.

Downgoat
fuente
@ Blackhole Tengo eso arreglado.
Downgoat
@ edc65 según los ejemplos, sin embargo, el separador es una nueva línea. La entrada también está en un formato estricto con espacios, etc.
Downgoat
@ edc65 ¿Estás seguro? Intente ajustar la función entre paréntesis y ejecútela de esa manera. A mí me funciona (Safari Nightly).
Downgoat
Tal vez soy demasiado obstinado, pero todavía siento que es demasiado simple para funcionar bien en todos los casos. Lo hice correr sin errores en Firefox agregando corchetes en la llamada de búsqueda (aún más corta que la mía). Y probé "a = 1 \ na + a \ na = 2". Y falla ...
edc65
Gracias por agregar mi sugerencia a tu respuesta. -1 porque todavía tiene
errores
1

Haskell, 187 bytes

Uso d.

import Data.List
f=filter
(!)=map
b((v,e,w):t)=e||or((\(_,p,_)->p)!take 1(f(\(x,_,y)->v==x||v`elem`y)t))
d=unlines.(\l->snd!f fst(zip(b!tails(((\(v:o:w)->(v,o/="=",w)).words)!l))l)).lines
Leif Willerts
fuente