57 votes

Code Golf : Morse

Le défi

Le code le plus court par nombre de caractères, qui saisit une chaîne de caractères en utilisant uniquement des caractères alphabétiques (majuscules et minuscules), des chiffres, des virgules, des points et un point d'interrogation, et renvoie une représentation de la chaîne en code Morse. La sortie en code Morse doit être constituée d'un tiret ( - ASCII 0x2D) pour un long bip (AKA 'dah') et un point ( . (ASCII 0x2E) pour un bip court (AKA 'dit').

Chaque lettre doit être séparée par un espace ( ' ' ASCII 0x20), et chaque mot doit être séparé par une barre oblique ( / , ASCII 0x2F).

Table de code morse :

alt text

Cas de test :

Input:
    Hello world

Output:
    .... . .-.. .-.. --- / .-- --- .-. .-.. -..

Input:
    Hello, Stackoverflow.

Output:
    .... . .-.. .-.. --- --..-- / ... - .- -.-. -.- --- ...- . .-. ..-. .-.. --- .-- .-.-.-

Le nombre de codes comprend les entrées/sorties (c'est-à-dire le programme complet).

70voto

P Daddy Points 14228

C (131 caractères)

Oui, 13 1 !

main(c){for(;c=c?c:(c=toupper(getch())-32)?
"•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"
[c-12]-34:-3;c/=2)putch(c/2?46-c%2:0);}

J'ai réussi à obtenir quelques caractères supplémentaires en combinant la logique de l'option while et for en une seule for et en déplaçant la déclaration de la boucle c dans la variable main comme paramètre d'entrée. Cette dernière technique, je l'ai empruntée à la réponse de strager à un autre défi .


Pour ceux qui essaient de vérifier le programme avec GCC ou avec des éditeurs uniquement en ASCII, vous aurez peut-être besoin de la version suivante, légèrement plus longue :

main(c){for(;c=c?c:(c=toupper(getchar())-32)?c<0?1:
"\x95#\x8CKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"
[c-12]-34:-3;c/=2)putchar(c/2?46-c%2:32);}

Cette version comporte 17 caractères de plus (pour un poids total de 148 caractères), en raison des modifications suivantes :

  • +4 : getchar() et putchar() au lieu de la version non portable getch() et putch()
  • +6 : codes d'échappement pour deux des caractères au lieu de caractères non-ASCII
  • +1 : 32 au lieu de 0 pour le caractère espace
  • +6 : ajouté " c<0?1: "pour supprimer les déchets provenant de caractères inférieurs à ASCII 32 (à savoir, de '\n' ). Vous obtiendrez toujours des déchets de n'importe quelle !"#$%&'()*+[\]^_ ` {|}~ ou tout ce qui est supérieur à ASCII 126.

Cela devrait rendre le code complètement portable. Compilez avec :

gcc -std=c89 -funsigned-char morse.c

Le site -std=c89 est facultatif. Le site -funsigned-char est nécessaire, cependant, ou vous obtiendrez des déchets pour la virgule et le point.


135 caractères

c;main(){while(c=toupper(getch()))for(c=c-32?
"•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"
[c-44]-34:-3;c;c/=2)putch(c/2?46-c%2:0);}

À mon avis, cette dernière version est également beaucoup plus attrayante sur le plan visuel. Et non, elle n'est pas portable, et elle n'est plus protégée contre les entrées hors limites. Il a également une interface utilisateur assez mauvaise, prenant l'entrée caractère par caractère et la convertissant en code Morse et ayant pas de condition de sortie (vous devez frapper Ctrl + Break ). Mais un code portable et robuste avec une interface utilisateur agréable n'était pas une exigence.

Voici une explication aussi brève que possible de ce code :

main(c){
    while(c = toupper(getch())) /* well, *sort of* an exit condition */
        for(c =
            c - 32 ? // effectively: "if not space character"
            "•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"[c - 44] - 34
            /* This array contains a binary representation of the Morse Code
             * for all characters between comma (ASCII 44) and capital Z.
             * The values are offset by 34 to make them all representable
             * without escape codes (as long as chars > 127 are allowed).
             * See explanation after code for encoding format.
             */
            : -3; /* if input char is space, c = -3
                   * this is chosen because -3 % 2 = -1 (and 46 - -1 = 47)
                   * and -3 / 2 / 2 = 0 (with integer truncation)
                   */
            c; /* continue loop while c != 0 */
            c /= 2) /* shift down to the next bit */
                putch(c / 2 ? /* this will be 0 if we're down to our guard bit */
                    46 - c % 2 /* We'll end up with 45 (-), 46 (.), or 47 (/).
                                * It's very convenient that the three characters
                                * we need for this exercise are all consecutive.
                                */
                    : 0 /* we're at the guard bit, output blank space */
                );
}

Chaque caractère de la longue chaîne du code contient le code Morse codé d'un caractère de texte. Chaque bit du caractère codé représente soit un tiret, soit un point. Un 1 représente un tiret, et un 0 un point. Le bit le moins significatif représente le premier tiret ou point du code Morse. Un dernier bit de "garde" détermine la longueur du code. En d'autres termes, le bit le plus élevé de chaque caractère codé représente la fin du code et n'est pas imprimé. Sans ce bit de garde, les caractères avec des points de queue ne pouvaient pas être imprimés correctement.

Par exemple, la lettre 'L' est " .-.. "en code Morse. Pour le représenter en binaire, il faut un 0, un 1 et deux autres 0, en commençant par le bit le moins significatif : 0010. Ajoutez un autre 1 pour le bit de garde, et vous obtenez notre code Morse codé : 10010, ou 18 en décimal. Ajoutez le décalage +34 pour obtenir 52, qui est la valeur ASCII du caractère '4'. Le tableau de caractères codés contient donc un '4' comme 33ème caractère (index 32).

Cette technique est similaire à celle qui est utilisée pour coder les caractères en ACoolie's , de strager (2) , Miles , de pingw33n , Alec's et Andrea's mais elle est légèrement plus simple, car elle ne nécessite qu'une seule opération par bit (décalage/division), au lieu de deux (décalage/division et décrémentation).

EDIT :
En lisant le reste des implémentations, je vois que Alec et Anon a trouvé ce schéma d'encodage - utilisant le bit de garde - avant moi. La solution d'Anon est particulièrement intéressante, car elle utilise l'algorithme Python bin et en supprimant la fonction "0b" et le bit de garde avec [3:] plutôt que de tourner en boucle et de changer de place, comme Alec et moi l'avons fait.

En prime, cette version gère également le trait d'union ( -....- ), la barre oblique ( -..-. ), deux-points ( ---... ), point-virgule ( -.-.-. ), égale ( -...- ), et au signe ( .--.-. ). Tant que les caractères 8 bits sont autorisés, leur prise en charge ne nécessite aucun octet de code supplémentaire. Il n'est pas possible de prendre en charge plus de caractères avec cette version sans ajouter de la longueur au code (à moins qu'il n'existe des codes morse pour les signes plus grand/moins grand que).

Parce que je trouve que les anciennes implémentations sont toujours intéressantes, et que le texte comporte quelques avertissements applicables à cette version, j'ai laissé le contenu précédent de ce billet ci-dessous.


Ok, on peut supposer que l'interface utilisateur peut craindre, non ? Donc, en empruntant à strager j'ai remplacé gets() qui fournit une entrée de ligne tamponnée, avec écho, avec getch() qui permet de saisir des caractères sans tampon et sans écho. Cela signifie que chaque caractère que vous tapez est immédiatement traduit en code morse sur l'écran. C'est peut-être cool. Il ne fonctionne plus avec stdin ou un argument de ligne de commande, mais il est sacrément petit.

J'ai gardé l'ancien code ci-dessous, cependant, pour référence. Voici le nouveau.

Nouveau code, avec vérification des limites, 171 caractères :

W(i){i?W(--i/2),putch(46-i%2):0;}c;main(){while(c=toupper(getch())-13)
c=c-19?c>77|c<31?0:W("œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"
[c-31]-42):putch(47),putch(0);}

Enter interrompt la boucle et quitte le programme.

Nouveau code, sans vérification des limites, 159 caractères :

W(i){i?W(--i/2),putch(46-i%2):0;}c;main(){while(c=toupper(getch())-13)
c=c-19?W("œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"[c-31]-42):
putch(47),putch(0);}

Voici l'ancien code 196/177, avec quelques explications :

W(i){i?W(--i/2),putch(46-i%2):0;}main(){char*p,c,s[99];gets(s);
for(p=s;*p;)c=*p++,c=toupper(c),c=c-32?c>90|c<44?0:W(
"œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"[c-44]-42):
putch(47),putch(0);}

Ceci est basé sur La réponse d'Andrea en Python en utilisant la même technique pour générer le code morse que dans cette réponse. Mais au lieu de stocker les caractères codables l'un après l'autre et de trouver leurs index, j'ai stocké les index l'un après l'autre et je les ai cherchés par caractère (de manière similaire à ma réponse précédente ). Cela permet d'éviter les longues lacunes vers la fin qui ont posé des problèmes aux premiers implémenteurs.

Comme avant J'ai utilisé un caractère supérieur à 127. La conversion en ASCII uniquement ajoute 3 caractères. Le premier caractère de la longue chaîne doit être remplacé par \x9C . Le décalage est nécessaire cette fois, sinon un grand nombre de caractères sont en dessous de 32, et doit être représentés par des codes d'échappement.

Comme précédemment, le traitement d'un argument de ligne de commande au lieu de stdin ajoute 2 caractères, et l'utilisation d'un véritable caractère d'espace entre les codes ajoute 1 caractère.

D'un autre côté, certaines des autres routines ici ne traitent pas les entrées en dehors de la plage acceptée de [ ,.0-9\?A-Za-z]. Si ce traitement était supprimé de cette routine, 19 caractères pourraient être supprimés, ce qui ramènerait le total à 177 caractères. Mais si cela est fait, et que des données non valides sont introduites dans ce programme, celui-ci risque de se planter et de brûler.

Le code dans ce cas pourrait être :

W(i){i?W(--i/2),putch(46-i%2):0;}main(){char*p,s[99];gets(s);
for(p=s;*p;p++)*p=*p-32?W(
"œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"
[toupper(*p)-44]-42):putch(47),putch(0);}

48voto

johnc Points 12140

Utilisation d'un Morse Code Font ?

Console.Write(params[0]);

23voto

hobbs Points 71946

Perl, 170 caractères (avec un peu d'aide du golfeur accompli mauke ). Mis en forme pour plus de clarté ; tous les retours à la ligne sont supprimés.

$_=uc<>;y,. ,|/,;s/./$& /g;@m{A..Z,0..9,qw(| , ?)}=
".-NINNN..]IN-NII..AMN-AI---.M-ANMAA.I.-].AIAA-NANMMIOMAOUMSMSAH.B.MSOIONARZMIZ"
=~/../g;1while s![]\w|,?]!$m{$&}!;print

Explication :

  1. Extraire le dictionnaire morse. Chaque symbole est défini en termes de deux caractères, qui peuvent être soit des points ou des tirets littéraux, soit une référence à la valeur d'un autre caractère défini. E et T contiennent des caractères fictifs pour éviter de désynchroniser le décodeur ; nous les supprimerons plus tard.
  2. Lire et formater l'entrée. "Hello world" devient "H E L L O / W O R L D"
  3. L'étape suivante dépend du fait que les dictionnaires d'entrée et de sortie soient distincts, donc transformez les points dans l'entrée en un caractère inutilisé (barre verticale), | )
  4. Remplace tout caractère de l'entrée qui se trouve dans le dictionnaire morse par sa valeur dans le dictionnaire, jusqu'à ce qu'aucun remplacement ne se produise.
  5. Retirez la carte fictive mentionnée à l'étape 1.
  6. Imprimez la sortie.

Dans la version finale, le dictionnaire est optimisé pour une meilleure efficacité d'exécution :

  • Tous les caractères à un symbole (E et T) et à deux symboles (A, I, M et N) sont définis directement et décodés en un seul passage.
  • Tous les caractères à trois symboles sont définis en fonction d'un caractère à deux symboles et d'un symbole littéral, le décodage se faisant en deux passes.
  • Tous les caractères à quatre symboles sont définis en fonction de deux caractères à deux symboles, le décodage se faisant en deux passes avec trois remplacements.
  • Les caractères à cinq et six symboles (chiffres et ponctuation) sont décodés en trois passages, avec respectivement quatre ou cinq remplacements.

Comme le code golfé ne remplace qu'un caractère par boucle (pour économiser un caractère de code !), le nombre de boucles est limité à cinq fois la longueur de l'entrée (trois fois la longueur de l'entrée si l'on utilise uniquement des caractères alphabétiques). Mais en ajoutant un g à la s/// le nombre de boucles est limité à trois (deux si l'on utilise uniquement l'alphabet).

Exemple de transformation :

Hello 123
H E L L O / 1 2 3
II .] AI AI M- / AO UM SM
.... . .-.. .-.. --- / .-M- .A-- I.--
.... . .-.. .-.. --- / .---- ..--- ...--

16voto

ACoolie Points 1034

Compréhension d'une liste en Python, une phrase de 159 caractères

for c in raw_input().upper():print c<","and"/"or bin(ord("•ƒwTaQIECBRZ^`šŒ#S#n|':<.$402&9/6)(18?,*%+3-;=>"[ord(c)-44])-34)[3:].translate(" "*47+"/.-"+" "*206),

Utilise l'emballage de données similaires pour Mise en œuvre de la C de Papa mais ne stocke pas les bits en ordre inverse et utilise bin() pour extraire les données plutôt que l'arithmétique. Notez également que les espaces sont détectés à l'aide de l'inégalité ; elle considère que tout caractère "inférieur à la virgule" est un espace.

Python for boucle, 205 caractères, y compris les nouvelles lignes

for a in raw_input().upper():
 q='_ETIANMSURWDKGOHVF_L_PJBXCYZQ__54_3___2__+____16=/_____7___8_90'.find(a);s=''
 while q>0:s='-.'[q%2]+s;q=~-q/2
 print['/','--..--','..--..','.-.-.-',''][' ,?.'.find(a)]+s,

7voto

dmckee Points 50318

J'ai travaillé sur un codage compact pour les symboles, mais je ne vois pas comment faire mieux que les arbres implicites déjà utilisés, donc je présente le codage ici au cas où quelqu'un d'autre pourrait l'utiliser.

Considérez la chaîne :

 --..--..-.-.-..--...----.....-----.--/

qui contient toutes les séquences nécessaires sous forme de sous-chaînes. Nous pourrait codez les symboles par décalage et longueur comme ceci :

       ET  RRRIIGGGJJJJ    
--..--..-.-.-..--...----.....-----.--/
          CCCC  DD WWW       00000
,,,,,,   AALLLL BBBB        11111
--..--..-.-.-..--...----.....-----.--/
  ??????  KKK  MMSSS       22222   
        FFFF  PPPP        33333
--..--..-.-.-..--...----.....-----.--/
        UUU XXXX         44444       
          NN  PPPP  OOO 55555
--..--..-.-.-..--...----.....-----.--/
               ZZZZ    66666
                      77777      YYYY
--..--..-.-.-..--...----.....-----.--/
       ......        88888 HHHH
                    99999 VVVV  QQQQ
--..--..-.-.-..--...----.....-----.--/

avec l'espace (c'est-à-dire la limite du mot) qui commence et se termine sur le dernier caractère (le '/'). N'hésitez pas à l'utiliser si vous y voyez un bon moyen.

La plupart des symboles les plus courts ont plusieurs codages possibles, bien sûr.


P Papa a trouvé une version plus courte de cette astuce (et je peux maintenant voir au moins une partie de la redondance ici) et a fait une belle implémentation c. Alec a fait une implémentation en python avec la première version (boguée et incomplète). Hobbs a fait une version perl assez compacte que je ne comprends pas du tout.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X