음악 프로젝트에서 약간의 미적 문제가 발생하여 한동안 버그가 발생했습니다.
나는 타입을 가지고 있으며 a 와 a를 data Key = C | D | ...
구성 할 수 있습니다 . 주요 및 사소한 규모를 예 구분합니다.Scale
Key
Mode
Mode
나는 정의 할 수 있습니다 Mode
에서 함수 등의 형태 Key
로를 Scale
. 이 경우 모드는 소문자 이름을 가지며 (괜찮음) 다음과 같이 Scale을 얻을 수 있습니다
aScale = major C
그러나 음악가들은 이렇게 말하지 않습니다. 그들은이 척도를 주요 C 척도가 아닌 C 주요 척도로 지칭합니다 .
내가 원하는 것
이상적으로 쓰고 싶습니다
aScale = C major
이것이 가능합니까?
내가 시도한 것
from Key
을 구성하는 함수를 만들 수 있으므로 쓸 수 있습니다.Scale
Mode
aScale = c Major
그러나 키를 스케일 생성에 국한시킬 수는 없습니다. 다른 것들도 필요합니다 (예 : 화음 구성 ). 또한 Key
의 인스턴스 여야합니다 Show
.
추가 함수 (또는 값 생성자)를 사용할 때 Mode
이후 를 넣을 수 있습니다 Key
.
aScale = scale C major
와 scale :: Key -> Mode -> Scale
그러나 여분의 단어 스케일 은 시끄럽고 그 이름과 반대로 보이지만 scale
실제로 스케일과 관련이 없습니다. 지능형 부분에 major
, scale
정말입니다 flip ($)
.
A는 사용 newtype Mode = Major | Minor ...
을 제외하고 정말 많이 변경되지 않습니다 scale
요구하는 것은 더 지능이 될 수 있습니다 :
aScale = scale C Major
답변
해결책 1 :
이것을 사용하십시오
data Mode = Major | Minor
data Scale = C Mode | D Mode | E Mode | F Mode | G Mode | A Mode | B Mode
이제 대문자 C와 대문자 M을 쓸 수 있습니다.
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)
답변
다음은 실제로 권장하지 않지만 매우 “음악적”인 기발한 솔루션입니다.
infix 8 ♮
(♮) :: Key -> Mode -> Scale
(♮) = (Data.Function.&)
-- ≡ flip ($)
그럼 당신은 쓸 수 있습니다
> C♮ major :: Scale
이 정말 목표로 물론, 당신도 가질 것입니다 F♯ minor
및 B♭ major
등
답변
추가 연산자가 마음에 들지 않으면 &
에서 사용할 수 있습니다 Data.Function
. 그것이 major
함수 라고 가정하면 Key -> Scale
쓸 수 C & major
있습니다. 그 결과 Scale
가치가 있습니다.
Prelude Data.Function> :t C & major
C & major :: Scale
답변
이미 몇 가지 좋은 대답이 있지만 여기에 도움이 될 수있는 연속 전달 스타일 솔루션이 있습니다 (이 특정 예에서는 아니지만 일종의 역 응용 구문이 필요한 다른 상황에서).
일부 문제 도메인 유형에 대한 표준 정의 :
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
또는 접두사 형식 (예 Cont
: s를 인수로 사용) :
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
) 등을 쉽게 지원할 수 있습니다 .
답변
그러나 키를 스케일 구성에 국한시킬 수는 없습니다. 다른 것들도 필요합니다 (예 : 화음 구성). 또한 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
이제 적절한 인스턴스를 정의하여 다른 유형에도 소문자를 사용할 수 있습니다.