Élimination du code mort

20

Le code mort est là, ne faisant rien, nous fixant sachant qu'il ne sera jamais exécuté ... mais aujourd'hui, nous pouvons nous venger.

spécification

L'entrée sera une chaîne multiligne.

Chaque ligne peut être soit une affectation, soit une expression .

Affectation

Une affectation prend la forme <name> = numberoù nom est une séquence de lettres, de soulignements et de chiffres, mais ne commençant pas par un nombre.

Les variables peuvent être affectées un certain nombre de fois.

Expression

Une expression est de la forme <var_name OR number> <operation> <var_name OR number> ...

Une expression peut être n'importe quelle combinaison de:

  • Variables déjà définies
  • Opérateurs arithmétiques de base +-*/
  • Nombres (entiers)

Production attendue

Vous devez sortir la chaîne avec des affectations redondantes , affectations qui ne sont jamais utilisées par aucune des expressions qui la suivent, supprimées. Veuillez noter que les affectations peuvent également être redondantes si une affectation supplémentaire à la même variable est effectuée avant l'exécution d'une expression utilisant la variable.

Cas de test

dans

a = 10
a * 3

en dehors

a = 10
a * 3

dans

foo = 8
2 - 1
a = 18

en dehors

2 - 1

dans

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

en dehors

b = 4
b / 6
b + 1

dans

a = 1
a = 2
a + 1

en dehors

a = 2
a + 1

dans

FooBar1 = 0
Fuz__ = 8
Fuz__ / 1

en dehors

Fuz__ = 8
Fuz__ / 1

dans

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

en dehors

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

dans

a = 1
1 / 5 * 8 + 4

en dehors

1 / 5 * 8 + 4

dans

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

en dehors

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

dans

a = 7
5 / a

en dehors

a = 7
5 / a
Caridorc
la source
1
Si vous ajoutez ce cas particulièrement difficile:a = 1; a + 1; a = 1; a + 1; ? Où le second a = 1peut être ignoré uniquement parce qu'il aétait précédemment défini sur la même valeur ( 1).
flodel
3
@flodel Non, pas besoin de regarder les valeurs
Caridorc
@flodel testcase incorporé
Caridorc
Vous devez ajouter un cas de test dans lequel une variable est utilisée dans une expression, mais pas en tant que premier élément de l'expression. Particulièrement important est le dernier membre de l'expression.
isaacg
@isaacg pas de code mort, le var peut être n'importe où, testcase ajouté
Caridorc

Réponses:

9

PHP - 197 octets

La fonction fonctionne en analysant chaque ligne, dans l'ordre inverse et l'une après l'autre, et en maintenant un tableau des variables utilisées.

  • S'il y a un caractère égal =dans la ligne, c'est une affectation.
    • Si la variable est utilisée, l'affectation est utile et la ligne est imprimée, mais la variable n'est plus considérée comme utilisée.
    • Sinon, ne faites rien.
  • Sinon, la ligne est une expression. Nous séparons la ligne après chaque espace et ajoutons chaque symbole à la liste des variables utilisées. Nombres (1 , 2...) et les opérateurs ( +, -, ...) seront ajoutés aussi, mais comme ils ne sont pas des noms de variables valides, ce n'est pas un problème. La ligne est alors bien sûr imprimée.
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;}

Voici la version non golfée:

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;
}
Trou noir
la source
7

Rétine , 45 octets

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

À des fins de comptage, chaque ligne va dans un fichier distinct (où <empty> est un fichier vide) et \ndoit être remplacée par un saut de ligne réel (0x0A).

Cela suppose que la chaîne se terminera toujours par un saut de ligne.

Comme cette expression régulière n'utilise aucune fonctionnalité spécifique à .NET, vous pouvez la tester sur regex101 .

L'idée est assez simple: supprimez toutes les affectations à partir desquelles nous pouvons trouver (recherche en avant) une autre affectation à la même variable ou à la fin de la chaîne sans passer par une autre utilisation de la variable.

Martin Ender
la source
6

Pyth, 40 octets

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

Semble un peu long. Peut-être que je peux économiser un ou deux octets demain.

Essayez-le en ligne: démonstration ou suite de tests

Explication:

_.__.zdonne tous les suffixes des lignes d'entrée dans l'ordre inverse. Par exemple, l'entrée FooBar1 = 0; Fuz__ = 8; Fuz__ / 1donne la liste:

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

Ensuite, je filtre les éléments de liste T, dans lesquels il =ne se trouve pas dans le dernier élément de T(expression) ou (affectation), le dernier élément dans T, qui contient le nom de la variable, est une expression. Ensuite, imprimez le dernier élément de chacun des éléments restants sur une ligne distincte.

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
la source
8
Aww c'est trop mignon ....__.
orlp
Ce code échoue sur pyth.herokuapp.com/…
isaacg
@isaacg Fixed.
Jakube
4

CJam, 49 octets

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

Essayez-le en ligne

L'approche ici est qu'une liste de variables non affectées est conservée lors du traitement des lignes d'entrée de l'arrière vers l'avant:

  • Si la ligne est une expression, toutes les variables de l'expression sont ajoutées à la liste. En fait, dans l'implémentation, tous les jetons sont ajoutés à la liste, car il enregistre le code et le fait d'avoir des numéros et des opérateurs dans la liste ne fait aucun mal.

  • Si la ligne est une affectation, elle teste si le nom de variable affecté est dans la liste. Si tel est le cas, l'affectation est acceptée et le nom de variable supprimé de la liste. Sinon, l'affectation est ignorée.

Explication:

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
la source
4

Python 2, 270 267 octets

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

L'indentation est: 1. Espace 2. Onglet

Sauvegardé 3 octets grâce à @Kamehameha!

kirbyfan64sos
la source
L'espace après l'impression dans print ''.joinet inen in [npeut être supprimé.
Kamehameha
En outre, vous pouvez utiliser cette astuce en utilisant un tabau lieu du double espace après la exceptligne et en enregistrant un octet.
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,"")

  • L est une ligne de l'entrée (à partir de la dernière)
  • W sont les symboles (variables, opérateurs, nombres) sur une ligne
  • Rest un vecteur de symboles qui sera imprimé. Il comprend des variables dont l'affectation est nécessaire.
  • Q est le vecteur de lignes dans la sortie
flodel
la source
Vous pouvez remplacer scan(what="",sep="\n")par scan(,"",sep="\n"). Vous pouvez également être en mesure de remplacer l' separgument nommé par son équivalent positionnel, mais je ne me souviens pas où iraient les virgules pour cela.
Alex A.
... économie 6. Très agréable. Merci Alex!
flodel
2

JavaScript (ES6) 164 177

À l'aide de chaînes de modèle, toutes les nouvelles lignes sont importantes et comptées.

Test d'exécution de l'extrait dans FireFox (requis pour la compatibilité ES6, y compris les fonctions fléchées)

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
la source
Hé, ce n'est pas 164 octets!
Cyphase
@Cyphase ligne 1:20 + 1 nouvelle ligne, ligne 2; 92 + 1 nouvelle ligne, ligne 3:48 + 1 nouvelle ligne, ligne 4: 1. 21 + 93 + 49 + 1 => 164. La ungolfedpartie est à titre explicatif seulement. La TESTpartie est ... euh juste devinez ...
edc65
Je sais. Je plaisantais. Pardon :).
Cyphase
1

JavaScript ES6, 79 75 118 octets

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`
`

Dites-moi si cela ne fonctionne pas pour un cas. Toutes les idées de golf sont les bienvenues.


Explication

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
  `

Testé sur Safari Nightly. Version compatible avec 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`
`

Vous pouvez passer dans babeljs pour obtenir une version ES5.

Downgoat
la source
@Blackhole J'ai corrigé ce problème.
Downgoat
@ edc65 selon les exemples, le séparateur est cependant une nouvelle ligne. L'entrée est également dans un format strict avec des espaces, etc.
Downgoat
@ edc65 Êtes-vous sûr? Essayez de placer la fonction entre parenthèses et exécutez-la comme ça. Cela fonctionne pour moi (Safari Nightly).
Downgoat
Je suis peut-être trop obstiné mais je pense toujours que c'est trop simple de bien travailler dans tous les cas. Je l'ai fait fonctionner sans erreurs dans Firefox en ajoutant des crochets dans l'appel de recherche (toujours beaucoup plus court que le mien). Et essayé "a = 1 \ na + a \ na = 2". Et ça échoue ...
edc65
Merci d'avoir ajouté ma suggestion à votre réponse. -1 car il est toujours buggé
edc65
1

Haskell, 187 octets

Utilisez 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
la source