Організуйте музику григоріанської церкви


19

Рік 930, і у Григоріанської церкви виникають проблеми. У них тисячі сторінок співочої музики, але проблема полягає в тому, що всю ноту просто кинули на купу, замість того, щоб мати реальну організаційну систему:

Зображення нот
Зображення користувача gamerprinter у Гільдії картографів .

Церкві потрібно організувати всі ноти, тому вони найняли середньовічного інженера-програміста, щоб написати програму, щоб організувати її для них. Ви - інженер програмного забезпечення, якого найняли. Однак процес складання в середньовічні часи передбачає написання програми на папері командою повільних біблійних книжників. Щоб зменшити час, необхідний команді переписувачів для складання вашого коду, ви повинні зробити програму якомога меншою.

Церква хоче, щоб музика пісень була організована на основі музичного масштабу, про який вони написані. Вся музика церкви, що співає, написана в дорійських масштабах . Враховуючи ноти певного музичного твору, ваша програма виводить масштаб Доріана, який він є. Тут я поясню, що таке масштаб Доріана. Якщо ви вже знаєте, ви можете пропустити цей розділ.

У будь-якій мелодії 12 можливих нот. Ось вони в порядку:

C C# D D# E F F# G G# A A# B

Півтони (представлено з використанням S) є приріст на один крок вправо, обтікання (так півтон вгору від B буде назад в C). Сигнал (представлений з використанням T) два півтони. Наприклад, півтоном вгору від F # буде G. Будь-тоном від F # буде G #.

Щоб створити доріанську шкалу, ми починаємо з будь-якої ноти у списку, а потім рухаємося вгору за наступним шаблоном, перелічуючи нотатки, які ми зустрічаємо:

T, S, T, T, T, S

Приклад. Я починаю з А. Нотатки моєї доріанської шкали:

A
B  (up a tone)
C  (up a semitone)
D  (up a tone)
E  (up a tone)
F# (up a tone)
G  (up a semitone)

Шкала має ноти A, B, C, D, E, F # і G. Так як я виходив з А, ми називаємо це Доріан масштабу в . Тому існує 12 різних масштабів Доріану, кожна з яких названа на честь ноти, з якої вони почали. Кожен з них використовує однаковий візерунок тонів і півтонів, тільки починаючи з іншого положення. Якщо моє пояснення не є узгодженим, ви можете також звернутися до Вікіпедії .

Введення програми може бути надано з усього, що підходить для вашої програми (наприклад, STDIN, аргумент командного рядка raw_input()). Він може не бути попередньо ініціалізований у змінній. Введенням буде список відокремлених комами нот, що представляють мелодію твору. Можуть бути повторні нотатки. У вкладі завжди буде достатньо різних приміток, щоб можна було рішуче вивести масштаб твору. Приклад введення:

B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Виходом програми має бути рядок Dorian scale in X, де X - початкова нота шкали. Вихід з прикладу вводу:

Dorian scale in B

Порівнюючи це зі шкалою Доріана в B ( B C# D E F# G# A), ми бачимо, що всі ноти мелодії знаходяться в межах цієї шкали. Примітка C # не використовується в цьому випадку. Однак є достатньо приміток, щоб однозначно визначити Б Доріана як правильний ключ. Жодна інша шкала Доріана не підходить, оскільки яку б іншу шкалу ми не пробували, завжди є хоча б одна нота мелодії, яка не належить до шкали.

Це кодовий гольф, тому виграє запис із найменшою кількістю символів. Запитуйте в коментарях, якщо у вас є питання.


Отже, що ми повинні зробити - це лише інтерпретувати перший тон / півтон?
avall

@Avall Вибачте, я не розумію вашого питання. Введення не завжди починається з тоніка, якщо це те, що ви запитуєте.
абсент

Надайте нам більше прикладів. Особливо ті, які не починаються з тоніку.
avall

1
Зворотною проблемою є Шкала від ключа та режиму
Пітер Тейлор

1
@David Відповідно до цього мета-питання , я прийняв прийняття найкоротшої відповіді після періоду очікування 12 днів з моменту початку завдання. Просто сталося, що відповідь CJam була розміщена прямо, коли я збирався прийняти наступний найкоротший.
абсент

Відповіді:



8

С, 171 146

i,b;main(int c,char**v){for(;c=v[1][i];)b|=c/65<<c*2%7+v[1][++i]%2*7;for(i=12;i--;)b&(1016056>>i)||printf("Dorian scale in %c%c",65+i*3%7,(i<5)*35);}

Розбивати рядки в C не так просто, тому я пішов на більш математичний підхід.

Я скористаюсь Колом П’ятих. Якщо ми упорядковуємо нотатки в наступному порядку на основі підрахунку 7 семітонів одночасно (відомих як "п'ята"), ми виявляємо, що всі нотатки, дозволені в будь-якій заданій шкалі, утворюють послідовний блок із 7 нот і всіх заборонених нот формують послідовний блок із 5 нот.

F C G D A E B F# C# G# D# A#

(це коло, він обертається назад до Fкінця.)

Позиція природної ноти у наведеній вище послідовності може бути обчислена як (ASCII code) * 2 % 7. Тоді, якщо наступний символ є непарним (стосується, #але не кома, пробіл або нульовий байт), ми додаємо 7, щоб зробити його різким. Ми зберігаємо растрові карти приміток, які були використані.

Число 243(двійкове 11111000) відповідає приміткам, забороненим у масштабі A # Dorian. Я помножив це на те, (1<<12)+1=4097щоб дати магічне число 1016056. Це право змінюється, щоб перевірити (за допомогою ANDing), чи мелодія містить заборонені ноти для кожної з 12 шкал по черзі. Якщо мелодія не містить заборонених нот, масштаб друкується.

Для виводу нам потрібно надрукувати ім'я масштабу, закодоване у зворотному порядку, щоб перейти до п'ятих вище, пам’ятайте, що ми йдемо назад, тому що ми змінюємо права.) Послідовність ASCII ADGCFBEADGCFгенерується 65+i*3%7. Для перших п'яти з них слід також надрукувати гострий.

Невикористаний код

i,b;
main(int c,char**v){
  for(;c=v[1][i];)                          //for each character in first commanline argument v[1]
                                               //if it is a letter (assume uppercase, ASCII 65 or over)
   b|=c/65<<c*2%7+v[1][++i]%2*7;               //convert to position in the circle of fifths. 
                                               //Add 7 if the next character is odd (ASCII'#')
                                               //leftshift 1 by this number and OR this with the contents of b.

  for(i=12;i--;)b&(1016056>>i)||printf         //if melody includes no prohibited notes for the scale i, print
   ("Dorian scale in %c%c",65+i*3%7,(i<5)*35); //the scale letter, and a # (ASCII 35) if required, otherwise an ASCII 0.
}

Неправильна поведінка введення: Якщо подано недостатньо приміток, щоб однозначно визначити масштаб, воно виведе всі можливі масштаби. Якщо подано неможливе поєднання нотаток, воно не видасть нічого. Примітки повинні бути обмежені комою (або іншим символом без пробілу з рівним кодом ASCII <= 64.) Пробіли не можна використовувати, оскільки все після першого пробілу вважатиметься іншим аргументом. Коди ASCII> 64 інтерпретуються як примітки описаним чином.


Мене шокувало, що коло п'ятих має цю властивість! Можливо, я можу трохи більше використати його для гольфу.
Рей

1
@Ray Це насправді, тому ми маємо набір нотаток, які ми маємо. Октава має відношення частоти 2: 1. П'ятий, визначений Піфагором, має співвідношення 3: 2 і є найважливішим музичним інтервалом після октави. Оскільки 1,5 ^ 12 близький, але не дорівнює 2 ^ 7, сучасну рівну загартовану п’яту видавлюють до 1.4983, так що рівно 12 п’ятіх розміщуються в 7 октавах. Старомодним рішенням було використовувати лише 7 нот із доступних 12 із кола. Ось чому ми маємо шкалу на основі 7 нерівномірно розташованих нот. Це не якась випадкова умова, за нею є якась суцільна математика.
Рівень р. Св.

Існує ряд інструментів, які упорядковують ноти в п'ятих для зручності (скрипка налаштована таким чином, а бас-гітара налаштована на четверті, що є співвідношенням 4: 3). Найяскравіший приклад (і єдиний мені відомий інструмент, який має ноти, викладені в коло п'ятих для гарного акустичного дизайну) - це steelpan: google.es/patents/US7696421 . З цим макетом не має значення, якщо нота поруч із тією, яку ви б'єте, дзвонить трохи.
Рівень р. Св.

4

Хаскелл - 152

w=words
n=w"C C# D D# E F F# G G# A A# B"
f s="Dorian scale in "++[n!!i|i<-[0..11],all(`elem`[(n++n)!!(i+j)|j<-[0,2,3,5,7,9,10]])s]!!0
main=interact$f.w

Безумовно

type Note = String
type Scale = [Note]

notes :: [Note]
notes = words "C C# D D# E F F# G G# A A# B"

isScale :: Scale -> [Note] -> Bool
isScale scale notes = all (`elem` scale) notes

takeScale :: Int -> Scale
takeScale i = [(notes ++ notes) !! (i + j) | j <- [0, 2, 3, 5, 7, 9, 10]]

findScale :: [Note] -> Note
findScale xs = head [notes !! i | i <- [0..11], isScale (takeScale i) xs]

main = interact (("Dorian scale in "++) . findScale . words)

3

Python 2 - 177 символів

Це не так вже й коротко, але я вважаю радістю Python писати кілька вкладених для циклів в один рядок, навіть коли не займається гольфом. На жаль, мені довелося поставити вхідний оператор в окремий рядок, щоб він не виконувався більше одного разу.

j=set(raw_input().split(','))
print"Dorian Scale in",[x for x in[["A A# B C C# D D# E F F# G G#".split()[(b+n)%12]for n in[0,2,3,5,7,9,10]]for b in range(12)]if j<set(x)][0][0]

Я не використовую Python 3, але я вважаю, що це рідкісний випадок, коли заяві для друку не потрібно більше символів. Оскільки printфункція є, я міг би компенсувати потребу в дужках із використанням *оператора розпакування списку для заміни останнього [0].


2
Ви б також бути в змозі замінити inputна raw_inputі зберегти 4 -х символів в Python 3.
comperendinous

"Я вважаю радістю Python писати кілька вкладених для циклів в одному рядку": але чи знаходите ви радість читати їх?
Калеб Пол

@Wideshanks Звичайно, ні ... це все про код лише для запису!
feersum

3

Рубін - 132

12.times{|i|$*[0].split(?,)-(g=(0..6).map{|j|%w{C C# D D# E F F# G G# A A# B}[-i+=~(58>>j&1)]})==[]?(puts"Dorain scale in "+g[0]):g}

Введення з аргументів командного рядка.
напрruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Спробуйте за адресою: ideone


3

Хаскелл - 140

Скористайтеся властивістю Circle of Fifths, запровадженою @steveverrill. Якщо ми дозволимо circle0 = words "C G D A E B F# C# G# D# A# F"і circle = circle0 ++ circle0, ми можемо побудувати всі масштаби, зробивши 7 послідовних нот circle.

scales = [take 7 . drop i $ circle | i <- [0..11]]

У кожному побудованому таким чином scale !! 3масштабі 4-й елемент - це назва шкали.

Код

w=words
n=w"C G D A E B F# C# G# D# A# F"
f s="Dorian scale in "++[x!!3|x<-[take 7.drop i$n++n|i<-[0..]],all(`elem`x)s]!!0
main=interact$f.w

Безумовно

type Note = String
type Scale = [Note]

notes :: [Note]
notes = words "C G D A E B F# C# G# D# A# F"

scales :: [Scale]
scales = [take 7 . drop i $ notes ++ notes | i <- [0..11]]

findScale :: [Note] -> Note
findScale xs = head [scale !! 3 | scale <- scales, all (`elem` scale) xs]

main = interact (("Dorian scale in "++) . findScale . words)

2

Scala, 130 128 127

print("Dorian scale in "+(".#?".r findAllIn "FCGDAEBF#C#G#D#A#"*2 sliding(7)find{l=>args(0)split','forall(l contains _)}get 3))

Використовуючи метод кола п'ятих. Введення з аргументів командного рядка, тобто

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