首页 > 解决方案 > 如何定义所有音符名称.. Cbb Cb C Cs Css .. 作为 Haskell 中的构造函数

问题描述

我正在演奏音符名称,其目标是不混淆等音等号,即我想让临时记号(升号和降号)正确。音符上方完美五度的音符B必须是Fs和 不是Gb,即使FsGb是钢琴键盘上的同一个键。

此外,我希望Fs在 haskell 程序中方便地编写 ee,无需空格、引号或额外的函数

我最终定义了 35 个构造函数,范围CbbBss. 虽然这很有效,并且确实使临时记号正确,但我对最多两个临时记号的限制感到不满。Int在内部,无论如何我们表示为s 的变音记号。

标签: haskelltemplate-haskell

解决方案


我同意 Carsten 的观点,实际上有很多这样的分散构造函数是个坏主意。使用像这样的数据更明智

data BaseNote = C | D | E | F | G | A | B
data PitchClass = PitchClass
  { baseNote :: BaseNote
  , accidentals :: Int }
data Note = Note
  { pitchClass :: PitchClass
  , octave :: Int }

至于

此外,我希望Fs在 haskell 程序中方便地编写 ee,无需空格、引号或额外的函数。

你有多种选择。

  • 你可以使用-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, C♯3, D♮3, D♮3, C♯3]
    

TBH 我不认为这两个真的很好。IMO 更有意义的是,根本不以绝对音高来指定音乐素材,而是以音阶音程步长的序列来指定音乐素材。


推荐阅读