음악 작품 내부의 간격 크기를 인쇄하십시오


10

배경

서양 음악에서는 모든 단일 음표에 이름이 지정되어 있습니다. 각 옥타브에는 "CC # / Db DD # / Eb EFF # / Gb GG # / Ab AA # / Bb B C"순서로 12 개의 고유 한 음표가 있습니다. 여기서 마지막 C는 첫 번째 옥타브 위의 1 옥타브입니다.

다른 옥타브의 음을 구별하기 위해 음표 이름 끝에 숫자 (이 숫자는 한 자릿수로 제한됨)가 추가됩니다. 따라서 C5는 C4보다 1 옥타브 높은 음입니다. Bb6은 B5 이상입니다.

중요한 사실은 B5와 C6은 서로 바로 옆에있는 음표이고 C0과 B9는 가장 낮고 가장 높은 음표입니다.

두 음표 사이에는 반음의 수인 거리가 있습니다. Bb4는 B4 아래의 반음이며, 그 자체는 C5 아래의 반음입니다. 한 옥타브에는 12 개의 반음이 있으므로 Bb4는 A # 3보다 12가 떨어져 있습니다 (한 음표의 이름이 두 개까지 될 수 있음).

도전

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하거나 -0또는 0두 개의 동일한 노트?
Howard

@Howard 내가 지정하지 않았으므로 둘 중 하나가 허용됩니다.
PhiNotPi

1
"Bb4는 B4 아래의 하나의 반음이며, 그 자체는 C4 아래의 하나의 반음입니다". 마지막에 C5를 의미합니까?
Keith Randall

와우. 오류를 찾아 주셔서 감사합니다. 이제 수정되었습니다.
PhiNotPi

답변:


6

골프 스크립트, 61

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

4

하스켈, 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

펄, 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 자

일부 개선 사항과 함께 leftaroundabout의 솔루션을 기반으로합니다.

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

@ephemient, 이론적으로 컴파일러가 할 수 있다고 생각 REG=c;REG+=b[..c+=..];c=REG합니다. 그러나 실제로 이와 같은 것을보고 놀랄 것입니다. 그러나 여전히 UB입니다.
ugoren

그것은 코드 골프입니다 – 우리는 이미 scanf프로토 타입없이 UB를 호출했습니다 . 괜찮습니다. 현실에서 무엇이 합법적이지 않은지 아는 것이 좋습니다. :)
ephemient

2

씨, 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


아주 좋아요 몇 가지 제안 : , -> F(*b-65)대신 argv를 남용 합니다 (첫 번째 인수 포인터를 작업 버퍼로 사용하십시오). c-=65;b[1]<36&&++c||b[1]>97&&c--?2:1b[1]&16?1:(c+=b[1]%2*2-1,2)main(e,b,c,d)char*b{
ugoren

다른 하나-나는 c=F(*b)%12대체 될 수 있다고 생각c=-~*b*1.6;c%=12 합니다. 왜? sin원본에서 F9.6으로 대체 할 수 있습니다. c*1.6+9.6이고 (c+6)*1.6, c-=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.

이 스크립트의 경우, "공백으로 구분 된 목록"은 요소 사이에 하나 이상의 공백이있을 수 있고 시작 또는 끝에 0 개 이상의 공백이있을 수 있습니다. 이 스크립트는 출력 종료시 추가 공간을 인쇄하지만 입력 종료시 추가 공간 (또는 줄 바꿈)도 허용합니다.

"공백으로 구분 된 목록"에 요소 사이에 정확히 1 개의 공백이 있고 시작 또는 끝에 0 개의 공백이있는 더 엄격한 정의를 채택하려는 경우 (를 사용 하고 사용 하지 않음 )로 단축 contents R/ [#-b]+/ all-matching-slices할 수 있습니다 . 그러나 출력이 끝날 때 여분의 공간을 막기 위해 더 많은 코드를 추가해야합니다.contents " " splitsplittingregexp

더 이상 사용되지 않는 단어를 사용하면 dupd단축 할 수 있습니다over [ - "%+d " printf ] dip 하여 dupd - "%+d " printf8자를 저장할 . 더 이상 사용되지 않는 단어는 "곧 제거 될 예정"이므로 사용하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.