如何定义所有音符名称 .. Cbb Cb C Cs Css .. 作为 Haskell 中的构造函数

问题描述

我在演奏音符名称的目的是不混淆同音等号,即我想正确处理临时记号(升号和降号)。即使 BFs 是钢琴键盘上的同一个键,Gb 上方的完美五分之一的音符必须是 Fs 而不是 Gb .

此外,我还希望能够方便地编写 e.e. Fs 在 haskell 程序中,没有空格、引号或额外的函数@H_502_16@。

我最终定义了 35 个构造函数,范围从 CbbBss。虽然这行得通并且确实得到了临时记号,但我对最多两个临时记号的限制感到不满。在内部,我们无论如何都表示为Int

  • 有没有办法定义无限数量的构造函数,如标题中所示,因此可以使用任意数量的记号(如 Cbbbb)?也许是模板haskell?
  • 或者,我是否可以方便地在 Haskell 程序中编写 Cbbbb没有引号、空格或额外的函数@H_502_16@),而无需将 Cbbbb 设为构造函数

解决方法

我同意 Carsten 的观点,即实际上拥有大量这样的分散构造函数是一个坏主意。使用像

这样的数据更明智
data BaseNote = C | D | E | F | G | A | B
data PitchClass = PitchClass
  { baseNote :: BaseNote,accidentals :: Int }
data Note = Note
  { pitchClass :: PitchClass,octave :: Int }

至于

此外,我还希望能够方便地编写 e.e. Fs 在 haskell 程序中,没有空格、引号或额外的函数。

您有多种选择。

  • 您可以使用 -XPatternSynonyms。这使您可以为已定义的数据类型获得可匹配的构造函数。

    {-# LANGUAGE PatternSynonyms #-}
    pattern Cn = PitchClass C 0
    pattern Cs = PitchClass C 1
    pattern Cb = PitchClass C (-1)
    ...
    

    这些可以由 TemplateHaskell 宏提供以避免代码重复。

  • 您可以提供一个函数,使其看起来像单个构造函数名称一样紧凑,但实际上并非如此。

    (♮),(♯),(♭) :: BaseNote -> Int -> Note
    bn♮octv = Note (PitchClass bn 0) octv
    bn♯octv = Note (PitchClass bn 1) octv
    bn♭octv = Note (PitchClass bn (-1)) octv
    

    现在你可以写类似的东西

    [A♮2,A♮2,C♯3,D♮3,C♯3]
    

TBH 我认为这两个都不是很好。 IMO 更有意义的是,根本不以绝对音高来指定音乐素材,而是将其指定为 音阶度间隔步骤 的序列。