在西方音乐中,每个音乐音符都有一个指定的名字。在每个八度范围内,有12个独特的音符按以下顺序排列:"C、C#/Db D# D#/Eb E F#/Gb G# G#/Ab #/Bb B C",其中最后的C是第一个八度以上的一个八度。
为了区分不同八度音阶的音符之间的差异,在音符名称的末尾附加一个数字(针对这个限制在一个数字上的挑战)。因此,C5是高于C4一个八度的音符。Bb6高于B5。
一个重要的事实是,B5和C6是彼此相邻的音符,而C0和B9是最低音和最高音符。
在任意两个音符之间,有一个距离,即它们之间的半音数。Bb4比B4低一个半个,这本身就是比C5低一个半个。一个八度有12个半音,所以Bb4与A#3的距离是12,因为它是一个八度以上的音阶(注意一个音符最多可以有两个名字)。
挑战
您的挑战是编写尽可能最短的程序,该程序可以从STDIN获取一个音乐音符列表,并打印到STDOUT的间隔更改列表。
输入将是一个空格分隔的音乐音符列表。每个音符将包括一个大写字母and,一个可选的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发布于 2012-04-07 09:19:59
" "/{)12*\{"bC#D EF G A B"?(+}/}%(\{.@-.`\0>{"+"\+}*\}/;]" "*发布于 2012-04-09 10:56:57
基于左撇子的解决方案,并作了一些改进。
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);
}一些我认为值得提及的技巧:
argv[0] (这里称为b)是指向程序名称的指针,但在这里用作划痕缓冲区。我们只需要4个字节(例如C#2\0),所以我们就足够了。c是参数的数量,所以它以1开头(当运行时没有参数)。我们用它来防止第一轮印刷。可能的问题-- c+=b[..c+=..]是一种--很奇怪。我不认为这是未定义的行为,因为?:是一个序列点,但也许我错了。
发布于 2012-04-09 17:52:12
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.对于此脚本,“空格分隔列表”可以在元素之间有一个或多个空格,在开头或结尾有0或多个空格。这个脚本确实在输出结束时打印一个额外的空间,但它也在输入结束时接受额外的空格(或换行符)。
如果我采用一个更严格的定义,即“空格分隔列表”在元素之间只有一个空格,在开始或结束时有0个空格,那么我可以将contents R/ [#-b]+/ all-matching-slices缩短为contents " " split (使用splitting而不是regexp)。但是,我需要添加更多的代码来防止输出结束时的额外空间。
如果我使用不推荐的单词dupd,我可以将over [ - "%+d " printf ] dip缩短为dupd - "%+d " printf,保存8个字符。我不使用反对词,因为它们“打算很快被移除”。
https://codegolf.stackexchange.com/questions/5410
复制相似问题