在Haskell中从循环生成子列表

时间:2015-11-24 16:54:12

标签: haskell cycle

我们说我有一个十二个音符的列表(它们有自己的数据类型),我想要一个函数,它返回一个以给定音符开头并循环的音符列表。

data Note = C | CsDb | D | DsEb | E | F | FsGb | G | GsAb | A | AsBb | B deriving (Read, Eq, Ord, Enum, Bounded)
getNotes :: Note -> [Note]
getNotes root = take 12 $ doSomething root $ cycle noteList
    where noteList :: [Note]
          noteList = [minBound..maxBound]

这样

ghci> getNotes E
[E, F, FsGb, G, GsAb, A, AsBb, B, C, CsDb, D, DsEb] 

我可以想到一些草率的方法来做到这一点,但感觉应该有一个明显的,非常Haskellian的方式。有什么建议吗?

4 个答案:

答案 0 :(得分:2)

我只是

getNotes root = [root .. maxBound] ++ init [minBound .. root]

但我可以看到你更喜欢循环方法。 <怎么样

getNotes root = map snd . take 12 $ [(0,root) .. ]

......遗憾的是,这实际上并不起作用:它需要一个(Enum a, Enum b, Bounded b) => Enum (a,b)个实例,由于某些原因这个实例没有定义,至少不在前奏中。

或者,您可以使用root的索引:

getNotes root = take 12 . drop (fromEnum root) $ cycle [minBound .. maxBound]

答案 1 :(得分:1)

根据@leftaroundabout的第二个想法,这是一个有效的版本 - 以防你好奇并想玩它:

{-# LANGUAGE ScopedTypeVariables #-}

module Stackoverflow where

data Note = C | CsDb | D | DsEb | E | F | FsGb | G | GsAb | A | AsBb | B
          deriving (Show, Enum, Bounded)

instance (Enum a, Enum b, Bounded b) => Enum (a,b) where
  toEnum i =
    let (d,m) = i `divMod` (fromEnum (maxBound :: b) + 1)
    in (toEnum d, toEnum m)
  fromEnum (a, b) = fromEnum a * (fromEnum (maxBound :: b) + 1) + fromEnum b

getNotes :: Note -> [Note]
getNotes root = map snd . take 12 $ [(0,root) .. ]

示例:

λ> getNotes E
[E,F,FsGb,G,GsAb,A,AsBb,B,C,CsDb,D,DsEb]
PS:这个想法非常聪明@leftaroundabout&lt; - 所以大家一定要给他很多赞成票;)

答案 2 :(得分:1)

您可以做的最小变化是使用dropWhile

getNotes :: Note -> [Note]
getNotes root = take 12 . dropWhile (/= root) . cycle $ [minBound .. maxBound]

答案 3 :(得分:0)

怎么样

getNotes :: Note -> [Note]
getNotes root = ys ++ xs where (xs,ys) = break (==root) [minBound..maxBound]

?这与@ leftaroundabout的第一个建议大致相同,避免init,但会产生一些相等的比较: - )