根据音调持续时间翻译摩尔斯电码


36

目标

摩尔斯电码通常表示为声音。给定代表声音是打开还是关闭的比特流,请将其转换为字母,数字和空格。

国际摩尔斯电码

细节

  • 根据重复的ON / OFF位的长度分析位流。
    • 1 ON位是一个点
    • 3 ON位是破折号
    • 1 OFF位定点和破折号
    • 3个OFF位分隔字符
    • 7个OFF位定界单词(空格)
  • 输入可以是字符串或数组。输入中只允许选择两个唯一的字符/值。(例如0/1,对/错,逗号/空格)
  • 输出返回一个字符串或打印到标准输出。

Input:    101010100010001011101010001011101010001110111011100000001011101110001110111011100010111010001011101010001110101
Analysis: \--H--/   E   \---L---/   \---L---/   \----O----/\-- --/\---W---/   \----O----/   \--R--/   \---L---/   \--D--/
Output:   HELLO WORLD

假设条件

  • 流始终以ON位开始和结束。
  • 没有前导或尾随空格。
  • 输入始终有效。
  • 支持所有字母(不区分大小写)和数字。

测试用例

101010100010001011101010001011101010001110111011100000001011101110001110111011100010111010001011101010001110101
HELLO WORLD

10100000001011100011101110000000101110000000101011101000101000101010001010101
I AM A FISH

1010111011101110001110111011101110111000101110111011101110001110111010101
2017

101010001110111011100010101
SOS

计分

这是代码高尔夫。下周这个时候的最低字节数代码将获胜。


输出可以有尾随空格吗?
布赖恩J

Answers:


9

APL(Dyalog)65 62 60 57字节

-3感谢ngn。

隐式前缀功能。

CY'dfns'
morse'/|[-.]+'S'&'∘(⊃∘'/. -'¨6|'1+|(00)+'S 1)

在线尝试!标头,f←和页脚只是允许从Input调用函数,同时保留TIO的字节数。在正常的APL会话中(对应于TIO的“输入”字段),将不需要它

⎕CY'dfns'Ç运算ÿDFN和工作区(库)

() 应用此默认功能:
'1+|(00)+'⎕S 1 PCRE S earch 1 遍和偶数长度0遍,返回匹配的长度
6| 除以
⊃∘'/. -'¨ 每个匹配长度的6的余数,从该字符串中选择相应的字符
'/|[-.]+'⎕S'&'∘ PCRE S earch斜杠和破折号-运行并将
morse 从莫尔斯电码转换为普通文本的内容返回


5
哇,从来不知道Dyalog内置了莫尔斯电码。
扎卡里

@Zacharýdfns中有很多内置函数。
暴民埃里克(Erik the Outgolfer)'17年

@Zacharý总是检查dfns
亚当

您正在链接到旧版本。
暴民埃里克(Erik the Outgolfer)'17年

BF功能...> _ <,哇。
扎卡里

8

Python 2中142个 135字节

lambda s:''.join(' E-T----Z-Q---RSWU--2FH-V980NIMA--1CBYX-6--GDOK534PLJ-7'[int('0'+l.replace('111','3'),16)%57]for l in s.split('000'))

在线尝试!

说明:

将字符串拆分为字母0000表示空格)

来替换每一个1113,并将其转换为基体16。

然后,每个数字都由进行修改57,给出的范围为0..54,这是当前字符的索引。


转换为基数的先前版本3:

Python 2中273个 252 247字节

lambda s:''.join(chr(dict(zip([0,242,161,134,125,122,121,202,229,238,241]+[2]*7+[5,67,70,22,1,43,25,40,4,53,23,49,8,7,26,52,77,16,13,2,14,41,17,68,71,76],[32]+range(48,91)))[int('0'+l.replace('111','2').replace('0',''),3)])for l in s.split('000'))

在线尝试!

转换为二进制的先前版本:

Python 2中282个 261 256字节

lambda s:''.join(chr(dict(zip([0,489335,96119,22391,5495,1367,341,1877,7637,30581,122333]+[2]*7+[23,469,1885,117,1,349,477,85,5,6007,471,373,119,29,1911,1501,7639,93,21,7,87,343,375,1879,7543,1909],[32]+range(48,91)))[int('0'+l,2)])for l in s.split('000'))

在线尝试!


5

红宝石,123字节

->s{s.split(/0000?/).map{|r|r[0]?"YE9_0IZTO_BHKU58V_GR_SFA__1_4NP_60X_____C__D_ML7WQ3__2__J"[r.to_i(2)%253%132%74]:" "}*""}

在线尝试!

根据字符数限制分割输入字符串。使用3或4个OFF位,以便将空格转换为空字符串。取每个字符的基数2的值,并使用3个连续除法模的取值进入一个合理范围(小于60个可能的值)。


做得非常好。
恢复莫妮卡iamnotmaynard

2
我注意到,它确定是否适用于所有情况,但是如果您0?从Regexp中删除它仍然适用于四个测试用例。
乔丹

4

Python中175个 168字节

s=lambda t,f=''.join:f('; TEMNAIOGKDWRUS;;QZYCXBJP;L;FVH09;8;;;7;;;;;;;61;;;;;;;2;;;3;45'[int('1'+f('0'if j[1:]else j for j in i.split('0')),2)]for i in t.split('000'))

首先将字符串转换为0(破折号)/ 1(点)字符串的列表,添加前缀1(防止前导零和处理空格),然后转换为二进制。

由于每个代码的长度均不超过5,因此结果的范围为0到63,可以在字符串中列出。


1
我独立地得到了基本相同的解决方案,但是有169个字节:lambda s:''.join("_ TEMNAIOGKDWRUS__QZYCXBJP_L_FVH09_8___7_______61_______2___3_45"[int('1'+filter(int,l).replace('2','0'),2)]for l in s.replace('111','2').split('000'))
Alex Varga

@AlexVarga Python 2的用法不错filter
Colera Su


3

Visual Basic .NET(.NET Core),252个字节

-7个字节,感谢@recursive

Function A(i)
For Each w In i.Split({"0000000"},0)
For Each l In w.Split({"000"},0)
Dim c=0
For Each p In l.Split("0")
c=c*2+1+p.Length\2
Next
A &="!ETIANMSURWDKGOHVF!L!PJBXCYZQ!!54!3!!!2!!!!!!!16!!!!!!!7!!!8!90"(c)
Next
A+=" "
Next
End Function

该函数接受1s和0s的字符串,并返回一个字符串。(实际上,只有0对于OFF是一个硬性要求。任何不OFF被认为是ON)。

字符串文字是摩尔斯电码设置为数组形式的二进制堆。VB.NET允许您将字符串索引为字符数组。的\是整数除法,以用于左子堆1或右子堆111

!当那个堆位置没有值时,我用作空白。只需要适当填充索引。

通过VB.NET,您可以通过为函数名称分配一个值(在本例中为A)来返回。我只是迭代地进行字符串串联(&)来构建输出字符串。我第一次需要使用,&因为使用会+留下一个前导null字符,但是在其他任何时候我都可以使用+,其行为与&字符串相同。

在线尝试!


1
您可以使用来保存7个字节"!ETIANMSURWDKGOHVF!L!PJBXCYZQ!!5473!!8290!!!!!16",然后使用来进行索引M(c-c\48*22),然后甚至不使用来保存另外4 个字节M,而只需使用字符串文字内联即可。
递归

@recursive我了解4字节的技巧,感谢您的帮助!我无法理解您更改索引的方式。如果我替换字符串文字,然后使用M(c-c\48*22),则2017案例的索引超出范围。我认为VB将以相同的优先级进行除法和乘法。我想念一个括号吗?
Brian J

您对优先次序是正确的。 c\48*22将为022。这是一种有条件地从中减去22的方法c,可M通过“折叠”字符串的末尾使其变短。如果那对您不起作用,您始终可以从中删除paren A &=(" ")的另外2个字节。:)
递归

然后,您可以更改&=+=,并删除另外两个空格。
递归

@recursive哦,du!过多的多余部分。更改为加号的问题是我的字符串开头有一个前导空字符。不过,也许这没什么大不了的。
布赖恩J

3

的JavaScript(ES6),170个 131字节

s=>s.split`000`.map(e=>'  ETIANMSURWDKGOHVF L PJBXCYZQ'[c=+`0b${1+e.replace(/0?(111|1)/g,d=>+(d>1))}`]||'473168290 5'[c%11]).join``


怎么运行的:

如果将点更改为0,将破折号更改为1,并以1开头,则会得到二进制数字,将其转换为十进制后,您将:

  1. 字母:2-18、20和22-29。
    可以通过将索引为来将它们转换为正确的字母' ETIANMSURWDKGOHVF L PJBXCYZQ'
  2. 数字:32、33、35、39、47、48、56、60、62和63。
    如果我们采用这些数字模数11,我们将得到数字0-8和10,可以将它们转换为正确的数字索引到'473168290 5'

该程序拆分字符,然后将每个字符转换为点和破折号,然后根据上述规则将其转换为适当的输出。


测试用例:


3

Python 2,127个字节

lambda s:''.join("IVMB  T  K 9LZF 1HWO3 GUS4 8 7A  E QR 26   NJX    Y0P 5D  C"[(int('0'+l)^2162146)%59]for l in s.split('000'))

在线尝试!

通过删除替换并在base 10中工作来构建TFeld的解决方案,但需要按位xor和更长的参考字符串。


2

PHP,321个 284字节

@ovs节省了37个字节

$a=array_flip([242,161,134,125,122,121,202,229,238,241,5,67,70,22,1,43,25,40,4,53,23,49,8,7,26,52,77,16,13,2,14,41,17,68,71,76]);foreach(split('0000000',$argv[1])as$w){foreach(split('000',$w)as$m){echo($v=$a[base_convert(str_replace([111,0],[2,],$m),3,10)])>9?chr($v+55):$v;}echo' ';}  

先前版本(321字节)

$a=array_flip([22222,12222,11222,11122,11112,11111,21111,22111,22211,22221,12,2111,2121,211,1,1121,221,1111,11,1222,212,1211,22,21,222,1221,2212,121,111,2,112,1112,122,2112,2122,2211]);foreach(split('0000000',$argv[1])as$w){foreach(split('000',$w)as$m){echo($v=$a[str_replace([111,0],[2,],$m)])>9?chr($v+55):$v;}echo' ';}

在线尝试!

非高尔夫版本:

$a=array_flip(
// Building an array $a with every Morse letter representation (1=dot, 2=dash) and flip it
               [22222,12222,11222,11122,11112,
                // 01234
                11111,21111,22111,22211,22221,
                // 56789
                12,2111,2121,211,1,1121,221,
                // ABCDEFG
                1111,11,1222,212,1211,22,
                // HIJKLM
                21,222,1221,2212,121,111,2,
                // NOPQRST
                112,1112,122,2112,2122,2211]);
                // UVWXYZ
foreach (split('0000000', $argv[1]) as $w){
// for each word (separate with 7 consecutive zeroes)
    foreach (split('000',$w) as $m){
    // for each letter (separate with 3 consecutive zeroes)
        echo ($v = $a[str_replace([111,0],[2,],$m)]) > 9
        // Replace '111' with '2' and '0' with nothing and find $v, the corresponding entry in the array $a
            ? chr($v+55)
            // If > 9th element, then letter => echo the ASCII code equal to $v+55
            : $v;
            // Else echo $v
    }
    echo ' ';
    // Echo a space
}

2

Java(OpenJDK 8),370字节

s->{String r="";for(String t:s.split("0000000")){for(String u:t.split("000"))for(int x[]={1,7,5,21,29,23,87,93,119,85,117,341,375,343,373,471,477,349,469,1877,1367,1909,1879,1501,1911,1885,7637,5495,7543,7639,6007,30581,22391,122333,96119,489335},i=x.length;i-->0;)if(u.equals(Long.toString(x[i],2)))r+="ETISNAURMHD5WVLKGFB64ZXPOC73YQJ82910".charAt(i);r+=" ";}return r;}

在线尝试!

  • @Jeutnarg节省了3个字节。

1
可以通过使用Long.toString(x [i],2)而不是Integer.toString(x [i],2)来
减少一些磨损

2

GNU sed 261 + 1 = 262字节

+1字节的-r标志。

s/000/;/g
s/111/_/g
s/0//g
s/$/;:51111141111_3111__211___i1____6_11117__1118___119____10_____H1111V111_F11_1L1_11P1__1J1___B_111X_11_C_1_1Y_1__Z__11Q__1_S111U11_R1_1W1__D_11K_1_N__1G__1O___I11A1_M__E1T_/
:
s/([1_]+);(.*([^1_])\1)/\3\2/
t
y/i/1/
s/;/ /g
s/:.*//g

在线尝试!

说明

这是一个非常基本的查找表解决方案。

前三行转换输入,因此破折号是_s,点是1s。首先,将000s替换为;,使字符用分隔;,单词用分隔;;0。然后,111将替换为,_0丢弃所有剩余的s,将1s 保留为点。

s/000/;/g
s/111/_/g
s/0//g

下一行追加了查找表。它采用cmcmcm...where c是一个字符的形式,是s和s表示它m的序列。取代表中的歧义。由于sed中的正则表达式总是贪婪的,因此该表按从最长到最短的代码排序(例如,使用match 代替)。_1i11_A1_i1____

s/$/;:51111141111_3111__211___i1____6_11117__1118___119____10_____H1111V111_F11_1L1_11P1__1J1___B_111X_11_C_1_1Y_1__Z__11Q__1_S111U11_R1_1W1__D_11K_1_N__1G__1O___I11A1_M__E1T_/

接下来,在循环中,_s和1s的每个序列(以及后续的;)被相应的字符替换:

:
s/([1_]+);(.*([^1_])\1)/\3\2/
t

最后,i1s 代替cleanup:s ,其余;s为空格,并删除查找表:

y/i/1/
s/;/ /g
s/:.*//g


1

的JavaScript(ES6),104个 102 101 99字节

s=>s.split`000`.map(n=>" _T__9VEFO0K7MX_CGS__LU1RYIJ845__Z_B_D6QP_3__AHNW2"[n*1741%8360%51]).join``

测试用例

怎么样?

因为从二进制转换为十进制会消耗字节,所以我们使用了一个哈希函数,该函数可直接用于以10为底的二进制块。

dot dash dot dot = 101110101
101110101 * 1741 = 176032685841
176032685841 % 8360 = 3081
3081 % 51 = 21

--> The 21st character in the lookup table is 'L' (0-indexed).

我非常喜欢这种一步一步的方法。您执行了多大的搜索才能使这37个输出适合具有足够短函数的大小为50的完美散列?
jayprich

n*p%m0%m11p<100001<m0<100001<m1<100

1

视网膜144个 138 130 103字节

T`d`@#
^|@@@

 @?#
E
{T`L#@`R6_BI_Z5S1C_GD8__\L\HNF3P__7_`\w@#
T`589B-INPRSZ#@`490XYKT2\OVAMJWUQ_`\w##

在线尝试!链接包括测试用例。说明:

T`d`@#

因为0和1是有效的输出,所以将二进制数字更改为其他字符。

^|@@@
 

在每个字符之前插入一个空格,在单词之间插入两个空格。

 @?#
E

假定所有字符均为Es。

{T`L#@`R6_BI_Z5S1C_GD8__\L\HNF3P__7_`\w@#

假定所有字母后跟一个点,请翻译所有字母。例如,如果我们有一个E,并且看到第二个点(插入E时我们消耗了第一个点),则它转换为I。对于只能合法后跟破折号的字母,它们会进行假设,然后下一个阶段就消耗了破折号。其他字母将被删除(保留L一个字节)。

T`589B-INPRSZ#@`490XYKT2\OVAMJWUQ_`\w##

如果显示出它们实际上是一个破折号,则请纠正错误的翻译。在上一阶段假定它时,也会消耗破折号。重复两次翻译,直到消耗完所有的点和破折号。


0

Perl 5,241 + 1(-p)= 242字节

%k=map{(23,469,1885,117,1,349,477,85,5,6007,471,373,119,29,1911,1501,7639,93,21,7,87,343,375,1879,7543,1909,489335,96119,22391,5495,1367,341,1877,7637,30581,122333)[$i++]=>$_}A..Z,0..9;map{$\.=$k{oct"0b$_"}for split/000/;$\.=$"}split/0{7}/}{

在线尝试!


0

PHP,181 + 1字节

foreach(explode(_,strtr($argn. 0,[1110=>1,10=>0,"0000"=>_A,"00"=>_]))as$t)echo$t<A?~$t[-5]?(10+substr_count($t,0)*(1-2*$t[-5]))%10:__ETIANMSURWDKGOHVF_L_PJBXCYZQ[bindec("1$t")]:" ";

与管道一起运行-nR在线尝试


0

ES6,268字节

从摩尔斯的base36表示映射到索引位置后,使用ASCII编码。这不是我最好的高尔夫日,但只花了15分钟左右。

s=>s.split('00000').map(w=>String.fromCharCode.apply({},w.split('000').map(c=>"ahkn,225z,h9z,48n,11z,9h,1g5,5w5,nlh,2me5,,,,,,,,n,d1,1gd,39,1,9p,d9,2d,5,4mv,d3,ad,3b,t,1h3,15p,5w7,2l,l,7,2f,9j,af,1g7,1h1".split(',').indexOf(parseInt(c,2).toString(36))+48))).join(' ')

易于阅读(有点):

s=>
s
.split('00000')
.map(w=>
	String.fromCharCode.apply({},
		w.split('000')
			.map(c=>
				"ahkn,225z,h9z,48n,11z,9h,1g5,5w5,nlh,2me5,,,,,,,,n,d1,1gd,39,1,9p,d9,2d,5,4mv,d3,ad,3b,t,1h3,15p,5w7,2l,l,7,2f,9j,af,1g7,1h1"
				.split(',')
				.indexOf(
					parseInt(c,2).toString(36)
				)+48)
			)
	).join(' ')


0

Wolfram语言(Mathematica),288字节

考虑过从文件中以二进制形式读取数据,但这很难解释。Base 36似乎是一种很好的折衷方法,可以按词法有效地存储数据。

将0和1的字符串作为输入。进行一系列替换,从7个零的游程开始,然后是3的游程,然后是最长的二进制字母,再到最短的二进制字母。更换顺序很重要。

StringReplace[#,Thread@Rule[Join[{"0000000","000"},#~FromDigits~36~IntegerString~2&/@StringSplit@"ahkn 2me5 225z nlh h9z 5w7 5w5 5tj 4mv 48n 1h3 1h1 1gd 1g7 1g5 15p 11z d9 d3 d1 af ad 9p 9j 9h 3b 39 2l 2f 2d t n l 7 5 1"],Insert[Characters@" 09182Q7YJ3OZCX6P4GKBWLFV5MDRUHNASTIE","",2]]]&

在线尝试!


等等,Mathematica没有内置的莫尔斯电码?
扎卡里

还没!我检查了。
凯莉·洛德

0

Perl 5,195个字节

194字节代码+ 1 for -p

%h=map{$_,(A..Z,0..9)[$i++]}unpack"S26I2S7I","\xd5]u]\xddUw\xd7uww\xdd\xd7]WWwWwuwwwwwWwWUU\xd5uw\xdd\xdd";s/0{7}/ /g;s/(\d+?)(000|\b)/$h{oct"0b$1"}/ge

我不能仅使用标准的打包二进制字符串来完成此操作,我必须转义更高字节的字符,否则如果有人知道我错过了什么,或者为什么要破坏它,那我就应该是171 !

在线尝试!

说明

二进制字符串是与摩尔斯字符(- 表示等)相关的数字的packed列表,并用范围压缩并用作查找。这些表达式用空格替换所有7 s的游程,然后将所有用3 s或单词边界分隔的数字游程及其对应的键从哈希中替换。101011101349FA..Z,0..9s///00\b%h

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.