Eliminação de código morto

20

O código morto fica lá, sem fazer nada, olhando para nós sabendo que nunca será executado ... mas hoje podemos nos vingar.

Especificação

A entrada será uma sequência multilinha.

Cada linha pode ser uma atribuição ou uma expressão .

Tarefa

Uma atribuição é da forma em <name> = numberque nome é uma sequência de letras, sublinhados e números, mas não começa com um número.

As variáveis ​​podem ser atribuídas qualquer número de vezes.

Expressão

Uma expressão é da forma <var_name OR number> <operation> <var_name OR number> ...

Uma expressão pode ser qualquer combinação de:

  • Variáveis ​​já definidas
  • Operadores aritméticos básicos +-*/
  • Números (inteiros)

Saída esperada

Você deve saída a corda com redundantes atribuições , tarefas que nunca são usadas por qualquer das expressões que se lhe segue, removidos. Observe que as atribuições também podem ser redundantes se uma atribuição adicional para a mesma variável for realizada antes de uma expressão usando a variável seja executada.

Casos de teste

em

a = 10
a * 3

Fora

a = 10
a * 3

em

foo = 8
2 - 1
a = 18

Fora

2 - 1

em

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

Fora

b = 4
b / 6
b + 1

em

a = 1
a = 2
a + 1

Fora

a = 2
a + 1

em

FooBar1 = 0
Fuz__ = 8
Fuz__ / 1

Fora

Fuz__ = 8
Fuz__ / 1

em

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

Fora

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

em

a = 1
1 / 5 * 8 + 4

Fora

1 / 5 * 8 + 4

em

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

Fora

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

em

a = 7
5 / a

Fora

a = 7
5 / a
Caridorc
fonte
1
Você deve adicionar este caso particularmente difícil a = 1; a + 1; a = 1; a + 1;:? Onde o segundo a = 1pode ser descartado apenas porque afoi definido anteriormente com o mesmo valor ( 1).
usar o seguinte comando
3
@flodel Não, não há necessidade de olhar para os valores
Caridorc
@flodel testcase incorporada
Caridorc
Você deve adicionar um caso de teste em que uma variável é usada em uma expressão, mas não como o primeiro elemento da expressão. Especialmente importante é como o último membro da expressão.
Isaacg
@isaacg nenhum código morto, o var pode ser em qualquer lugar, testcase acrescentou
Caridorc

Respostas:

9

PHP - 197 bytes

A função funciona analisando cada linha, na ordem inversa e uma após a outra, e mantendo uma matriz das variáveis ​​usadas.

  • Se houver um caractere igual =na linha, é uma atribuição.
    • A variável é usada, a atribuição é útil e a linha é impressa, mas a variável não é mais considerada usada.
    • Caso contrário, não faça nada.
  • Caso contrário, a linha é uma expressão. Dividimos a linha após cada espaço e adicionamos cada símbolo à lista de variáveis ​​usadas. Números ( 1, 2...) e operadores ( +, -...) será adicionado também, mas desde que eles não são nomes de variáveis válidos, não é um problema. É claro que a linha é impressa.
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;}

Aqui está a versão não destruída:

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;
}
Blackhole
fonte
7

Retina , 45 bytes

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

Para fins de contagem, cada linha entra em um arquivo separado (onde <empty>está um arquivo vazio) e\n deve ser substituída por um feed de linha real (0x0A).

Isso pressupõe que a string sempre terminará com um avanço de linha.

Como esse regex não usa nenhum recurso específico do .NET, você pode testá-lo no regex101 .

A idéia é bastante simples: remova todas as atribuições das quais podemos encontrar (pesquisando adiante) outra atribuição para a mesma variável ou o final da string sem passar por outro uso da variável.

Martin Ender
fonte
6

Pitão, 40 bytes

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

Parece meio longo. Talvez eu possa salvar um ou dois bytes amanhã.

Experimente on-line: Demonstration or Test Suite

Explicação:

_.__.zfornece todos os pós-correções das linhas de entrada em ordem inversa. Por exemplo, a entrada FooBar1 = 0; Fuz__ = 8; Fuz__ / 1fornece a lista:

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

Depois, filtro os elementos da lista T, nos quais =não existe o último elemento de T(expressão) ou (atribuição), o último elemento T, que contém o nome da variável, é uma expressão. Depois imprima o último elemento de cada um dos elementos restantes em uma linha 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
fonte
8
Aww é tão fofo ....__.
orlp
Este código falha no pyth.herokuapp.com/…
isaacg 30/08/2015
@isaacg Fixed.
Jakube 30/08/2015
4

CJam, 49 bytes

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

Experimente online

A abordagem aqui é que uma lista de variáveis ​​não atribuídas é mantida durante o processamento das linhas de entrada de trás para frente:

  • Se a linha for uma expressão, todas as variáveis ​​na expressão serão adicionadas à lista. Na verdade, na implementação, todos os tokens são adicionados à lista, pois ele salva código e ter números e operadores na lista não causa nenhum dano.

  • Se a linha for uma atribuição, ele testa se o nome da variável atribuída está na lista. Se for, a atribuição é aceita e o nome da variável removido da lista. Caso contrário, a atribuição é ignorada.

Explicação:

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

O recuo é: 1. Espaço 2. Guia

Guardado 3 bytes graças a @Kamehameha!

kirbyfan64sos
fonte
O espaço após a impressão dentro print ''.joine indentro in [npode ser removido.
Kamehameha
Além disso, você pode usar esse truque usando um em tabvez do espaço duplo após a exceptlinha e salvando um 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,"")

Onde

  • L é uma linha da entrada (a partir da última)
  • W são os símbolos (variáveis, operadores, números) em uma linha
  • Ré um vetor de símbolos que serão impressos. Inclui variáveis ​​cuja atribuição é necessária.
  • Q é o vetor de linhas na saída
modelo
fonte
Você pode substituir scan(what="",sep="\n")por scan(,"",sep="\n"). Você também pode substituir o separgumento nomeado por seu equivalente posicional, mas não me lembro onde as vírgulas iriam para isso.
Alex A.
... economizando 6. Muito bom. Obrigado Alex!
flodel
2

JavaScript (ES6) 164 177

Usando cadeias de modelo, todas as novas linhas são significativas e contadas.

Teste a execução do snippet no FireFox (necessário para compatibilidade com o ES6, incluindo funções de seta)

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
fonte
Ei, isso não é 164 bytes!
Cyphase
@ Linha de fase 1:20 + 1 nova linha, linha 2; 92 + 1 nova linha, linha 3:48 + 1 nova linha, linha 4: 1. 21 + 93 + 49 + 1 => 164. A ungolfedparte é apenas para explicação. A TESTparte é ... uhm apenas adivinhar ...
edc65
Eu sei. Eu estava apenas brincando. Desculpe :).
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`
`

Diga-me se isso não funcionar em um caso. Todas as idéias para o golfe são bem-vindas.


Explicação

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
  `

Testado no Safari Nightly. Versão amigável do 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`
`

Você pode entrar no babeljs para obter uma versão do ES5.

Downgoat
fonte
@ Blackhole Eu tenho isso corrigido.
Downgoat 29/08/2015
@ edc65 de acordo com exemplos, o separador é uma nova linha. Entrada também está em um formato rigoroso com espaços, etc.
Downgoat
@ edc65 Você tem certeza? Tente colocar a função entre parênteses e execute-a assim. Funciona para mim (Safari Nightly).
Downgoat 29/08/2015
Talvez eu seja muito obstinado, mas ainda sinto que é muito simples funcionar bem em todos os casos. Eu o fiz rodar sem erros no Firefox adicionando colchetes na chamada de pesquisa (ainda muito menor que a minha). E tentei "a = 1 \ na + a \ na = 2". E ele falhar ...
edc65
Obrigado por adicionar minha sugestão à sua resposta. -1, porque ele ainda está sob escuta
edc65
1

Haskell, 187 bytes

Use 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
fonte