Morse Decode Golf


24

Gittikçe artan uzay nefretinden endişe duydum ve bu cevap bana Mors kodunun bu sinsi beyaz boşlukların kaldırılmasında güvenli olduğundan emin olmak için ilham verdi.

Bu nedenle, göreviniz Mors kodunu tüm boşluklar kaldırılarak başarıyla çevirebilecek bir program oluşturmak olacaktır.

Mors kodu

Kurallar:

  1. Giriş, yalnızca çizgi ve noktalardan oluşan bir dize olacaktır (ASCII 2D ve 2E). Diğer karakterleri içeren giriş için çıkış tanımsız. Girdiyi almak için seçtiğiniz dile uygun herhangi bir yöntemi kullanmaktan çekinmeyin (stdin, metin dosyası, anında kullanıcı, ne olursa olsun). Mors kodu girişinin yalnızca AZ harflerinden oluştuğunu ve eşleşen numaraların veya noktalama işaretlerinin gerekli olmadığını varsayabilirsiniz.

  2. Çıktı yalnızca bu sözlük dosyasında bulunan kelimeleri içermelidir (yine, sözlük dosyasına erişmek için uygun herhangi bir yöntemi kullanmaktan çekinmeyin). Tüm geçerli kod çözme işlemleri stdout'a verilmelidir ve girdideki tüm noktalar ve çizgiler kullanılmalıdır. Çıktıdaki eşleşen her kelime bir boşlukla ayrılmalı ve olası her kod çözme yeni bir satırla ayrılmalıdır. Büyük harf, küçük harf veya karışık harf çıktısını uygun şekilde kullanabilirsiniz.

  3. Standart boşluklar üzerindeki tüm kısıtlamalar yukarıda belirtildiği gibi bir istisna ile uygulanır, gerçekten isterseniz 2 numaralı şartta belirtilen sözlük dosyasına bir internet bağlantısı üzerinden erişebilirsiniz. URL kısaltması kabul edilebilir, goo.gl/46I35Z'nin muhtemelen en kısa olduğunu düşünüyorum .

  4. Bu kod golf, en kısa kod kazanır.

Not: Sözlük dosyasını Pastebin'e göndermek, tüm satır sonlarını Windows tarzı 0A 0E dizisine değiştirdi. Programınız sadece 0A, sadece 0E veya 0A 0E ile satır sonları alabilir.

Test Durumları:

Giriş:

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

Çıktı şunları içermelidir:

Selam Dünya

Giriş:

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

Çıktı şunları içermelidir:

bulmaca programlama ve kod golf

Giriş:

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

Çıktı şunları içermelidir:

hızlı kahverengi tilki tembel köpeğin üstünden atlar


3
Nasıl arasındaki söyleyebilir AN (.- -.)ve EG (. --.)?
Ağustos'ta

2
@Sieg - Çıktı daha sonra her iki geçerli kod çözmeyi içermelidir.
Comintern

1
@Dennis - Ahhh ... Bahse girerim ya Pastebin ya da tarayıcım bunu yapmış. Kaynak dosyamda yok. Satır sınırlayıcıyı sisteme uygun bir sistemle değiştirebilir, başka bir değişiklik yapamazsınız. Telefonumda olmadığımda soruyu düzenleyeceğim.
Comintern

2
@Falko bu doğru davranış. Sorunun çıktınızın "merhaba dünya" içermesi gerektiğini , bunun bununla sınırlı olmadığını söylediğini unutmayın. Geçerli tüm kodlamaları yazdırmalıdır.
Ocaklar

2
(neredeyse şiirsel, gerçekten)
hobbs

Yanıtlar:


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*' '}}

"Aşırı golf" gibi bir uygulama varsa, bu sefer başıma geldiğimden şüpheleniyorum. Bu çözüm, tüm sözlük kelimelerinin tekrarlanan permütasyonlarının bir dizisini oluşturur. , uzunluk 1'den giriş uzunluğuna kadar . "A" nın sözlük dosyasındaki en kısa kelime olduğu ve kodunun iki karakter uzunluğunda olduğu göz önüne alındığında, girdi büyüklüğünün yarısına kadar uzunluk izinleri oluşturmak yeterli olurdu, ancak /2bu alandaki ayrıntılara eşlik etmek de aynı olacaktır. bu yüzden kaçındım.

Permütasyon dizisi oluşturulduktan sonra ( NB : pangrammatik örnek girişi durumunda 45404 104 uzunluğundadır ), her permütasyon dizisi bitiştirilir ve alfabetik karakterleri, Mors kodu eşdeğerleriyle yerine, oldukça uygun bir (Regexp, Hash)varyantı ile değiştirilir. #gsubyöntem; Bu dize girişe eşitse geçerli bir kod çözme bulduk.

Sözlük, "d" adlı bir dosyadan (birkaç kez) okunur ve giriş bir yeni satır içermemelidir.

Örnek çalışma (programa evrenin sıcak ölümünden önce sona ermesi için mücadele etme şansı verecek bir sözlükle):

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

5

Haskell, 296 karakter

  • Sözlük dosyası: "d" adında bir metin dosyası olmalı
  • Girdi: stdin, sonunda yeni bir satırsonu olabilir ancak dahili boşluk yok
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]

Elemanların açıklaması:

  • mainsözlüğü okur, stdin okur, yürütür &ve çıktısını &uygun boşluklarla düzenler.
  • (replicate 97"X"++words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..")!!)(tanımının içindeki ifade &), endeksleri karakter kodları (97 kodudur 'a') ve değerler Mors dizileri olan bir listedir .
  • !(infix operatörü olarak adlandırılan bir işlev), bir önekle bir dize ile eşleşir; önek varsa geri kalanı bir elemanlı listede (liste monadında başarılı), aksi takdirde boş listeden (liste monadındaki başarısızlık) döndürür.
  • &“karakteristik olmayan” yürütme için monad listesini kullanır; o

    1. girişini seçer d(bir sözlük kelimesi),
    2. !Sözcüğün Mors biçimini ( w>>=((…)!!).fromEnumeşdeğer olan concatMap (((…)!!) . fromEnum) w) giriş dizgisiyle eşleştirmek için kullanır i,
    3. d&jgeri kalanı eşleştirmek için kendisini ( ) çağırır ve
    4. Olası sonucu bir sözcük listesi olarak döndürür, w:nmonad [w:n](bu kısaca , eşdeğer olan return (w:n)) listesindedir .

    6. satırdan sonraki her satırın 6. satırda dobaşlayan ifadenin bir parçası olduğuna dikkat edin ; bu, tek bir satırda noktalı virgül kullanmakla aynı sayıda karakter alır, ancak bir programda yalnızca bir kez yapmanıza rağmen daha okunaklıdır.

Bu program son derece yavaştır. Her bir desen eşleşmesinde yeniden hesaplamak yerine, orijinallerin yanındaki morsified sözcükleri bir listedeki saklayarak kolayca ve hızlıca yapılabilir. Yapılacak bir sonraki şey, gereksiz dalları denemekten kaçınmak için, kelimeleri Mors sembolleri (2 ary trie ) ile anahtarlanmış bir ikili ağaçta saklamak olacaktır .

Eğer sözlük dosyası "-" gibi kullanılmamış semboller içermiyorsa, daha önce replicate 97"X"++yapmak yerine çıkarılmasını sağlayan, biraz daha kısa yapılabilir ..(-97+)!!


Kahretsin evlat, bu çok zekice. +1 sana.
Alexander Craggs,

1
(+(-97))Bunun olarak yeniden yazılabileceğini biliyor muydunuz (-97+)?
Gurur haskeller

|0<1=[]
H'nin

2
interact12 karakter kullanın ve kazanın. interact$unlines.map unwords.(words f&)
gxtaillon

1
Sen yerine gerekir concatMapile>>=
gururlu haskeller

3

Python - 363 345

Kod:

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

Açıklama:

Sözlük "d" adında bir düz metin dosyası olarak saklanmalıdır.

D, P, UVe Nmors arama tablosu daha kısa tanımı için sadece bazı yardımcı değişkenlerdir.

s(i)Önceden çevrilmiş mesaj kısmını pve kalan kod parçasının geçerli her çevirisini basan özyinelemeli bir işlevdir i: iBoşsa, kodun sonuna ulaştık bve tüm çeviriyi içerdik, yani biz basitçe print. Aksi halde w, sözlükteki her bir kelimeyi kontrol ederiz d, onu mors koduna çeviririz C, ve kalan kod ibaşlıyorsa , çevrilen Ckelimeye kelimeyi ekleriz ve fonksiyonu kalan kısımda tekrar tekrar çağırırız .wbs

Verimlilik hakkında not:

Bu oldukça yavaş ama golf versiyonudur. Özellikle sözlüğü yüklemek ve dict(zip(...))her yinelemede mors arama tablosu ( ) oluşturmak (daha fazla değişkenleri önlemek için) çok maliyetlidir. Ayrıca, sözlük dosyasındaki tüm sözcükleri talep üzerine yapılan her özyinelemede değil, bir kez önceden çevirmek daha etkili olacaktır. Bu fikirler, 40 karakterden daha fazla fakat önemli bir hızlanma ile aşağıdaki sürüme götürür :

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

Sen değiştirerek 2 karakter kaydedebilirsiniz .startswith(C)ile [:len(C)]==C.
Greg Hewgill

Vay, teşekkürler! Oldukça garipleşiyor, çünkü her bir sözdizimde tüm sözlüğü yüklemek karakterleri kurtarıyor ve algoritmayı bir kez daha yavaşlatıyor.
Falko

@GregHewgill: Evet, aslen bunu yaptım. Her iki versiyona da cevap verebilmek için cevabımı değiştirdim.
Falko,

1
Sözcüğü sözcük uzunluğuna göre sıralayarak daha hızlı ve daha ilginç sonuçlar (daha uzun sözcükler) oluşturabilirsiniz. d=sorted(open('d').read().split(),key=len,reverse=1)Veya, sözlüğünüzü bu şekilde önceden sıralayarak harici olarak yapın.
Greg Hewgill

Heck, sözlük dosyasını yeniden biçimlendirebilirseniz, önceden hesaplanmış bir Python sözlüğü olarak biçimlendirin ve M=eval(open('d').read()):)
Greg Hewgill

3

Perl (5.10+), 293 karakter

Sözlük dosyası "d" olarak kaydedilmelidir (ve eğer kelimeler arasında CR'ler istemiyorsanız unix formatında olmalıdır), stdin'deki mors girişi, izleyen yeni satır olmadan (kullanım 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}

(yalnızca biçimlendirme için satır aralığı).

Ungolfed kod:

# 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:

Mors kodu sembolleri "" değiştirilerek saklanır. ve "-", 0 ve 1'in ikilik rakamlarına, bir "1" (öndeki noktalar kesilmeyecek şekilde) hazırlar, ikili sayıyı ondalık sayıya dönüştürür ve ardından karakteri 61 değerinde daha yüksek olan (beni alır. basılabilir karakterler ve ters eğik çizgi gerektiren hiçbir şey yok).

Bunu bir tür bölümleme sorunu olarak düşündüm ve buna dayalı bir çözüm geliştirdim. Sözlükteki her kelime için, bir kelimenin başında dizgenin boşluksuz mors gösterimi ile eşleşen (ve onu yakalayan) bir regex nesnesi oluşturur. Daha sonra, hiçbir kelimeyle eşleşmeyen ve tüm girişi "kalan giriş" olarak gösteren bir durum oluşturarak genişlik ilk aramaya başlar. Ardından, kalan girişin başında eşleşen sözcükleri arayarak ve eşleşen sözcüklere sözcük ekleyen ve kalanı diğer sözcükten kaldıran yeni durumlar yaratarak her durumu genişletir. Geriye kalan hiçbir girişi olmayan ve başarılı olan sözcüklerin listesini içeren durumlar. Hiçbir kelimeyle eşleşemeyen devletler (başarılı olanlar dahil), hiç çocuk devlet üretmezler.

Ungolfed versiyonunda, durumların okunabilirlik için karma değerler olduğuna dikkat edin; golf sürümünde dizilerdir (daha kısa kod ve daha az bellek tüketimi için); slot [0]kalan giriş ve slot [1]eşleşen kelimelerdir.

yorum

Bu çok yavaş. Merak etmeyen bir çözüm olup olmadığını merak ediyorum. Marpa'yı kullanarak (tek bir giriş dizisi için çoklu ayrıştırma yapabilen bir Earley ayrıştırıcısı) bir tane oluşturmaya çalıştım, ancak sadece gramer yapılarını oluşturan bellekten mahrum kaldı. Belki BNF girişi yerine daha düşük seviyeli bir API kullanıyor olsaydım ...


Kevin Reid ile aynı şartı eklersem (girişte yeni satır yok) kaldırarak 7 karakter kaydedebilirim chomp(). Yapmalımıyım?
Ocak

"Uygun herhangi bir yöntemi kullanmaktan çekinmeyin".
Comintern

ordBunun yerine 2 bayt'ı tıraş edin ord$_. Diyerek bayt Tıraş 1 join$"yerinejoin" "
Zaid

2

Haskell - 418

Bu kod çözme problemi dinamik programlama ile verimli bir şekilde çözülebilir. Bunun bir kod çözücü olduğunu biliyorum, ama hızlı kodu seviyorum.

Diyelim ki girdi dizgemiz var s, sonra bir dizi oluşturuyoruz dp, dp[i]tüm alt dizginin geçerli kod çözme sonuçlarının listesi s[:i]. Her kelime için wöncelikle bunu kodlayan sözlükte, mwo zaman bir kısmını hesaplayabilir dp[i]dan dp[i - length(mw)]eğer s[i - length(mw):i] == mw. Binanın zaman karmaşıklığı dpolduğunu O({count of words} {length of s} {max word length}). Son olarak, dp[length(s)]son unsur, ihtiyacımız olan şey.

Aslında, tüm kod çözmeyi her birinin öğesi olarak kaydetmemize gerek yoktur dp[i]. İhtiyacımız olan son çözülmüş kelime. Bu, uygulamayı çok daha hızlı hale getirir. İ3 dizüstü bilgisayarımdaki "merhaba dünya" davasını bitirmek 2 saniyeden daha ucuza geldi. Soruya gönderilen diğer durumlar için, çıkacak çok fazla olduğundan program pratik olarak bitmeyecektir.

Dinamik programlama tekniğini kullanarak geçerli kod çözme sayısını hesaplayabiliriz . Kodu burada bulabilirsiniz . Sonuçlar:

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

Makul derecede etkili bir çözüm görmek sevindim. Şimdi anlıyorum sadece :)
Ocak

2

PHP, 234 226 bayt

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)." ");}}

özyinelemeli işlevi, adlı bir dosyadan sözlük alır d.
Sözlük harf içermeyen her kelime için başarısız.

define ("d","<filename>");Fonksiyonu çağırmadan önce herhangi bir dosya adını kullanabilirsiniz .

Daha hızlı yürütme için 2 veya 3 bayt ekleyin :
Kaldır $s?:print"$r\n";, $s!=$m?önce 0!==ve:print$r.$w önce ;}} .

Yıkmak

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

notlar

Dict adlı bir dosya olmalı d. Mors dize komut satırı tarafından iletilir. Örneğin:

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

"Mors kodu sıkıştırma" için bir ikili ağaç kullanıyorum

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.