使用枚举类型:列出、比较和构建直方图

Working With Enumeration Types: Listing, Comparing, and Building Histograms

我最近完成了(为了自学)一个 homework assignment of some UPenn Haskell Course of building a Mastermind 求解器。

赋值从

的定义开始
data Color = Red | Green |  Blue |  Yellow | Orange |  Purple deriving (Eq, Show)

type Code = [Color]

并涉及枚举类型的各种操作。虽然我已经做到了 complete the assignment,但我觉得我的代码有几个部分没有必要重复或效率低下:

  1. 为了枚举所有长度为n的代码(即所有长度为n的笛卡尔组合Color 值),我使用了

    allColors = [Red, Green, Blue, Yellow, Orange, Purple]
    

    这似乎是重复和脆弱的,因为它只是 Color 的所有可能性。有没有办法单独定义枚举,然后从中构建一个列表?从概念上讲,我想要 allColors = listAll(Color) 之类的东西(问题是 listAll 是什么)。

  2. 由于许多表达式包含 (if l == r then 1 else 0)(其中 lrColor),我最终写了一个boolToInt 函数。当然有一些方法可以比较两个枚举类型并将结果更容易地转换为整数,不是吗?即,我希望 Red == Red 评估为 1,Red == Blue 评估为 0。

  3. 部分解决方案需要从 Code 构建直方图,我使用

    countColor :: Color -> Code -> Int
    countColor _ [] = 0
    countColor c (r:rs) = (boolToInt (c == r)) + countColor c rs
    
    countColors :: Code -> [Int]
    countColors code = [countColor c code | c <- allColors]
    

    这看起来既低效又冗长。有没有更短更有效的方法?

1) 如果我们定义

data Color = Red | Green |  Blue |  Yellow | Orange | Purple
   deriving (Eq, Show, Enum, Bounded, Ord)

然后我们可以使用

> [minBound .. maxBound] :: [Color]
[Red,Green,Blue,Yellow,Orange,Purple]

2) 要将布尔值转换为整数,我们可以使用

> fromEnum True
1
> fromEnum False
0

3) 对于直方图,我们可以从排序和分组开始:

> import Data.List
> map (\xs -> (head xs, length xs)) . group . sort $ [Red, Red, Blue, Red]
[(Red,3),(Blue,1)]

加零留作练习。

使用数组而不是列表可以通过删除 O(log n) 因子来提高性能,但我会避免这种情况,除非我们知道性能在这里真的很重要。

对于 1. 和 3. 请参阅

对于 2。我建议您避免在递归函数中手动添加计数器。在 Haskell 中使用标准的高阶函数通常更为惯用。例如,countColor 只是给你满足谓词的子列表的长度。因此,我将其实现为

countColor c = length . filter (==c)

同样,

exactMatches ls rs = length . filter id $ zipWith (==) ls rs