Морзе Декодування Гольф


24

Мене занепокоїло зростаюча ненависть до просторів, і ця відповідь надихнула мене на переконання, що код Морзе захищений від цього підступного видалення пробілів.

Отже, вашим завданням буде створити програму, яка зможе успішно перевести код Морзе з усіма видаленими пробілами.

Код Морзе

Правила:

  1. Введенням буде рядок, що складається лише з тире та крапок (ASCII 2D і 2E). Вихід не визначений для введення, що містить будь-які інші символи. Не соромтеся використовувати будь-який зручний для вашої мови метод для отримання вводу (stdin, текстовий файл, підказник користувача, що завгодно). Можна припустити, що введення коду Морзе складається лише з букв AZ, а відповідні числа чи пунктуація не потрібні.

  2. Вихідні дані повинні містити лише слова, що містяться у цьому словниковому файлі (знову ж, не соромтесь використовувати будь-який зручний спосіб доступу до файлу словника). Усі дійсні декодування повинні бути виведені в stdout, і всі точки і тире на вводі повинні використовуватися. Кожне відповідне слово у висновку повинно бути розділене пробілом, а кожне можливе декодування має бути розділене новим рядком. Як зручніше, ви можете використовувати великі регістри, малі та змішані регістри.

  3. Всі обмеження на стандартні лазівки застосовуються за одним винятком, як зазначено вище, ви можете отримати доступ до файлу словника, на який посилається вимога 2, через Інтернет-з'єднання, якщо ви цього дуже хочете. Скорочення URL-адрес прийнятне, я вважаю, що goo.gl/46I35Z , ймовірно, найкоротший.

  4. Це код гольфу, найкоротший виграш коду.

Примітка: Опублікування словникового файлу на Pastebin змінило всі закінчення рядків на послідовності стилю Windows 0A 0E. Ваша програма може приймати закінчення рядків лише з 0A, лише 0E або 0A 0E.

Випробування:

Вхід:

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

Вихідні дані повинні містити:

Привіт Світ

Вхід:

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

Вихідні дані повинні містити:

програмування головоломок та код гольфу

Вхід:

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

Вихідні дані повинні містити:

швидка бура лисиця стрибає над ледачим собакою


3
Як ви можете розказати між AN (.- -.)і EG (. --.)?
seequ

2
@Sieg - Вихід повинен буде включати обидва дійсні декодування.
Комінтерн

1
@Dennis - А-а-а ... Буду обділятись, що це зробив або Пастебін, або мій браузер. У моєму вихідному файлі їх немає. Ви можете змінити роздільник ліній на відповідний системі, інших змін не буде. Я відредагую це питання, коли не перебуваю на телефоні.
Комінтерн

2
@Falko це правильна поведінка. Зауважте, що проблема говорить про те, що ваш висновок повинен містити "привіт світ", а не те, що він обмежений цим. Він повинен друкувати всі дійсні декодування.
варення

2
(майже поетичний, насправді)
хобби

Відповіді:


5

Рубі, 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*' '}}

Якщо існує така практика, як "перевертання гольфу", я підозрюю, що я взяв участь у цьому часі. Це рішення генерує масив масивів повторних перестановок усіх словникових слів від довжини 1 до довжини вводу. Зважаючи на те, що "a" - найкоротше слово у словниковому файлі, а його код - два символи, його було б достатньо для генерації перестановок довжиною до половини розміру вводу, але додавання /2є рівнозначним для багатослівності в цьому домені, тому я утримався.

Після того, як масив перестановок був сформований ( NB : вона має довжину 45404 104 в разі pangrammatic наприклад , вхід), кожен масив перестановок об'єднується, і її літерні символи замінюються на їх кодових еквівалентів Морзе з допомогою досить зручного (Regexp, Hash)варіанту #gsubметод; ми знайшли дійсне декодування, якщо цей рядок дорівнює вхідному.

Словник читається (кілька разів) з файлу з назвою "d", і вхід не повинен містити новий рядок.

Приклад запуску (зі словником, який дасть програмі шанс боротьби після закінчення теплової смерті Всесвіту):

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

5

Хаскелл, 296 символів

  • Файл словника: повинен бути текстовий файл з назвою "d"
  • Вхід: stdin, може мати зворотний новий рядок, але немає внутрішнього простору
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]

Пояснення елементів:

  • mainчитає словник, читає stdin, виконує &та форматує вихід &з відповідним пробілом.
  • (replicate 97"X"++words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..")!!)(вираз усередині визначення &) - це список, індекси якого є символьними кодами (97 - код 'a'), а значення - послідовностями Морзе.
  • !(функція, імені оператора інфіксації) відповідає рядку проти префікса; якщо префікс присутній, він повертає залишок у одноелементному списку (успіх у монаді списку), інакше порожній список (збій у монаді списку)
  • &використовує монаду списку для "недетермінованого" виконання; це

    1. вибирає запис d(словникове слово),
    2. використовує !для порівняння форми Морзе цього слова ( w>>=((…)!!).fromEnumщо еквівалентно concatMap (((…)!!) . fromEnum) w) проти рядка введення i,
    3. викликує себе ( d&j), щоб відповідати решті рядка, і
    4. повертає можливий результат у вигляді списку слів w:n, у списку монада [w:n](що є коротшим, конкретний еквівалент return (w:n)).

    Зауважте, що кожен рядок після рядка 6 є частиною doвиразу, розпочатого у рядку 6; це займає рівно стільки ж символів, що й крапки з комою в одному рядку, але є більш читабельним, хоча це можна зробити лише один раз у програмі.

Ця програма надзвичайно повільна. Це можна зробити швидше (і трохи довше) легко, зберігаючи узагальнені слова поруч із оригіналами у списку, а не перераховувати їх у кожному збігу шаблонів. Наступне, що потрібно зробити, - це зберігати слова у двійковому дереві, накресленому символами Морзе (дворічна трійка ), щоб уникнути спроб зайвих гілок.

Це може бути трохи коротше, якщо файл словника не містить невикористаних символів, таких як "-", що дозволяє видалити replicate 97"X"++на користь виконання .(-97+)перед !!.


Чортовий син, це розумно. +1 вам.
Олександр Креггс

1
Чи знаєте ви, що (+(-97))можна переписати як (-97+)?
гордий haskeller

Ви повинні видалити третє визначення h і замість цього додати |0<1=[]до другого визначення
гордий haskeller

2
Використовуйте interactта виграйте 12 символів. interact$unlines.map unwords.(words f&)
gxtaillon

1
Ви повинні бути в змозі замінити concatMapз>>=
гордий haskeller

3

Пітон - 363 345

Код:

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

Пояснення:

Словник повинен зберігатися як звичайний текстовий файл під назвою "d".

D, P, UІ Nтільки деякі допоміжні змінні для більш короткого визначення таблиці Морзе пошуку.

s(i)- це рекурсивна функція, яка друкує раніше перекладену частину повідомлення pта кожен дійсний переклад частини коду, що залишилася i: Якщо iпорожній, ми дійшли до кінця коду, який bмістить весь переклад, таким чином ми просто printйого. В іншому випадку ми перевіряємо кожне слово wв словнику d, переводимо його в код morse, Cі, якщо решта код iпочинається з C, ми додаємо слово wдо перекладеного початку bі викликаємо функцію sрекурсивно на решту.

Примітка щодо ефективності:

Це досить повільна, але гольф-версія. Особливо завантаження словника та побудова таблиці пошуку morse ( dict(zip(...))) в кожній ітерації (щоб уникнути більшої кількості змінних) коштують багато. І було б ефективніше перекласти всі слова у словниковому файлі один раз заздалегідь, а не в кожній рекурсії на вимогу. Ці ідеї призводять до наступної версії з ще 40 символами, але значною швидкістю:

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

Можна зберегти 2 символи, замінивши .startswith(C)на [:len(C)]==C.
Грег Х'югілл

Нічого, дякую! Це стає досить дивним, оскільки завантаження всього словника в кожній рекурсії економить символи - і ще раз уповільнює алгоритм.
Фалько

@GregHewgill: Так, це я і робив спочатку. Я просто відредагував свою відповідь на обидві версії.
Фалько

1
Ви можете отримати більш цікаві результати (довші слова) швидше, сортуючи словник за зменшенням довжини слів. d=sorted(open('d').read().split(),key=len,reverse=1)Або зробіть це зовні, попередньо сортуючи словник таким чином.
Грег Хьюгілл

Чорт, якщо ви можете переформатувати словниковий файл, відформатуйте його як попередньо розрахований словник Python і M=eval(open('d').read()):)
Грег Х'югілл

3

Perl (5,10+), 293 символи

Файл словника повинен бути збережений як "d" (і повинен бути у форматі unix, якщо ви не хочете, щоб CR були між словами), morse введенням на stdin, без зворотного нового рядка (використання 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}

(рядкові перерви лише для форматування).

Невикористаний код:

# 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;
}

Образ дії:

Символи коду Морзе зберігаються, змінюючи "". і "-" у двійкові цифри 0 і 1, попередньо додавши "1" (щоб провідні точки не збивались), перетворювали двійкове число в десяткове, а потім кодували символ зі значенням 61 вище (що отримує мене все символи для друку та нічого, що потребує зворотної косої риски).

Я розглядав це як певну проблему розділення, і на цьому будував рішення. Для кожного слова в словнику він створює об'єкт регулярного вираження, який буде відповідати (і захоплювати) безпробільне подання морза цього слова на початку рядка. Тоді він розпочинає пошук у ширину, створюючи стан, який не відповідає жодним словам і має весь вхід як "вхід, що залишився". Потім він розширює кожен стан, шукаючи слова, які відповідають на початку залишку введення, і створює нові стани, які додають слово до відповідних слів і видаляють морс з вхідних, що залишилися. Держави, які не мають решти даних, є успішними та мають надрукований список слів. Держави, які не можуть відповідати жодним словам (у тому числі успішним), не створюють жодних дочірніх держав.

Зауважте, що у непереробленій версії стани є хешами для читабельності; у гольф-версії вони масиви (для коротшого коду та зменшення обсягу пам'яті); слот [0]- це інший вхід, а слот [1]- відповідні слова.

Коментар

Це нечестиво повільно. Мені цікаво, чи є рішення, якого немає. Я спробував побудувати його за допомогою Marpa (аналізатор Ерлі з можливістю надання декількох синтаксичних сигналів для одного вхідного рядка), але у мене не вистачило пам'яті лише на побудову граматики. Можливо, якщо я використовував API нижчого рівня замість входу BNF ...


Якщо я додаю ту саму вимогу, що і Кевін Рейд (немає нового рядка у введенні), я можу зберегти 7 символів, видаливши chomp(). Чи я повинен?
варення

"Сміливо користуйтесь будь-яким зручним методом".
Комінтерн

Голіть 2 байти ordзамість, а не ord$_. Голіть 1 байт, кажучи join$"замістьjoin" "
Заїд

2

Хаскелл - 418

Ця проблема декорування може бути ефективно вирішена за допомогою динамічного програмування. Я знаю, що це кодовий гольф, але я люблю швидкий код.

Скажімо, у нас є рядок введення s, потім ми будуємо масив dp, dp[i]це список усіх дійсних результатів декодування підрядків s[:i]. Для кожного слова wу словнику ми спочатку кодуємо його mw, а потім можемо обчислити частину dp[i]з dp[i - length(mw)]if s[i - length(mw):i] == mw. Часова складність будівництва dpє O({count of words} {length of s} {max word length}). Нарешті, dp[length(s)]останній елемент - це те, що нам потрібно.

Насправді нам не потрібно зберігати все декодування як елемент кожного dp[i]. Нам потрібно останнє розшифроване слово. Це робить реалізацію набагато швидшою. Щоб закінчити "привіт світ" на моєму ноутбуці i3, обійшлося менше 2 секунд. Для інших випадків, розміщених у запитанні, програма не закінчиться докладно, оскільки їх надто багато.

Використовуючи техніку динамічного програмування, ми можемо обчислити кількість дійсних декодувань . Ви можете знайти код тут . Результати:

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

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

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

Безумовно

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

Гольф

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

Радий бачити досить ефективне рішення. Тепер я просто маю це зрозуміти :)
hobbs

2

PHP, 234 226 байт

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

рекурсивна функція, бере словник з файлу з іменем d.
Помилка для кожного слова у словнику, що містить небуквене слово.

Ви можете використовувати будь-яке ім'я файлу, якщо define ("d","<filename>");перед тим, як викликати функцію.

Додайте 2 або 3 байти для швидшого виконання:
Видаліть $s?:print"$r\n";, вставіть $s!=$m?до 0!==і :print$r.$wдо ;}}.

зламатися

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

Примітки

Дікт повинен бути файлом з ім’ям d. Рядок morse передається командним рядком. наприклад:

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

Для "стиснення коду morse" я використовую двійкове дерево

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.