打印音乐片段中的间隔大小


10

背景

在西方音乐中,每个音符都有一个分配的名称。在每个八度音程中,按以下顺序排列十二个唯一音符:“ CC#/ Db DD#/ Eb EFF#/ Gb GG#/ Ab AA#/ Bb B C”,其中最后一个C比第一个高八度。

为了区分不同八度音符之间的区别,在音符名称的末尾附加了一个数字(此挑战仅限于一个数字)。因此,C5是比C4高八度的音符。Bb6高于B5。

一个重要的事实是,B5和C6是彼此相邻的音符,而C0和B9是最低和最高音符。

在任何两个音符之间,存在一个距离,即它们之间的半音数量。Bb4是B4之下的一个半音,它本身是C5之下的一个半音。一个八度音阶中有十二个半音,因此Bb4与A#3的距离为12,因为它比A#3高八度音阶(请注意,单个音符最多可以具有两个名称)。

挑战

您面临的挑战是编写尽可能最短的程序,该程序可以从STDIN中获取音符列表,并将间隔更改列表打印到STDOUT。

输入内容将是音符的空格分隔列表。每个音符将由一个大写字母AG,一个可选的b或#符号以及一个数字组成。您将不必处理E#/ Fb或B#/ Cb。输入示例:

C4 D4 E4 F4 G4 A4 B4 C5 C4

输出将是一个用空格分隔的整数列表,这些整数表示每个连续音符之间的距离,并始终以+或-作为前缀,以显示该音符相对于前一个音符是升还是降。输出的数字总是比输入的音符少一个。上述输入的示例输出:

+2 +2 +1 +2 +2 +2 +1 -12

其他一些示例输入:

E5 D#5 E5 B4 E5 F#5 E5 B4
C0 B0 Bb1 A2 G#3 G4 F#5 F6
G4 Ab4 Gb4 A4 F4 A#4

及其相应的输出:

-1 +1 -5 +5 +2 -2 -5
+11 +11 +11 +11 +11 +11 +11
+1 -2 +3 -4 +5

规则与限制

  1. 获胜者取决于源代码中的字符数

  2. 您的程序应仅包含可打印的ASCII字符

  3. 您不得使用任何与音乐或声音相关的内置功能

  4. 除此以外,适用标准代码高尔夫规则


如果它打印+0-00两个相同的音符?
霍华德

@Howard因为我没有指定,所以任何一个都可以接受。
PhiNotPi 2012年

1
“ Bb4是B4之下的一个半音,它本身是C4之下的一个半音”。您是说C5结尾,对吗?
Keith Randall

哇,没注意到。感谢您发现错误。现在已修复。
PhiNotPi'4

Answers:


6

GolfScript,61岁

" "/{)12*\{"bC#D EF G A B"?(+}/}%(\{.@-.`\0>{"+"\+}*\}/;]" "*

4

Haskell,161个字符

f(c:r)=maybe(12*read[c])(+f r).lookup c$zip"bC#D.EF.G.A.B"[-1..]
g n|n<0=show n|1<3='+':show n
h x=zipWith(-)(tail x)x
main=interact$unwords.map g.h.map f.words

4

Perl,103岁

#!/usr/bin/perl -an
/.((b)|(\D))?/,(($~,$,)=($,,12*$'+ord()%20%7*2+(ord()%7>3)-$-[2]+$-[3]))[0]&&printf'%+d ',$,-$~for@F

3

C,123个字符

基于leftaboutabout的解决方案,进行了一些改进。

main(c,b,d)
    char*b;
{
    while(d=c,~scanf("%s",b)?c=-~*b*1.6,c%=12,c+=b[~b[1]&16?c+=1-b[1]/40,2:1]*12:0)
        d>1&&printf("%+d ",c-d);
}

我认为值得一提的一些技巧:
1. argv[0](此处称为b)是指向程序名称的指针,但在此处用作暂存缓冲区。我们只需要4个字节(例如C#2\0),所以我们有足够的空间。
2. c是参数的数量,因此它从1开始(当不带参数运行时)。我们用它来防止在第一轮打印。

可能的问题- c+=b[..c+=..]有点奇怪。我不认为这是未定义的行为,因为这?:是一个序列点,但也许我错了。


如果您将其视为c = c + b[..c+=..],那么显然是未定义的行为。无论内部的顺序如何[..],您都不知道外部c是在之前,期间还是之后获取的b[..]
ephemient 2012年

@ephemient,我想理论上编译器可以做到REG=c;REG+=b[..c+=..];c=REG。但是,我会惊讶地看到这样的事情在实践中。但它仍然是UB。
ugoren

它是Code Golf –我们已经通过使用UB了scanf而没有原型,这没关系。知道现实中什么是合法和不合法是
一件好事

2

C, 241 229 183

F(c){c-=65;return c*1.6+sin(c/5.+.3)+9;}
#define r if(scanf("%s",b)>0){c=F(*b)%12;c+=b[b[1]<36&&++c||b[1]>97&&c--?2:1]*12
main(e,c,d){char b[4];r;}s:d=c;r;printf("%+d ",c-d);goto s;}}

除了自己做加号,您还可以做printf("%+d ",c-d)
hammar

您可以忽略包括ideone.com/G00fS
Hauleth'4

非常好。几点建议:F(*b-65)不是c-=65;b[1]<36&&++c||b[1]>97&&c--?2:1- > b[1]&16?1:(c+=b[1]%2*2-1,2),由滥用的argv: main(e,b,c,d)char*b{(使用第一个参数指针作为工作缓冲)。
ugoren

另一个-我认为c=F(*b)%12可以用代替c=-~*b*1.6;c%=12。为什么?sin原来F可以用9.6代替。c*1.6+9.6(c+6)*1.6c-=65然后(c+6)变成c-59,然后c+1(60 * 96%12 == 0)。
ugoren

感谢所有的建议!它们可以正常工作并使它变短,但是我想我会保留现在的样子。没有正弦,这将不再是我的解决方案。
停止转为逆时针

1

因子303个字符

USING: combinators formatting io kernel math regexp sequences ;
f contents R/ [#-b]+/ all-matching-slices
[ 0 swap [ {
{ [ dup 48 < ] [ drop 1 ] }
{ [ dup 65 < ] [ 48 - 12 * ] }
{ [ dup 98 < ] [ "C-D-EF-G-A-B" index ] }
[ drop -1 ]
} cond + ] each
swap [ over [ - "%+d " printf ] dip ] when* ] each drop

有评论,

! combinators => cond
! formatting => printf
! io => contents
! kernel => swap dup drop over dip when*
! math => < - * +
! regexp => R/ all-matching-slices
! sequences => each
USING: combinators formatting io kernel math regexp sequences ;

f       ! Push false, no previous note value.

! Find notes (as string slices) in standard input. The golfed regexp
! R/ [#-b]+/ matches all notes and no whitespace.
contents R/ [#-b]+/ all-matching-slices

! For each string slice:
[
    0       ! Push 0, initial note value.
    swap    ! Move note slice to top of stack, above note value.

    ! For each Unicode codepoint in note:
    [
        ! Convert the Unicode codepoint to its value in semitones.
        ! For golf, [ 48 ] is shorter than [ CHAR: A ].
        {
            ! Sharp # {35} has 1 semitone.
            { [ dup 48 < ] [ drop 1 ] }
            ! 0-9 {48-57} has 0 to 9 octaves (1 octave = 12 semitones).
            { [ dup 65 < ] [ 48 - 12 * ] }
            ! A-G {65-71} has 0 to 11 semitones.
            { [ dup 98 < ] [ "C-D-EF-G-A-B" index ] }
            ! Flat b {98} has -1 semitone.
            [ drop -1 ]
        } cond

        +       ! Add semitones to cumulative note value.
    ] each

    swap    ! Move previous note value to top of stack.
    ! When there is a previous note value:
    [
        ! Keep current note on stack.
        over [
            ! Compute and print interval.
            - "%+d " printf
        ] dip
    ] when*
    ! Current note replaces previous note at top of stack.
] each

drop    ! Drop previous note, so stack is empty.

对于此脚本,“以空格分隔的列表”在元素之间可以有1个或多个空格,在开头或结尾可以有0个或多个空格。该脚本的确在输出末尾打印了多余的空格,但在输入末尾也接受了多余的空格(或换行符)。

如果我采用更严格的定义,即“以空格分隔的列表”在元素之间恰好有1个空格,在开头或结尾处有0个空格,那么我可以简化contents R/ [#-b]+/ all-matching-slicescontents " " split(使用splitting,而不是regexp)。但是,我需要添加更多代码以防止输出末尾有多余的空间。

如果使用不赞成使用的单词dupd,则可以缩短over [ - "%+d " printf ] dipdupd - "%+d " printf,节省8个字符。我没有使用过时的词,因为它们“打算很快被删除”。

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.