C, 59 octets
i;f(char*s){while(*s&3?*s&9||(i+=i+*s%5):putchar(i),*s++);}
Des nombres magiques, des nombres magiques partout!
(De plus, C plus court que Python, JS, PHP et Ruby? Du jamais vu!)
Il s'agit d'une fonction qui prend une chaîne en entrée et sort vers STDOUT.
Procédure pas à pas
La structure de base est:
i; // initialize an integer i to 0
f(char*s){
while(...); // run the stuff inside until it becomes 0
}
Ici, le "stuff inside" est un tas de code suivi de ,*s++
, où l'opérateur virgule ne renvoie que la valeur de son deuxième argument. Par conséquent, cela parcourra la chaîne et sera défini *s
sur chaque caractère, y compris l'octet NUL de fin (puisque postfix ++
renvoie la valeur précédente), avant de quitter.
Jetons un coup d'œil au reste:
*s&3?*s&9||(i+=i+*s%5):putchar(i)
Décoller le ternaire et les courts-circuits ||
, cela peut être étendu à
if (*s & 3) {
if (!(*s & 9)) {
i += i + *s % 5;
}
} else {
putchar(i);
}
D'où viennent ces nombres magiques? Voici les représentations binaires de tous les personnages impliqués:
F 70 01000110
B 66 01000010
i 105 01101001
z 122 01111010
u 117 01110101
32 00100000
\0 0 00000000
Tout d'abord, nous devons séparer l'espace et NUL du reste des caractères. La façon dont cet algorithme fonctionne, il conserve un accumulateur du nombre "courant", et l'imprime chaque fois qu'il atteint un espace ou la fin de la chaîne (ie '\0'
). En remarquant cela ' '
et '\0'
étant les seuls caractères à n'avoir aucun des deux bits les moins significatifs définis, nous pouvons bit par bit ET le caractère avec 0b11
pour obtenir zéro si le caractère est un espace ou NUL et non nul sinon.
En creusant plus profondément, dans la première branche "if", nous avons maintenant un personnage qui en fait partie FBizu
. J'ai choisi uniquement de mettre à jour l'accumulateur sur F
s et B
s, donc j'avais besoin d'un moyen de filtrer les izu
s. Idéalement, F
et les B
deux n'ont que le deuxième, le troisième ou le septième ensemble de bits de poids faible, et tous les autres nombres ont au moins un autre ensemble de bits. En fait, ils ont tous le premier ou le quatrième bit le moins significatif. Par conséquent, nous pouvons binaire ET avec 0b00001001
, qui est 9, ce qui donnera 0 pour F
et B
et non nul sinon.
Une fois que nous avons déterminé que nous avons un F
ou B
, nous pouvons les mapper à 0
et 1
respectivement en prenant leur module 5, parce que F
est 70
et B
est 66
. Puis l'extrait
i += i + *s % 5;
est juste une façon golfique de dire
i = (i * 2) + (*s % 5);
qui peut également être exprimé comme
i = (i << 1) | (*s % 5);
qui insère le nouveau bit à la position la moins significative et décale tout le reste sur 1.
"Mais attendez!" vous pourriez protester. "Après avoir imprimé i
, quand est-il jamais remis à zéro?" Eh bien, putchar
convertit son argument en un unsigned char
, qui se trouve être 8 bits. Cela signifie que tout ce qui dépasse le 8e bit le moins significatif (c'est-à-dire la jonque des itérations précédentes) est jeté, et nous n'avons pas à nous en soucier.
Merci à @ETHproductions d' avoir proposé de remplacer 57
par 9
, d'économiser un octet!