Morse Decode Golf


24

Sono diventato allarmato dal crescente odio per gli spazi e questa risposta mi ha ispirato ad assicurarmi che il codice Morse sia al sicuro da questa insidiosa rimozione degli spazi bianchi.

Quindi, il tuo compito sarà quello di creare un programma in grado di tradurre con successo il codice Morse con tutti gli spazi rimossi.

codice Morse

Regole:

  1. L'input sarà una stringa composta solo da trattini e punti (ASCII 2D e 2E). L'output non è definito per l'input contenente altri caratteri. Sentiti libero di usare qualsiasi metodo conveniente per la tua lingua preferita per ricevere l'input (stdin, file di testo, prompt user, qualunque cosa). Puoi presumere che l'inserimento del codice Morse sia composto solo dalle lettere AZ e che non siano richiesti numeri o punteggiatura corrispondenti.

  2. L'output dovrebbe includere solo le parole contenute in questo file di dizionario (di nuovo, sentiti libero di usare qualsiasi metodo conveniente per accedere al file di dizionario). Tutte le decodifiche valide devono essere emesse su stdout e tutti i punti e trattini nell'input devono essere usati. Ogni parola corrispondente nell'output deve essere separata da uno spazio e ogni possibile decodifica dovrebbe essere separata da una nuova riga. È possibile utilizzare l'output maiuscolo, minuscolo o misto come più conveniente.

  3. Tutte le restrizioni sulle scappatoie standard si applicano con un'eccezione, come indicato sopra, è possibile accedere al file del dizionario di cui al requisito 2 tramite una connessione Internet se proprio lo si desidera. L'accorciamento degli URL è accettabile, credo che goo.gl/46I35Z sia probabilmente il più breve.

  4. Questo è il codice golf, il codice più corto vince.

Nota: la pubblicazione del file del dizionario su Pastebin ha modificato tutte le terminazioni di riga in sequenze 0A 0E in stile Windows. Il tuo programma può assumere terminazioni di riga solo con 0A, solo 0E o 0A 0E.

Casi test:

Ingresso:

......-...-.. ---. ----- ...-..- ..

L'output deve contenere:

Ciao mondo

Ingresso:

...-. ----- ----- .... ... - .. - ... --- .. - ...-.... ... - ... .. -----... -. ----....-..

L'output deve contenere:

puzzle di programmazione e codice golf

Ingresso:

-..... - ...-...-.. -.. ....-.... ---- --- --- ..-.- --.. --- -. .... --- ...-...-... ......---- --- ... ..-- ---.

L'output deve contenere:

la veloce volpe marrone salta sopra il cane pigro


3
Come puoi distinguere tra AN (.- -.)e EG (. --.)?
Seequ,

2
@Sieg - L'output dovrebbe quindi includere entrambe le decodifiche valide.
Comintern,

1
@Dennis - Ahhh ... Scommetto che sia Pastebin che il mio browser lo hanno fatto. Il mio file sorgente non li ha. È possibile modificare il delimitatore di linea in uno appropriato per il sistema, senza altre modifiche. Modificherò la domanda quando non sono sul mio telefono.
Comintern,

2
@Falko questo è il comportamento corretto. Nota che il problema dice che il tuo output deve includere "ciao mondo", non che sia limitato a quello. Dovrebbe stampare tutte le decodifiche valide.
Hobbs,

2
(quasi poetico, davvero)
hobbs

Risposte:


5

Ruby, 210

(1..(g=gets).size).map{|x|puts IO.read(?d).split.repeated_permutation(x).select{|p|p.join.gsub(/./,Hash[(?a..?z).zip"(;=/%513':07*)29@-+&,4.<>?".bytes.map{|b|('%b'%(b-35))[1,7].tr'01','.-'}])==g}.map{|r|r*' '}}

Se esiste una pratica come il "golf eccessivo", sospetto di aver partecipato questa volta. Questa soluzione genera una matrice di matrici di permutazioni ripetute di tutte le parole del dizionario , dalla lunghezza 1 alla lunghezza dell'input. Dato che "a" è la parola più breve nel file del dizionario e il suo codice è lungo due caratteri, sarebbe stato sufficiente generare permutazioni di lunghezza fino a metà della dimensione dell'input, ma l'aggiunta /2equivale alla verbosità in questo dominio, così mi sono trattenuto.

Una volta che l'array di permutazione è stato generato ( NB : è di lunghezza 45404 104 nel caso dell'input di esempio pangrammatico), ogni array di permutazione viene concatenato e i suoi caratteri alfabetici vengono sostituiti con i loro equivalenti di codice Morse tramite la (Regexp, Hash)variante piuttosto conveniente della #gsubmetodo; abbiamo trovato una decodifica valida se questa stringa è uguale all'input.

Il dizionario viene letto (più volte) da un file denominato "d" e l'input non deve contenere una nuova riga.

Esempio di corsa (con un dizionario che darà al programma una possibilità di combattere per terminare prima della morte per caldo dell'universo):

$ cat d
puzzles
and
code
dummy
golf
programming
$ echo -n .--..-.-----..-..-----..-.--..--...---..--...-.......--.-..-.-.----...--.---.-....-. | ruby morse.rb
programming puzzles and code golf
^C

5

Haskell, 296 personaggi

  • File del dizionario: deve essere un file di testo denominato "d"
  • Input: stdin, può avere una nuova riga finale ma nessuno spazio interno
main=do f<-readFile"d";getLine>>=mapM(putStrLn.unwords).(words f&)
i!""=[i]
(i:j)!(p:q)|i==p=j!q
_!_=[]
_&""=[[]]
d&i=do
w<-d
j<-i!(w>>=((replicate 97"X"++words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..")!!).fromEnum)
n<-d&j
[w:n]

Spiegazione degli elementi:

  • mainlegge il dizionario, legge stdin, esegue &e formatta l'output di &con spazi bianchi appropriati.
  • (replicate 97"X"++words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..")!!)(un'espressione all'interno della definizione di &) è un elenco i cui indici sono codici di caratteri (97 è il codice di 'a') e i valori sono sequenze Morse.
  • !(una funzione denominata operatore infix) corrisponde a una stringa rispetto a un prefisso; se il prefisso è presente, restituisce il resto in un elenco a un elemento (esito positivo nella monade elenco), altrimenti l'elenco vuoto (errore nella monade elenco)
  • &usa la monade lista per l'esecuzione “non deterministica”; esso

    1. seleziona una voce di d(una parola del dizionario),
    2. utilizza !per abbinare la forma Morse di quella parola ( w>>=((…)!!).fromEnum, che è equivalente a concatMap (((…)!!) . fromEnum) w) con la stringa di input i,
    3. si chiama ( d&j) per corrispondere al resto della stringa e
    4. restituisce il possibile risultato come un elenco di parole w:n, nella lista monade [w:n](che è l'equivalente più breve e concreto a return (w:n)).

    Si noti che ogni riga dopo la riga 6 fa parte dodell'espressione avviata sulla riga 6; questo richiede esattamente lo stesso numero di caratteri dell'uso del punto e virgola su una sola riga, ma è più leggibile, anche se puoi farlo solo una volta in un programma.

Questo programma è estremamente lento. Può essere reso più veloce (e leggermente più lungo) facilmente memorizzando le parole bloccate accanto agli originali in un elenco invece di ricalcolarle ad ogni corrispondenza del modello. La prossima cosa da fare sarebbe memorizzare le parole in un albero binario digitato dai simboli Morse (un trie 2-ary ) in modo da evitare di provare rami inutili.

Potrebbe essere leggermente più breve se il file del dizionario non contenesse simboli inutilizzati come "-", consentendo la rimozione replicate 97"X"++in favore di fare .(-97+)prima del !!.


Dannato figlio, è intelligente. +1 a te.
Alexander Craggs,

1
Lo sapevi che (+(-97))può essere riscritto come (-97+)?
orgoglioso haskeller il

Dovresti rimuovere la terza definizione di h e invece aggiungere |0<1=[]alla seconda definizione
orgoglioso haskeller il

2
Usa interacte vinci 12 personaggi. interact$unlines.map unwords.(words f&)
gxtaillon,

1
Dovresti essere in grado di sostituire concatMapcon>>=
orgoglioso haskeller il

3

Python - 363 345

Codice:

D,P='-.';U,N='-.-','.-.'
def s(b,i):
 if i=='':print b
 for w in open('d').read().split():
  C=''.join([dict(zip('abcdefghijklmnopqrstuvwxyz-\'23',[P+D,D+3*P,U+P,'-..',P,D+N,'--.',4*P,2*P,P+3*D,U,N+P,2*D,D+P,D*3,'.--.',D+U,N,P*3,D,'..-',3*P+D,'.--','-..-',U+D,'--..']+['']*4))[c]for c in w]);L=len(C)
  if i[:L]==C:s(b+' '+w,i[L:])
s('',input())

Spiegazione:

Il dizionario deve essere memorizzato come file di testo semplice denominato "d".

D, P, UE Nsono solo alcune variabili di supporto per una definizione più corto del tavolo morse di ricerca.

s(i)è una funzione ricorsiva che stampa la parte del messaggio precedentemente tradotta pe ogni traduzione valida della parte di codice rimanente i: Se iè vuota, abbiamo raggiunto la fine del codice e bcontiene l'intera traduzione, quindi semplicemente print. Altrimenti controlliamo ogni parola wnel dizionario d, la traduciamo in codice morse Ce, se il codice rimanente iinizia con C, aggiungiamo la parola wall'inizio tradotto be chiamiamo la funzione sricorsivamente sul resto.

Nota sull'efficienza:

Questa è una versione piuttosto lenta ma giocosa. Soprattutto caricare il dizionario e costruire la tabella di ricerca morse ( dict(zip(...))) in ogni iterazione (per evitare più variabili) costa molto. E sarebbe più efficiente tradurre tutte le parole nel file del dizionario una volta in anticipo e non in ogni ricorsione su richiesta. Queste idee portano alla seguente versione con 40 caratteri in più ma una notevole accelerazione:

d=open('d').read().split()
D,P='-.';U,N='-.-','.-.'
M=dict(zip('abcdefghijklmnopqrstuvwxyz-\'23',[P+D,D+3*P,U+P,'-..',P,D+N,'--.',4*P,2*P,P+3*D,U,N+P,2*D,D+P,D*3,'.--.',D+U,N,P*3,D,'..-',3*P+D,'.--','-..-',U+D,'--..']+['']*4))
T=[''.join([M[c]for c in w])for w in d]
def s(b,i):
 if i=='':print b
 for j,w in enumerate(d):
  C=T[j];L=len(C)
  if i[:L]==C:s(b+' '+w,i[L:])
s('',input())

Puoi salvare 2 caratteri sostituendoli .startswith(C)con [:len(C)]==C.
Greg Hewgill,

Wow grazie! Sta diventando piuttosto strano, dal momento che il caricamento dell'intero dizionario in ogni ricorsione salva i caratteri e rallenta ancora una volta l'algoritmo.
Falko,

@GregHewgill: Sì, è quello che ho fatto inizialmente. Ho appena modificato la mia risposta per indirizzare entrambe le versioni.
Falko,

1
Puoi produrre risultati più interessanti (parole più lunghe) più rapidamente ordinando il dizionario in base alla lunghezza delle parole. d=sorted(open('d').read().split(),key=len,reverse=1)Oppure, fallo esternamente preordinando il tuo dizionario in quel modo.
Greg Hewgill,

Diamine, se riesci a riformattare il file del dizionario, formattalo come un dizionario Python precalcolato e M=eval(open('d').read()):)
Greg Hewgill

3

Perl (5.10+), 293 caratteri

Il file del dizionario deve essere salvato come "d" (e deve essere in formato unix se non si desidera CR tra le parole), input morse su stdin, senza newline finale (usare echo -n).

open D,d;chomp(@w=<D>);@m{a..z}=map{substr(sprintf("%b",-61+ord),1)=~y/01/.-/r}
'BUWI?OKMATJQDCLSZGE@FNHVXY'=~/./g;%w=map{$_,qr#^\Q@{[s/./$m{$&}/reg]}#}@w;
@a=[$i=<>];while(@a){say join$",@{$$_[1]}for grep!$$_[0],@a;
@a=map{$x=$_;map{$$x[0]=~$w{$_}?[substr($$x[0],$+[0]),[@{$$x[1]},$_]]:()}@w}@a}

(interruzioni di riga solo per la formattazione).

Codice non golfato:

# Read the word list
open my $dictionary, '<', 'd';
chomp(my @words = <$dictionary>);

# Define morse characters
my %morse;
@morse{'a' .. 'z'} = map {
  $n = ord($_) - 61;
  $bits = sprintf "%b", $n;
  $bits =~ tr/01/.-/;
  substr $bits, 1;
} split //, 'BUWI?OKMATJQDCLSZGE@FNHVXY';

# Make a hash of words to regexes that match their morse representation
my %morse_words = map {
  my $morse_word = s/./$morse{$_}/reg;
  ($_ => qr/^\Q$morse_word/)
} @words;

# Read the input
my $input = <>;

# Initialize the state
my @candidates = ({ remaining => $input, words => [] });

while (@candidates) {
  # Print matches
  for my $solution (grep { $_->{remaining} eq '' } @candidates) {
    say join " ", @{ $solution->{words} }; 
  } 
  # Generate new solutions
  @candidates = map {
    my $candidate = $_;
    map {
      $candidate->{remaining} =~ $morse_words{$_}
        ? {
          remaining => substr( $candidate->{remaining}, $+[0] ),
          words => [ @{ $candidate->{words} }, $_ ],
        }
        : ()
    } @words
  } @candidates;
}

Modus operandi:

I simboli del codice Morse vengono memorizzati cambiando "." e "-" in cifre binarie 0 e 1, anteponendo un "1" (in modo che i punti iniziali non vengano divorati), convertendo il numero binario in decimale, quindi codificando il carattere con il valore 61 più alto (che mi dà tutto caratteri stampabili e niente che abbia bisogno di backslashing).

Ho pensato a questo come una specie di problema di partizionamento e ho costruito una soluzione basata su quello. Per ogni parola nel dizionario, costruisce un oggetto regex che corrisponderà (e catturerà) la rappresentazione morse senza spazio di quella parola all'inizio di una stringa. Quindi inizia una ricerca ampia prima creando uno stato che non corrisponde a parole e ha l'intero input come "input rimanente". Quindi espande ogni stato cercando le parole che corrispondono all'inizio dell'input rimanente e creando nuovi stati che aggiungono la parola alle parole corrispondenti e rimuovono il morse dall'input rimanente. Gli stati che non hanno input rimanenti hanno esito positivo e hanno stampato il loro elenco di parole. Gli stati che non possono corrispondere a nessuna parola (compresi quelli riusciti) non generano stati secondari.

Si noti che nella versione non golfata gli stati sono hash per la leggibilità; nella versione golfata sono array (per codice più breve e minore consumo di memoria); slot [0]è l'ingresso rimanente e slot [1]è le parole corrispondenti.

Commento

Questo è empiamente lento. Mi chiedo se c'è una soluzione che non lo è. Ho provato a costruirne uno usando Marpa (un parser Earley con la possibilità di dare più analisi per una singola stringa di input) ma ho esaurito la memoria solo costruendo la grammatica. Forse se avessi usato un'API di livello inferiore invece dell'input BNF ...


Se aggiungo lo stesso requisito di Kevin Reid (nessuna nuova riga nell'input) posso salvare 7 caratteri rimuovendoli chomp(). Dovrei?
Hobbs,

"Sentiti libero di usare qualsiasi metodo conveniente".
Comintern,

Radere 2 byte con ordinvece di ord$_. Radere 1 byte dicendo join$"invece dijoin" "
Zaid,

2

Haskell - 418

Questo problema di decorazione può essere risolto in modo efficiente mediante la programmazione dinamica. So che questo è un codegolf, ma adoro il codice veloce.

Supponiamo che abbiamo la stringa di input s, quindi creiamo un array dp, dp[i]è l'elenco di tutti i risultati di decodifica validi della sottostringa s[:i]. Per ogni parola wnel dizionario, prima la codifichiamo in mw, quindi possiamo calcolare parte di dp[i]da dp[i - length(mw)]if s[i - length(mw):i] == mw. La complessità temporale dell'edificio dpè O({count of words} {length of s} {max word length}). Infine, dp[length(s)]l'ultimo elemento, è ciò di cui abbiamo bisogno.

In effetti, non è necessario memorizzare l'intera decodifica come elemento di ciascuno dp[i]. Ciò di cui abbiamo bisogno è l'ultima parola decodificata. Questo rende l'implementazione molto più veloce. Costa meno di 2 secondi per completare la custodia "ciao mondo" sul mio laptop i3. Per gli altri casi pubblicati nella domanda, il programma non terminerà praticamente poiché ci sono troppi risultati.

Usando la tecnica di programmazione dinamica, possiamo calcolare il numero di decodifiche valide . Puoi trovare il codice qui . risultati:

input: ......-...-..---.-----.-..-..-..
count: 403856

input: .--..-.-----..-..-----..-.--..--...---..--...-.......--.-..-.-.----...--.---.-....-.
count: 2889424682038128

input: -.....--.-..-..-.-.-.--....-.---.---...-.----..-.---..---.--....---...-..-.-......-...---..-.---..-----.
count: 4986181473975221635

Ungolfed

import Control.Monad

morseTable :: [(Char, String)]
morseTable = zip ['a'..'z'] $ words ".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.."

wordToMorse :: String -> Maybe String
wordToMorse xs = return . concat =<< mapM (`lookup` morseTable) xs

slice :: (Int, Int) -> [a] -> [a]
slice (start, end) = take (end - start) . drop start

decode :: String -> String -> IO ()
decode dict s = trace (length s) [] where
  dict' = [(w, maybe "x" id . wordToMorse $ w) | w <- lines dict]
  dp = flip map [0..length s] $ \i -> [(j, w) |
        (w, mw) <- dict', let j = i - length mw, j >= 0 && mw == slice (j, i) s]

  trace :: Int -> [String] -> IO ()
  trace 0 prefix = putStrLn . unwords $ prefix
  trace i prefix = sequence_ [trace j (w:prefix) | (j, w) <- dp !! i]

main :: IO ()
main = do
  ws <- readFile "wordlist.txt"
  decode ws =<< getLine

golfed

import Control.Monad
l=length
t=zip['a'..]$words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.."
h s=return.concat=<<mapM(`lookup`t)s
f d s=g(l s)[]where g 0 p=putStrLn.unwords$p;g i p=sequence_[g j(w:p)|(j,w)<-map(\i->[(j,w)|(w,m)<-[(w,maybe"x"id.h$w)|w<-lines d],let j=i-l m,j>=0&&m==(take(i-j).drop j$s)])[0..l s]!!i]
main=do d<-readFile"d";f d=<<getLine

Sono contento di vedere una soluzione ragionevolmente efficiente. Ora devo solo capirlo :)
hobbs il

2

PHP, 234 226 byte

function f($s,$r=""){$s?:print"$r
";foreach(file(d)as$w){for($i=+$m="";$p=@strpos(__etianmsurwdkgohvf_l_pjbxcyzq,$w[$i++]);)$m.=strtr(substr(decbin($p),1),10,"-.");0!==strpos($s,$m)?:g(substr($s,strlen($m)),$r.trim($w)." ");}}

funzione ricorsiva, prende il dizionario da un file denominato d.
Non riesce per ogni parola nel dizionario contenente una non lettera.

È possibile utilizzare qualsiasi nome di file se define ("d","<filename>");prima di chiamare la funzione.

Aggiungi 2 o 3 byte per un'esecuzione più rapida:
rimuovi $s?:print"$r\n";, inserisci $s!=$m?prima 0!==e :print$r.$wprima ;}}.

abbattersi

function f($s,$r="")
{
    $s?:print"$r\n";            // if input is empty, print result
    foreach(file(d)as$w)        // loop through words
    {
        // translate to morse:
        for($i=+$m="";              // init morse to empty string, $i to 0
                                        // loop while character is in the string
            $p=@strpos(__etianmsurwdkgohvf_l_pjbxcyzq,$w[$i++])
        ;)
            $m.=                        // 4. append to word morse code
                strtr(
                    substr(
                        decbin($p)      // 1: convert position to base 2
                    ,1)                 // 2: substr: remove leading `1`
                ,10,"-.")               // 3. strtr: dot for 0, dash for 1
            ;
        0!==strpos($s,$m)           // if $s starts with $m
            ?:f(                        // recurse
                substr($s,strlen($m)),  // with rest of $s as input
                $r.trim($w)." "         // and $r + word + space as result
            )
        ;
    }
}

1

Groovy 377 337

r=[:];t={x='',p=''->r[s[0]]=p+x;s=s.substring(1);if(p.size()<3){t('.',p+x);t('-',p+x)}};s='-eishvuf-arl-wpjtndbxkcymgzqo--';t()
m=('d'as File).readLines().groupBy{it.collect{r.get(it,0)}.join()}
f={it,h=[]->it.size().times{i->k=it[0..i]
if(k in m){m[k].each{j->f(it.substring(i+1),h+[j])}}}
if(it.empty){println h.join(' ')}}
f(args[0])

Gli appunti

Il dict deve essere un file denominato d. La stringa morse viene passata dalla riga di comando. per esempio:

% groovy morse.groovy ......-...-..---.-----.-..-..-.. | grep 'hello world'
hello world

Per "compressione del codice morse" sto usando un albero binario

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.