モールスデコードゴルフ


24

私はスペースに対する憎しみの高まりに不安になりました。この答えは、この暗示的な空白の除去からモールス信号が安全であることを確認するきっかけとなりました。

したがって、あなたの仕事は、モールス信号をすべてのスペースを削除して正常に翻訳できるプログラムを作成することです。

モールス信号

ルール:

  1. 入力は、ダッシュとドット(ASCII 2Dおよび2E)のみで構成される文字列になります。他の文字を含む入力の出力は未定義です。入力(stdin、テキストファイル、プロンプトユーザーなど)を受け取るために、選択した言語に都合のよい方法を自由に使用してください。モールス信号入力はA〜Zの文字のみで構成され、一致する数字や句読点は必要ないと想定できます。

  2. 出力には、この辞書ファイルに含まれる単語のみが含まれている必要があります(ここでも、辞書ファイルにアクセスするための便利な方法を自由に使用してください)。有効なデコードはすべて標準出力に出力する必要があり、入力内のすべてのドットとダッシュを使用する必要があります。出力内の一致した各単語はスペースで区切る必要があり、可能なデコードはそれぞれ改行で区切る必要があります。大文字、小文字、または大文字と小文字が混在する出力を便利に使用できます。

  3. 上記の1つの例外を除き、標準の抜け穴に対するすべての制限が適用されます。本当に必要な場合は、インターネット接続経由で要件2で参照されている辞書ファイルにアクセスできます。URLの短縮は許容されますが、goo.gl / 46I35Zが最短になると思われます。

  4. これはコードゴルフで、最短のコードが勝ちます。

注: Pastebinに辞書ファイルを投稿すると、すべての行末がWindowsスタイル0A 0Eシーケンスに変更されました。プログラムは、0Aのみ、0Eのみ、または0A 0Eの行末を想定できます。

テストケース:

入力:

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

出力には以下を含める必要があります。

こんにちは世界

入力:

.--..-.-----..-..-----..-.--..--...---..--...-.... ...--....-.-.----...--..---.-....-。

出力には以下を含める必要があります。

プログラミングパズルとコードゴルフ

入力:

-.....--....-..-......--....-.---..---...-.----..-.- --..---.--....---...-..-.-......-...---....---..-- ---。

出力には以下を含める必要があります。

速い茶色のキツネが怠zyな犬を飛び越える


3
との間AN (.- -.)をどのように見分けることができますEG (. --.)か?
seequ 14

2
@Sieg-出力には、両方の有効なデコードを含める必要があります。
コミンテルン14

1
@Dennis-ああ... Pastebinか私のブラウザがそうしたに違いない。私のソースファイルにはそれらがありません。行区切り文字をシステムに適したものに変更できますが、他の変更はありません。電話をしていないときに質問を編集します。
コミンテルン14

2
@Falkoそれは正しい動作です。この問題では、出力「hello world」が含まれている必要があり、それに限定されるわけではないことに注意してください。すべての有効なデコードを印刷する必要があります。
ホッブズ14

2
(ほとんど詩的、本当に)
ホッブズ14

回答:


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文字の長さであるため、入力のサイズの半分までの長さの順列を生成するのに十分でしたが、追加/2はこのドメインの冗長性と同等です。だから私は控えた。

置換配列が生成されると(NB:パングラマティックの例の入力の場合は長さ45404 104)、各置換配列は連結され、そのアルファベット文字は、の便利な(Regexp, Hash)バリアントを介してモールス符号に置き換えられます。#gsub方法; この文字列が入力と等しい場合、有効なデコードが見つかりました。

辞書は「d」という名前のファイルから(数回)読み込まれ、入力に改行を含めることはできません。

実行例(プログラムが宇宙の熱死の前に終了する格闘チャンスを与える辞書を使用):

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

5

Haskell、296文字

  • 辞書ファイル:「d」という名前のテキストファイルである必要があります
  • 入力:標準入力。末尾に改行がある場合がありますが、内部空白はありません
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要素リストの残りを返します(リストモナドで成功)、そうでない場合は空のリスト(リストモナドで失敗)
  • &「非決定的」実行にリストモナドを使用します。それ

    1. d(辞書の単語)のエントリを選択します。
    2. 用途!(その単語のモールス形態一致するようw>>=((…)!!).fromEnumに相当し、concatMap (((…)!!) . fromEnum) w入力文字列に対して)i
    3. d&j文字列の残りの部分と一致するように自分自身を呼び出します()
    4. 可能性のある結果を、単語のリストとしてw:n、リストモナド[w:n](より短い、具体的なに相当return (w:n))で返します。

    6行目以降のすべての行は、6行目からdo始まる式の一部であることに注意してください。これは、1行でセミコロンを使用する場合とまったく同じ数の文字を使用しますが、プログラムでは一度しか実行できませんが、読みやすくなります。

このプログラムは非常に遅いです。パターンの一致ごとに単語を再計算するのではなく、リスト内の元の単語の隣にある単語を保存することで、より速く(そして少し長く)簡単に作成できます。次にすべきことは、モールス記号(2進トライ)をキーとするバイナリツリーに単語を格納し、不要な分岐を試行しないようにすることです。

除去が可能、 - 「」辞書ファイルのような未使用のシンボルが含まれていなかった場合は、わずかに短くすることができreplicate 97"X"++やっての賛成で.(-97+)前に!!


くそー息子、それは賢い。あなたに+1。
アレクサンダークラッグス14

1
それ(+(-97))を次のように書き換えられることをご存知(-97+)ですか?
誇りに思ってhaskeller 14

あなたは時間の第三の定義を削除し、代わりに追加する必要があります|0<1=[]2番目の定義に
誇りhaskeller

2
interact12キャラクターを使用して勝ちます。interact$unlines.map unwords.(words f&)
gxtaillon 14

1
あなたは置き換えることができるはずconcatMap>>=
誇りhaskeller

3

Python- 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」という名前のプレーンテキストファイルとして保存する必要があります。

DPUおよびNモールスルックアップテーブルの短い定義のためのちょうどいくつかのヘルパー変数です。

s(i)は、以前に翻訳されたメッセージ部分pと残りのコード部分の各有効な翻訳を出力する再帰関数です。空のi場合i、コードの最後に到達し、b翻訳全体を含むため、単純にprintそれを行います。それ以外の場合はw、辞書内の各単語をチェックし、dモールス信号に変換しC、残りのコードiがで始まる場合、翻訳された先頭にC単語を追加し、残りの関数を再帰的に呼び出します。wbs

効率に関する注意:

これはかなり遅いが、ゴルフバージョンです。特に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())

に置き換える.startswith(C)と、2文字を保存できます[:len(C)]==C
グレッグヒューギル14

わあ、ありがとう!再帰ごとに辞書全体をロードすると文字が節約されるため、アルゴリズムが再び遅くなるため、かなり奇妙になっています。
ファルコ14

@GregHewgill:ええ、それは私が最初にやったことです。両方のバージョンに対応するために、回答を編集しました。
ファルコ14

1
辞書を単語の長さの降順でソートすることにより、より興味深い結果(長い単語)をより迅速に生成できます。d=sorted(open('d').read().split(),key=len,reverse=1)または、そのように辞書を事前にソートして、外部でそれを行います。
グレッグヒューギル14

ヘック、辞書ファイルを再フォーマットできる場合は、事前に計算されたPython辞書としてフォーマットし、M=eval(open('d').read()):)
グレッグヒューギル14

3

Perl(5.10以降)、293文字

辞書ファイルは「d」として保存する必要があり(単語間のCRが不要な場合はunix形式である必要があります)、標準入力にモールス信号を入力します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;
}

Modus Operandi:

モールス符号記号は、「。」を変更することで保存されます 「-」を2進数字0と1に、「1」を先頭に付けて(先頭のドットががたつかないように)、2進数を10進数に変換し、値61で文字をエンコードします(これですべてが得られます)印刷可能な文字とバックスラッシュを必要としないもの)。

これを一種のパーティション分割の問題と考え、それに基づいたソリューションを構築しました。辞書内の各単語に対して、文字列の先頭でその単語のスペースなしモールス表現に一致(およびキャプチャ)する正規表現オブジェクトを構築します。次に、単語に一致せず、入力全体を「残りの入力」として持つ状態を作成することにより、幅優先検索を開始します。次に、残りの入力の先頭で一致する単語を探し、一致した単語に単語を追加して残りの入力からモールス信号を削除する新しい状態を作成することにより、各状態を拡張します。入力が残っていない状態は成功し、単語のリストが印刷されます。どの単語にも一致しない状態(成功したものを含む)は、子状態を生成しません。

改変されていないバージョンでは、状態は読みやすくするためのハッシュであることに注意してください。ゴルフバージョンでは、それらは配列です(より短いコードとより少ないメモリ消費のため)。slot [0]は残りの入力で、slot [1]は一致した単語です。

解説

これは非常に遅いです。そうでない解決策があるかどうか疑問に思っています。Marpa(1つの入力文字列に対して複数の解析を行う機能を備えたEarleyパーサー)を使用して1つを構築しようとしましたが、文法を構築するだけでメモリ不足になりました。もしかしたら、BNF入力の代わりに低レベルAPIを使ったら...


Kevin Reidと同じ要件を追加すると(入力に改行はありません)、削除することで7文字を保存できますchomp()。したほうがいい?
ホッブズ14

「便利な方法を自由に使用してください」。
コミンテルン14

ord代わりに2バイト削るord$_join$"代わりに言う1バイトを剃るjoin" "
Zaid 14

2

ハスケル-418

この装飾問題は、動的プログラミングによって効率的に解決できます。私はこれがコードゴルフであることを知っていますが、速いコードが大好きです。

私たちは、入力文字列を持っていると言うs、我々は、アレイを構築、dpdp[i]ストリングのすべての有効な復号結果の一覧ですs[:i]w辞書内の各単語について、まずそれをにエンコードしmw、次にdp[i]from 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)]、最後の要素であるが必要です。

実際、デコード全体をeachの要素として保存する必要はありませんdp[i]。必要なのは、最後にデコードされた単語です。これにより、実装が大幅に高速化されます。i3ラップトップで「hello world」ケースを完成させるのに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 14

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

ノート

dictはという名前のファイルでなければなりませんd。モールス文字列はコマンドラインで渡されます。例えば:

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

「モールス符号圧縮」では、バイナリツリーを使用しています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.