Чи є шанс написати «С мажор» замість «мажор С»?


39

Я зіткнувся з невеликим естетичним питанням у своєму музичному проекті, і це мене клопоче вже певний час.

У мене є тип, data Key = C | D | ...і я можу побудувати букву Scaleвід a Keyі a Mode. У Modeрозрізняє , наприклад , великий і незначний масштаб.

Я можу визначити Modeтип як функцію від Keyдо Scale. У такому випадку режими матимуть малі імена (що нормально), і я можу отримати такий масштаб

aScale = major C

Але музиканти не говорять так. Вони називають цю шкалу як основну шкалу С , а не основну шкалу С.

Що я хочу

В ідеалі я хотів би написати

aScale = C major

Чи можливо це взагалі?

Що я спробував

Я можу зробити Keyфункцію, яка будує Scaleз а Mode, тож можу писати

aScale = c Major

Але я не можу обмежитися Ключами для побудови Терезів. Вони потрібні і для інших речей (наприклад, для створення акордів ). Також Keyмає бути екземпляр Show.


Я можу поставити Modeпісля, Keyколи використовую додаткову функцію (або конструктор значень):

aScale = scale C major з scale :: Key -> Mode -> Scale

Але додаткова шкала слова виглядає галасливою і всупереч її назви, scaleнасправді не стосується масштабів. Розумна частина є major, scaleнасправді справедлива flip ($).


Використання функції newtype Mode = Major | Minor ...насправді не сильно змінюється, за винятком scaleпотреби бути більш розумним:

aScale = scale C Major

3
Я раніше вважав, що бажаю надзвичайно подібного синтаксису в минулому, але TBH цього не варто. Просто піди major C.
Ліворуч

4
Як музичний каламбур: "Ключ" є оманливою назвою для цього типу даних, оскільки, наприклад, C-мажор і C мінор - це різні клавіші в стандартній термінології. "PitchClass" було б більш точною назвою для типу.
PLL

2
@PLL Дійсно, у мене виникають проблеми з пошуку хорошого імені для C, C #, D ... Я знаю, що Euterpea використовує PitchClass. Це правильніше, ніж Кі, але зовсім не "музичне". Зараз я граю з ідеєю називати його Root або Tonic, хоча це пропонує лише акорди та масштаби. Що, на чорт, музиканти називають цю річ - Ноту без октави?
Мартін Драйтцбург

4
@MartinDrautzburg: Я б не сказав, що крок класу є немузичним - це не просто програмістський розмова будь-якими способами, це було встановлено в теорії музики як сенс «нота без октави», щонайменше приблизно в середині 20 століття. Це не дуже часто за межами технічних музично-теоретичних контекстів, але це лише тому, що точне розмежування між «крок» і «крок без октави» насправді не потрібно часто в повсякденному використанні, і коли це потрібно, зазвичай зрозуміло з контексту. Але "Корінь" або "Тонік" чудово звучать як трохи більш звичні терміни, якщо менш точні.
PLL

1
Ні, тому що це не працює навпаки, програміст, який займається музикою
Emobe

Відповіді:


29

Рішення 1:

Використовуй це

data Mode  = Major | Minor
data Scale = C Mode | D Mode | E Mode | F Mode | G Mode | A Mode | B Mode 

Тепер ви можете писати (з великою літерою С та капіталом М)

aScale = C Major

Рішення 2a:

Це теж можливо

data Mode  = Major | Minor
data Key   = C | D | E | F | G | A | B 

data Scale = Scale Key Mode  

Тепер ти пишеш

aScale = Scale C Major

Рішення 2b:

Це теж можливо

data Mode  = Major | Minor
data Key   = C | D | E | F | G | A | B 

type Scale = (Key, Mode)  

Тепер ти пишеш

aScale = (C, Major)

ІМО із рішенням 2 добре послужить вам. Віддайте синтаксис haskell і зробіть у ньому чисту модель вашого домену. Речі можуть бути приємними, якщо ви робите
luqui

16

Ось одне примхливе рішення, яке я не рекомендую, але виглядає дуже "музично":

infix 8 
(♮) :: Key -> Mode -> Scale
(♮) = (Data.Function.&)
 -- ≡ flip ($)

Тоді можна писати

> C major :: Scale

Звичайно, там , де це дійсно з метою є те , що ви б також мати F♯ minorі B♭ majorт.д ..


1
Цікаво, чи є щось на кшталт нерозривного простору, дозволеного як оператор :)
chepner

26
@chepner насправді так: U + 2800 BRAILLE PATTERN BLANK можна використовувати як інфікс. Потрібно говорити, що це жахлива ідея ... Усі фактичні символи космосу заборонені як інфікси, але Unurode не дивно містить щось, що може бути взято в цілі зловживання.
Ліворуч

11

Якщо ви не заперечуєте проти зайвого оператора, ви можете скористатися &з Data.Function. Припускаючи, що majorце функція Key -> Scale, ви можете написати C & major. Це дає Scaleзначення:

Prelude Data.Function> :t C & major
C & major :: Scale

4

Уже є кілька хороших відповідей, але ось рішення для стилю продовження, яке може бути корисним (можливо, не для цього конкретного прикладу, але в інших контекстах, де потрібен такий собі синтаксис зворотного застосування).

За допомогою стандартних визначень для деяких типів проблемних доменів:

data Mode = Major | Minor                 deriving (Show)
data Key = C | D | E | F | G | A | B      deriving (Show)
data Semitone = Flat | Natural | Sharp    deriving (Show)

data Note = Note Key Semitone             deriving (Show)
data Scale = Scale Note Mode              deriving (Show)
data Chord = Chord [Note]                 deriving (Show)

Ви можете ввести тип продовження:

type Cont a r = (a -> r) -> r

і записати примітивні типи побудови нотаток для створення Contтаких типів:

a, b, c :: Cont Note r
a = mkNote A
b = mkNote B
c = mkNote C
-- etc.
mkNote a f = f $ Note a Natural

flat, natural, sharp :: Note -> Cont Note r
flat    = mkSemi Flat
natural = mkSemi Natural
sharp   = mkSemi Sharp
mkSemi semi (Note k _) f = f $ Note k semi

Тоді функції побудови масштабу, ноти та акорду можуть вирішити Contтипи s до звичайних у будь-якій формі постфікса (тобто, як продовження, що передаються до Cont):

major, minor :: Note -> Scale
major n = Scale n Major
minor n = Scale n Minor

note :: Note -> Note
note = id

або форму префікса (тобто взяття Conts в якості аргументів):

chord :: [Cont Note [Note]] -> Chord
chord = Chord . foldr step []
  where step f acc = f (:acc)

Тепер ви можете написати:

> c sharp note
Note C Sharp
> c note
Note C Natural
> c major
Scale (Note C Natural) Major
> b flat note
Note B Flat
> c sharp major
Scale (Note C Sharp) Major
> chord [a sharp, c]
Chord [Note A Sharp,Note C Natural]

Зауважте, що cсам не має Showпримірника, але є c note.

Змінюючи Noteтип, ви можете легко підтримувати подвійні випадкові випадки (наприклад c sharp sharp, відмінні від d) тощо.


Приємно. Я насправді намагався вирішити свою проблему, Contоднак я намагався приєднати її до конструкторів, A | B | C ...а не використовувати функції. Я не міг змусити це працювати і досі не розумію, чому, враховуючи, що конструктори значень - це лише функції. Якщо я можу дотримуватися функції перед клавішами, багато чого стає можливим. Якщо функція є, flip ($)то я отримую ваш шаблон flip ($) B :: Cont Key r. Мій оригінал aScale = scale C Majorне сильно відрізняється.
Мартін Дройцбург

3

Але я не можу обмежитися Ключами для побудови Терезів. Вони потрібні і для інших речей (наприклад, для створення акордів). Також Key повинен бути екземпляром Show.

Ви можете використовувати майстер-класи, щоб вміло обходити це:

{-# LANGUAGE FlexibleInstances #-}

data Key = C | D | E | F | G | A | B deriving(Show)

data Mode = Major | Minor

data Scale = Scale Key Mode

class UsesKey t where
  c, d, e, f, g, a, b :: t

instance UsesKey Key where
  c = C
  d = D
  e = E
  f = F
  g = G
  a = A
  b = B

instance UsesKey (Mode -> Scale) where
  c = Scale C
  d = Scale D
  e = Scale E
  f = Scale F
  g = Scale G
  a = Scale A
  b = Scale B

aScale :: Scale
aScale = c Major

Тепер ви можете використовувати малі літери і для інших типів, визначаючи відповідні екземпляри.

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