【问题标题】:Code Golf: Musical Notes代码高尔夫:音符
【发布时间】:2015-04-04 00:40:48
【问题描述】:

挑战

按字符数计算的最短代码,将根据用户输入输出乐谱。

输入将由一系列字母和数字组成 - 字母代表音符的名称,数字代表音符的长度。一个注释由 4 个垂直列组成。注释的头部将是大写 O,词干(如果存在)将是 3 行高,由管道字符 | 组成,标志将由反斜杠 \ 组成。

有效的音符长度是无、音符的 1/4、音符的 1/8、音符的 1/16 和音符的 1/32。

       |    |\    |\    |\
       |    |     |\    |\
       |    |     |     |\
 O    O    O     O     O
 1   1/4  1/8  1/16   1/32

音符是五线谱上的位置,根据它们的音符名称:

  ----

D ----
C     
B ----
A     
G ----
F     
E ----

所有输入都可以假定为有效且没有错误 - 每个音符在一行上用空格分隔,至少有一个有效音符。

测试用例

Input:
    B B/4 B/8 B/16 B/32 G/4 D/8 C/16 D B/16
Output:
                              |\               
    --------------------------|---|\--------
          |   |\  |\  |\      |   |\      |\
    ------|---|---|\--|\-----O----|--O----|\
          |   |   |   |\  |      O        | 
    -O---O---O---O---O----|--------------O--
                          |                 
    ---------------------O------------------

    ----------------------------------------

Input:
    E/4 F/8 G/16 A/32 E/4 F/8 G/16 A/32 
Output:

    --------------------------------

    --------------|\--------------|\
              |\  |\          |\  |\ 
    ------|\--|\--|\------|\--|\--|\
      |   |   |  O    |   |   |  O  
    --|---|--O--------|---|--O------
      |  O            |  O          
    -O---------------O--------------

Input:
    C E/32 B/8 A/4 B F/32 B C/16
Output:

    ------------------------------|\
              |\                  |\
    ----------|---|---------------|-
     O        |   |              O   
    ---------O----|--O----|\-O------
          |\     O        |\        
    ------|\--------------|\--------
          |\             O           
    -----O--------------------------

代码计数包括输入/​​输出(即完整程序)。

【问题讨论】:

  • @Idigas - 已修复,SO 的预览!= 最终输出
  • 我认为这是一个很棒的代码高尔夫!非常原创!
  • 我不想更改规格。提出了挑战。我知道如何设计我的挑战,并且不会掉以轻心。
  • @Noldorin:拜托,哦,拜托,别再改标题了。当你在做的时候,我们为什么不把我所有的高尔夫问题都改成“代码高尔夫:确定激光是否击中目标”,甚至是“代码高尔夫:使用 _ 和 | 以 ASCII 艺术形式显示的七段显示”。这是荒唐的。我看到没有人抱怨这个标题。
  • 至少“音符”怎么样?实际上,标题几乎没有提供有关内容的线索。

标签: language-agnostic code-golf rosetta-stone


【解决方案1】:

Golfscript(112 个字符)

' '%:A;10,{):y;A{2/.0~|1=~:r;0=0=5\- 7%
4y@--:q'  '' O'if-4q&!q*r*{16q/r<'|\\'
'| 'if}'  'if+{.32=y~&{;45}*}%}%n}%

【讨论】:

  • @gnibbler,正是我的想法。 =]
  • @gnibbler,谢谢。 =] 仍然感觉未优化,这是我的第一个高尔夫脚本。
  • 天哪,看看外面那个大and。没有缩写吗?
  • @mobrule,你应该保持安静,否则他可能会注意到他可以使用* 而不是and
  • @Lutz,Perl 是真正的语言?
【解决方案2】:

Perl,126 个字符(115/122 带开关)

Perl in 239 226 218 216 183 180 178 172 157 142 136 133 129 128 126 个字符

这个 126 个字符的 Perl 解决方案是我和 A. Rex 长期合作的结果。

@o=($/)x10;$/=$";map{m[/];$p=4+(5-ord)%7;
$_.=--$p?!($p&~3)*$'?16<$p*$'?"  |\\":"  | ":$/x4:" O  ",
$|--&&y@ @-@for@o}<>;print@o

A. Rex 还提出了一个使用perl -ap 开关运行的解决方案。 111(!) 此解决方案中的字符加上 4 个笔画用于额外的命令行开关, 此方案总分 115 分。

$\="$:
"x5;$p=4+(5-ord)%7,s#..##,$\=~s#(.)\K$#--$p?
$_*!($p&~3)?"$1|".(16<$p*$_?"\\":$1).$1:$1x4:O.$1x3#gemfor@F

此解决方案中的第一个换行符很重要。

或者在 shebang 行中嵌入开关的 122 个字符:

#!perl -ap
$\="$:
"x5;$p=4+(5-ord)%7,s#..##,$\=~s#(.)\K$#--$p?$_*!($p&~3)?"$1|".(16<$p*$_?
"\\":$1).$1:$1x4:O.$1x3#gemfor@F

(前两个换行符很重要)。

半音符可以支持额外的 12 个字符:

@o=($/)x10;$/=$";map{m[/];$p=4+(5-ord)%7;
$_.=--$p?!($p&~3)*$'?16<$p*$'?"  |\\":"  | ":$/x4:$'>2?" @  ":" O  ",
$|--&&y@ @-@for@o}<>;print@o

【讨论】:

  • split//,"D3C4B5A6G7F8E9" -> "D3C4B5A6G7F8E9"=~/./g
  • @Kinopiko,通常不赞成对其他人的解决方案进行优化。你可以写评论(就像你一样),但除非他们说没关系,否则请不要编辑答案。
  • grep -> 映射少一个字符。
  • 我真的很想阅读一篇关于 Perl 黑客在创建这样的东西时所经历的过程的长篇分步文章。
  • 你打高尔夫球然后调试?克里基!
【解决方案3】:

LilyPond - 244 字节

从技术上讲,这不符合输出规范,因为输出是雕刻精美的 PDF,而不是糟糕的 ASCII 文本替代品,但我认为问题只是需要 LilyPond 解决方案。事实上,您可以删除“\autoBeamOff\cadenzaOn\stemUp”,使其看起来更漂亮的格式。您还可以在“\layout{}”之后添加“\midi{}”来获取要收听的 MIDI 文件。

o=#(open-file"o""w")p=#ly:string-substitute
#(format o"~(~a"(p"2'1""2"(p"4'1""4"(p"6'1""6"(p"8'1""8"(p"/""'"(p"C""c'"(p"D""d'"(p" ""/1"(p"
"" "(ly:gulp-file"M")))))))))))#(close-port o)\score{{\autoBeamOff\cadenzaOn\stemUp\include"o"}\layout{}}

用法:lilypond thisfile.ly

注意事项:

  1. 输入必须位于程序所在目录中名为“M”的文件中。
  2. 输入文件必须以换行符结尾。 (或者通过以空格结尾来节省 9 个字节。)
  3. 输出是一个名为“thisfile.pdf”的 PDF,其中“thisfile.ly”是程序的名称。
  4. 我使用 LilyPond 2.12.2 对此进行了测试;其他版本可能无法使用。

我在 LilyPond 中做的不多,所以我不确定这是不是最好的方法,因为它必须将输入转换为 LilyPond 格式,将其写入辅助文件,然后将其读入. 我目前无法让内置的 LilyPond 解析器/评估器工作。 :(

现在正在研究 ASCII 输出解决方案.... :)

【讨论】:

【解决方案4】:

C89(186 个字符)

#define P,putchar(
N[99];*n=N;y;e=45;main(q){for(;scanf(" %c/%d",n,n+1)>0;n
+=2);for(;y<11;q=y-(75-*n++)%7 P+q-4?e:79)P*n&&q<4&q>0?
124:e)P*n++/4>>q&&q?92:e))*n||(e^=13,n=N,y++P+10))P+e);}

支持半音符(+7 个字符)

#define P,putchar(
N[99];*n=N;y;e=45;main(q){for(;scanf(" %c/%d",n,n+1)>0;n
+=2);for(;y<11;q=y-(75-*n++)%7 P+q-4?e:v<4?79:64)P*n&&q<4&q>0?
124:e)P*n++/4>>q&&q?92:e))*n||(e^=13,n=N,y++P+10))P+e);}

【讨论】:

  • 似乎需要 EOF 才能完成输入。如果我以echo 'E/4 F/8 G/16 A/32 E/4 F/8 G/16 A/32' | ./notes 执行它就可以了
  • @Chris,高尔夫参赛作品“脆弱”是很常见的。
  • @gnibbler:因为#define P,putchar,所以表示y++,putchar(10)
  • 我使用相同的技巧从我自己的答案中刮掉足够多的字节:)
【解决方案5】:

Python 178 个字符

167 是个误报,我忘了压住整个音符的词干。

R=raw_input().split()
for y in range(10):
 r=""
 for x in R:o=y-(5-ord(x[0]))%7;b=" -"[y&1]+"O\|";r+=b[0]+b[o==3]+b[-(-1<o<3and''<x[1:])]+b[2*(-1<o<":862".find(x[-1]))]
 print r

Python 167 个字符(损坏)

虽然里面有 2 个填充字符,但这个中没有邪恶之眼的空间,所以我添加了一个笑脸。这种技术利用了音符长度的最后一个字符的唯一性,我很幸运没有 1/2 音符或 1/64 音符

R=raw_input().split()
for y in range(10):
 r=""
 for x in R:o=y-(5-ord(x[0]))%7;b=" -"[y&1]+"O\|";r+=b[0]+b[o==3]+b[-(-1<o<3)]+b[2*(-1<o<":862".find(x[-1]))]
 print r

Python 186 个字符 &lt;&lt;o&gt;&gt;

Python 在这里使用了&lt;&lt;o&gt;&gt; evil eye 运算符,效果非常好。如果未找到该项目,find() 方法将返回 -1,这就是为什么 D 不需要出现在注释中的原因。


R=raw_input().split()
for y in range(10):
 r=""
 for x in R:o='CBAGFE'.find(x[0])+4;B=" -"[y%2];r+=B+(B,'O')[o==y]+(x[2:]and
y+4>o>y and"|"+(B,'\\')[int(x[2:])<<o>>6+y>0]or B*2)
 print r

11 个额外字节给出了一个带有半音符的版本


R=raw_input().split()
for y in range(10):
 r=""
 for x in R:t='CBAGFE'.find(x[0])+4;l=x[2:];B=" -"[y%2];r+=B+(B,'@O'[l
in'2'])[t==y]+(l and y+4>t>y and"|"+(B,'\\')[int(l)>>(6+y-t)>0]or B*2)
 print r
$ echo B B/2 B/4 B/8 B/16 B/32 G/4 D/8 C/16 D B/16| python notes.py 
                              |\            
------------------------------|---|\--------
      |   |   |\  |\  |\      |   |\      |\
------|---|---|---|\--|\-----@----|--O----|\
      |   |   |   |   |\  |      @        | 
-O---O---@---@---@---@----|--------------@--
                          |                 
-------------------------@------------------

--------------------------------------------

【讨论】:

  • 必须使用O 而不是o。不过,很好的解决方案;是时候打败它了。 =]
  • y-'CBAGFE'.find(x[0]) 可以是 y+1-(5-ord(x[0]))%7 以节省 3 个字符。然后你可能会考虑到+1
  • @mobrule。按照你的建议做了,去掉+1,这节省了一个额外的字节,但现在笑脸不见了。
  • 嗯..刚刚注意到我把整个笔记都弄坏了
  • 我认为您可以通过将所有缩进行更改为用分号分隔的一行来节省 2 个字符,因此您不必有换行符 空格缩进。
【解决方案6】:

159 个 Ruby 字符

n=gets.split;9.downto(0){|p|m='- '[p%2,1];n.each{|t|r=(t[0]-62)%7;g=t[2..-1]
print m+(r==p ?'O'+m*2:p>=r&&g&&p<r+4?m+'|'+(g.to_i>1<<-p+r+5?'\\':m):m*3)}
puts}

【讨论】:

  • 是的,我刚刚注意到了这一点。并在我使用时将其减少到 182 个字符:P
  • 我看到 182 然后刷新找到 159。疯了!
  • 该死,我知道 ruby​​ 可以变短,但你打败了我,你的甚至更短
  • 如果使用 '|'
【解决方案7】:

红宝石 136

n=gets;10.times{|y|puts (b=' -'[y&1,1])+n.split.map{|t|r=y-(5-t[0])%7
(r==3?'O':b)+(t[1]&&0<=r&&r<3?'|'<<(r<t[2,2].to_i/8?92:b):b+b)}*b}

Ruby 139(推文)

n=gets;10.times{|y|puts (b=' -'[y&1,1])+n.split.map{|t|r=y-(5-t[0])%7
(r==3?'O':b)+(t[1]&&0<=r&&r<3?'|'<<(r<141>>(t[-1]&7)&3?92:b):b+b)}*b}

红宝石 143

n=gets.split;10.times{|y|puts (b=' -'[y&1,1])+n.map{|t|r=y-(5-t[0])%7;m=t[-1]
(r==3?'O':b)+(m<65&&0<=r&&r<3?'|'<<(r<141>>(m&7)&3?92:b):b+b)}*b}

红宝石 148

这是另一种计算标志的方法,
其中m=ord(last character)#flags=1+m&amp;3-(1&amp;m/4)

另一种方式#flags=141&gt;&gt;(m&amp;7)&amp;3,可以多节省一个字节

n=gets.split;10.times{|y|b=' -'[y&1,1];n.each{|t|r=y-(5-t[0])%7;m=t[-1]
print b+(r==3?'O':b)+(m<65&&0<=r&&r<3?'|'<<(r<141>>(m&7)&3?92:b):b+b)}
puts}

红宝石 181

第一次尝试是我的Python解决方案的音译

n=gets.split;10.times{|y|r="";n.each{|x|o=y-(5-x[0])%7
r+=(b=" -"[y&1,1]+"O\\|")[0,1]+b[o==3?1:0,1]+b[-1<o&&o<3&&x[-1]<64?3:0,1]+b[-1<o&&o<(":862".index(x[-1]).to_i)?2:0,1]}
puts r}

【讨论】:

    【解决方案8】:

    F#,458 个字符

    相当短,但仍然大部分可读:

    let s=Array.init 10(fun _->new System.Text.StringBuilder())
    System.Console.ReadLine().Split([|' '|])
    |>Array.iter(fun n->
    for i in 0..9 do s.[i].Append(if i%2=1 then"----"else"    ")
    let l=s.[0].Length
    let i=68-int n.[0]+if n.[0]>'D'then 7 else 0
    s.[i+3].[l-3]<-'O'
    if n.Length>1 then
     for j in i..i+2 do s.[j].[l-2]<-'|'
     for j in i..i-1+(match n.[2]with|'4'->0|'8'->1|'1'->2|_->3)do s.[j].[l-1]<-'\\')
    for x in s do printfn"%s"(x.ToString())
    

    附简短评论:

    // create 10 stringbuilders that represent each line of output
    let s=Array.init 10(fun _->new System.Text.StringBuilder())
    System.Console.ReadLine().Split([|' '|])
    // for each note on the input line
    |>Array.iter(fun n->
    // write the staff
    for i in 0..9 do s.[i].Append(if i%2=1 then"----"else"    ")
    // write note (math so that 'i+3' is which stringbuilder should hold the 'O')
    let l=s.[0].Length
    let i=68-int n.[0]+if n.[0]>'D'then 7 else 0
    s.[i+3].[l-3]<-'O'
    // if partial note
    if n.Length>1 then
     // write the bar
     for j in i..i+2 do s.[j].[l-2]<-'|'
     // write the tails if necessary
     for j in i..i-1+(match n.[2]with|'4'->0|'8'->1|'1'->2|_->3)do s.[j].[l-1]<-'\\')
    // print output
    for x in s do printfn"%s"(x.ToString())
    

    【讨论】:

    • 你的F 后面应该有#
    【解决方案9】:

    C 196 个字符 &lt;&lt;o&gt;&gt;

    借用strager的一些想法。有趣的功能包括 n+++1 “triple +”运算符和 &lt;&lt;o&gt;&gt; “evil eye”运算符

    #define P,putchar
    N[99];*n=N;y;b;main(o){for(;scanf(" %c/%d",n,n+1)>0;n+=2);for(;y<11;)
    n=*n?n:(y++P(10),N)P(b=y&1?32:45)P((o=10-(*n+++1)%7-y)?b:79)P(0<o&o<4&&*n?'|':b)
    P(*n++<<o>>6&&0<o&o<4?92:b);}
    

    【讨论】:

    • 当 SO 失败但无法发布时,我设法获得了 196。您的解决方案(嗯,我的修改)暗示了我更多的优化。谢谢,但我会赢得这个。 ;D
    • 啊,但是你会有邪恶的眼睛吗?和三重+? :)
    • scanf(" %c/%d",n,n+1)+1 不可移植,因为按照标准,EOF 必须是 &lt; 0(因此 可能-1)。在我的回答中使用&gt;0(或&gt;=0,如果需要)。
    • @gnibbler,是的,我会在今天(美国东部标准时间)结束之前。我真的很想打败那个 Perl 解决方案!
    • @Evan Teran:更多这种类型的例子是“前进运算符”:while(x --&gt; 0),这实际上意味着while(x-- &gt; 0),以及“转换为布尔运算符”:!!integer,它做你认为它做的事情:)
    【解决方案10】:

    Perl 5.10 中的 168 个字符

    我最初的解决方案是 276 个字符,但经过大量调整后,它减少了 100 多个字符!

    $_=<>;
    y#481E-GA-D62 #0-9#d;
    s#.(/(.))?#$"x(7+$&).O.$"x($k=10).($1?"|":$")x3 .$"x(10-$2)."\\"x$2.$"x(9-$&)#ge;
    s#(..)*?\K (.)#-$2#g;
    print$/while--$k,s#.{$k}\K.#!print$&#ge
    

    如果您有改善这一点的小建议,请随时编辑我的代码。

    【讨论】:

      【解决方案11】:

      Lua,307 个字符

      b,s,o="\\",io.read("*l"),io.write for i=1,10 do for n,l in
      s:gmatch("(%a)/?(%d*)")do x=n:byte() w=(x<69 and 72 or 79)-x
      l=tonumber(l)or 1 d=i%2>0 and" "or"-"o(d..(i==w and"O"or
      d)..(l>3 and i<w and i+4>w and"|"or d)..(l>7 and i==w-3
      and b or l>15 and i==w-2 and b or l>31 and i==w-1 and b or
      d))end o"\n"end
      

      【讨论】:

        【解决方案12】:

        C -- 293 个字符

        仍然需要更多的压缩,它需要命令行上的 args 而不是读取它们...

        i,j,k,l;main(c,v)char **v;{char*t;l=4*(c-1)+2;t=malloc(10*l)+1;for(i=0;i<10;i
        ++){t[i*l-1]='\n';for(j=0;j<l;j++)t[i*l+j]=i&1?'-':' ';}t[10*l-1]=0;i=1;while
        (--c){j='G'-**++v;if(j<3)j+=7;t[j*l+i++]='O';if(*++*v){t[--j*l+i]='|';t[--j*l
        +i]='|';t[--j*l+i]='|';if(*++*v!='4'){t[j++*l+i+1]='\\';if(**v!='8'){t[j++*l+
        i+1]='\\';if(**v!='1'){t[j++*l+i+1]='\\';}}}}i+=3;}puts(t);}
        

        编辑:修复了 E

        编辑:最多 293 个字符,包括换行符...

        #define X t[--j*l+i]='|'
        #define Y t[j++*l+i+1]=92
        i,j,k,l;main(c,v)char**v;{char*t;l=4*(c-1)+2;t=malloc(10*l)+1;for(i=10;i;)t[--i*
        l-1]=10,memset(t+i*l,i&1?45:32,l-1);t[10*l-1]=0;for(i=1;--c;i+=3)j=71-**++v,j<3?
        j+=7:0,t[j*l+i++]=79,*++*v?X,X,X,*++*v-52?Y,**v-56?Y,**v-49?Y:0:0:0:0;puts(t);}
        

        【讨论】:

        • 哇,干得好。此外,我发现 perl 实现更具可读性的罕见情况之一 ;)
        • @ldigas,哈哈! +1 对您的评论。 =]
        猜你喜欢
        • 2010-12-17
        • 1970-01-01
        • 1970-01-01
        • 2010-12-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        • 2011-02-08
        • 2010-10-24
        相关资源
        最近更新 更多