Haskell:如果 x > 0,则将唯一字符分配给矩阵值

Haskell: Assigning unique char to matrix values if x > 0

所以我的程序目标是让它接收一个 Int 矩阵作为输入,程序将所有 > 0 的数字转换为唯一的顺序字符,而 0 转换为 '_'(无所谓,只是不在序列中的任何字符)。

例如

main> matrixGroupings [[0,2,1],[2,2,0],[[0,0,2]]
[["_ab"],["cd_"],["__e"]]

我能达到的最好成绩是

[["_aa"],["aa_"],["__a"]]

使用:

matrixGroupings xss = map (map (\x -> if x > 0 then 'a' else '_')) xss

据我所知,我遇到的问题是让程序记住它的最后一个值是什么,这样当值检查 > 0 时,它会选择行中的下一个字符。不过我这辈子都想不出怎么做。

如有任何帮助,我们将不胜感激。

你的问题是一个古老艺术的例子:用一连串的 标签。它至少可以追溯到 Chris Okasaki,我最喜欢的治疗方法是 Jeremy 长臂猿.

从这两个示例中可以看出,结构的方式可能有所不同 贴上标签。但在目前的情况下,我想最直接的方法就可以了。而在 Haskell 真的很短。让我们深入探讨。

食谱是这样的:

  • 为您的矩阵定义多态类型。它必须是这样的,一个数字矩阵和一个 字符矩阵都是合法成员。
  • 提供 Traversable class 的实例。在许多情况下,它可能会自动派生。
  • 根据自己的喜好选择一个 monad。一个简单的选择是 State(其实这是我唯一的选择 能想到。)
  • 在此 monad 中创建一个 action,将数字转换为字符。
  • 用这个动作遍历一个矩阵。

我们做饭吧!

  • 一个类型可能就这么简单:

    newtype Matrix a = Matrix [[a]] deriving Show
    

    完全有可能内部列表的长度不等——这种类型不会 保护我们免于制作 "ragged" 矩阵。这是糟糕的设计。但我要略过 现在。 Haskell 提供 endless depth 以求完美。这种类型足够好 我们这里的需求。

    我们可以马上定义一个矩阵的例子:

    example :: Matrix Int
    example = Matrix [[0,2,1],[2,2,0],[0,0,2]]
    
  • 定义一个Traversable有多难? 0 很难。

    {-# language DeriveTraversable #-}
    
    ...
    
    newtype Matrix a = Matrix [[a]] deriving (Show, Functor, Foldable, Traversable)
    

    Presto.

  • 我们从哪里得到标签?这是副作用。函数到达某处,需要一个 标签流,取出头部,然后将尾巴放回 extra-dimensional 口袋中。一种 可以做到这一点的 monad 是 State.

  • 它是这样工作的:

    label :: Int -> State String Char
    label 0 = return '_'
    label x = do
        ls <- get
        case ls of
            [ ] -> error "No more labels!"
            (l: ls') -> do
                put ls'
                return l
    

    我希望代码能自我解释。当一个函数 "creates" 是一个 monadic 值时,我们称它为 "effectful",或给定 monad 中的 "action"。例如,print 是一个动作, 好吧,打印东西。这是一个效果。 label 也是一个动作,虽然在不同的 单子。自己比较看看。

  • 现在我们准备做一道菜:

    matrixGroupings m = evalState (traverse label m) ['a'..'z']
    

就是这个。

λ matrixGroupings example
Matrix ["_ab","cd_","__e"]

祝你胃口大开!

P.S. 我夺走了你所有的荣耀,这不公平。为了让事情再次变得有趣,我挑战你做一个练习:你能否定义一个 Traversable 实例,以另一种顺序标记矩阵——先按列,然后按行?