Роздрукуйте розміри інтервалів всередині музичного твору


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 - це відстань 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або -0або 0два однакових нот?
Говард

@Howard Оскільки я не вказав, прийнятний будь-який.
PhiNotPi

1
"Bb4 - це один семітон нижче B4, який сам по собі є півтоном нижче C4". Ви маєте на увазі С5 наприкінці цього, правда?
Кіт Рендалл

Ого, ніколи цього не помічав. Дякуємо, що виявили помилку. Це зараз виправлено.
PhiNotPi

Відповіді:



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

Перл, 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 символів

Заснований на рішенні навколо рішення, з деякими вдосконаленнями.

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, я думаю, теоретично компілятор міг би зробити REG=c;REG+=b[..c+=..];c=REG. Однак я буду здивований, побачивши щось подібне на практиці. Але це все-таки UB.
угорен

Це 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).
хаммар

Ви можете опустити, включаючи ideone.com/G00fS
Hauleth,

Дуже хороший. Деякі пропозиції: 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{(Використовуйте перший вказівник аргументу як робочий буфер).
угорен

Ще один - я думаю, c=F(*b)%12його можна замінити c=-~*b*1.6;c%=12. Чому? sinв оригіналі Fможе бути замінено на 9.6. c*1.6+9.6є (c+6)*1.6, c-=65і (c+6)стань c-59, і тоді c+1(60 * 96% 12 == 0).
угорен

Дякую за всі пропозиції! Вони прекрасно працюють і скорочують його, але я думаю, я залишу його як зараз; насправді це не було б моїм рішенням без синуса.
перестали повертати проти годинника,

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-slicesйого contents " " split(використовуючи splitting, ні regexp). Однак мені потрібно буде додати більше коду, щоб запобігти додатковий простір в кінці виводу.

Якщо я використовую застаріле слово dupd, я можу скоротити over [ - "%+d " printf ] dipйого dupd - "%+d " printf, зберігаючи 8 символів. Я не використовую застарілі слова, оскільки вони "призначені для швидкого видалення".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.