Czy jest jakaś szansa na napisanie „C-dur” zamiast „major C”?


39

W moim projekcie muzycznym napotkałem niewielki problem estetyczny i od pewnego czasu mnie to denerwuje.

Mam typ data Key = C | D | ...i mogę zbudować Scalea Keyi a Mode. W Moderozróżnia np główną i mniejszą skalę.

Mogę zdefiniować Modetyp jako funkcję od Keydo Scale. W takim przypadku tryby będą miały małe litery (co jest w porządku) i mogę uzyskać taką skalę

aScale = major C

Ale muzycy tak nie mówią. Odnoszą się do tej skali jako do skali C-dur , a nie do skali C-dur .

Czego chcę

Idealnie chciałbym pisać

aScale = C major

Czy to w ogóle jest możliwe?

Co próbowałem

Mogę stworzyć Keyfunkcję, która konstruuje Scalez a Mode, więc mogę pisać

aScale = c Major

Ale nie mogę ograniczyć kluczy do budowania Łusków. Są one również potrzebne do innych rzeczy (np. Do tworzenia akordów ). KeyPowinien też być przypadek Show.


Mogę umieścić Modepo, Keykiedy używam dodatkowej funkcji (lub konstruktora wartości):

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

Ale dodatkowa skala słów wygląda głośno i wbrew nazwie, scaletak naprawdę nie dotyczy skal. Inteligentna część jest w środku major, scalejest naprawdę sprawiedliwa flip ($).


Używanie newtype Mode = Major | Minor ...naprawdę niewiele się zmienia, poza tym scalemusi być bardziej inteligentny:

aScale = scale C Major

3
W przeszłości sam chciałem mieć bardzo podobną składnię, ale TBH nie jest tego warte. Po prostu idź z major C.
lewo około

4
Podobnie jak muzyczny spór: „klucz” jest mylącą nazwą dla tego typu danych, ponieważ np. C-dur i C-moll to różne klucze w standardowej terminologii. „PitchClass” byłoby dokładniejszą nazwą tego typu.
PLL

2
@PLL Rzeczywiście mam problem ze znalezieniem dobrego imienia dla C, C #, D ... Wiem, że Euterpea używa PitchClass. Jest bardziej poprawny niż Key, ale wcale nie „muzyczny”. W tej chwili bawię się pomysłem nazywania go Root lub Tonic, ale to sugeruje tylko akordy i skale. Jak do diabła nazywają to muzycy - Notatka bez Oktawy?
Martin Drautzburg

4
@MartinDrautzburg: Nie powiedziałbym, że klasa dźwiękowa jest niemuzyczna - w żadnym wypadku nie jest to tylko programista, ale w teorii muzyki został uznany za „nutę bez oktawy” przynajmniej od połowy XX wieku. Nie jest to zbyt często poza technicznymi kontekstami teorii muzyki, ale dzieje się tak tylko dlatego, że dokładne rozróżnienie między „wysokością” a „wysokością bez oktawy” nie jest tak naprawdę często potrzebne w codziennym użyciu, a kiedy jest potrzebne, jest zwykle jasne z kontekstu. Ale „Root” lub „Tonic” brzmią dobrze jako nieco bardziej znane terminy, jeśli mniej precyzyjne.
PLL

1
Nie, ponieważ nie działa odwrotnie, programista zajmujący się muzyką
Emobe

Odpowiedzi:


29

Rozwiązanie 1:

Użyj tego

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

Teraz możesz pisać (z dużą literą C i wielką literą M)

aScale = C Major

Rozwiązanie 2a:

Jest to również możliwe

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

data Scale = Scale Key Mode  

Teraz piszesz

aScale = Scale C Major

Rozwiązanie 2b:

Jest to również możliwe

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

type Scale = (Key, Mode)  

Teraz piszesz

aScale = (C, Major)

IMO, wybierając rozwiązanie 2, będzie ci dobrze służyć. Poddaj się składni haskell i uczyń z niej czysty model swojej domeny. Można to zrobić przyjemnie
luqui

16

Oto jedno kapryśne rozwiązanie, którego tak naprawdę nie polecam, ale wygląda bardzo „muzykalnie”:

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

Potem możesz pisać

> C major :: Scale

Oczywiście, tam gdzie to jest naprawdę celowane jest to, że Ty też miałbyś F♯ minori B♭ majoritp.


1
Zastanawiam się, czy jest coś takiego jak
niezniszczalne

26
@chepner faktycznie tak: U + 2800 BRAILLE PATTERN BLANK może być użyty jako infix. Nie trzeba dodawać, że to okropny pomysł ... Wszystkie rzeczywiste znaki spacji są zabronione jako poprawki, ale nic dziwnego, że Unicode zawiera coś, co można zhakować w celu nadużycia.
lewo około

11

Jeśli nie przeszkadza ci dodatkowy operator, możesz skorzystać &z Data.Function. Zakładając, że majorjest to funkcja Key -> Scale, możesz pisać C & major. To daje Scalewartość:

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

4

Istnieje już kilka dobrych odpowiedzi, ale oto rozwiązanie w stylu przechodzenia kontynuacji, które może być pomocne (może nie w tym konkretnym przykładzie, ale w innych kontekstach, w których pożądana jest pewnego rodzaju składnia aplikacji odwrotnej).

Ze standardowymi definicjami dla niektórych typów domen problemowych:

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)

możesz wprowadzić ciąg dalszy:

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

i napisz prymitywne typy budowania notatek, aby budować Conttakie typy:

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

Następnie funkcje budowania skali, nuty i akordu mogą przekształcić Conts na typy zwykłe w dowolnej postaci pofiksowej (tj. Jako kontynuacje, które zostaną przekazane do Cont):

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

note :: Note -> Note
note = id

lub forma prefiksu (tj. przyjmowanie Conts jako argumentu):

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

Teraz możesz napisać:

> 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]

Zauważ, że csama nie ma Showinstancji, ale c notema.

Dzięki modyfikacji Notetypu można łatwo obsługiwać podwójne zdarzenia (np. c sharp sharpOdróżnić od d) itp.


Miły. Naprawdę próbowałem rozwiązać problem Cont, próbowałem jednak przykleić go do konstruktorów A | B | C ...zamiast używać funkcji. Nie mogłem tego uruchomić i nadal nie rozumiem dlaczego, biorąc pod uwagę, że konstruktory wartości są tylko funkcjami. Jeśli uda mi się nakleić funkcję przed klawiszami, wiele rzeczy stanie się możliwe. Jeśli funkcja jest, flip ($)to dostaję twój wzór flip ($) B :: Cont Key r. Mój oryginał aScale = scale C Majornie różni się zbytnio.
Martin Drautzburg

3

Ale nie mogę ograniczyć kluczy do budowania Łusków. Są one również potrzebne do innych rzeczy (np. Do tworzenia akordów). Klucz powinien być również instancją programu Show.

Możesz użyć klas, aby sprytnie obejść to:

{-# 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

Teraz możesz używać małych liter także dla innych typów, definiując odpowiednie wystąpienia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.