问题
So my goal for the program is for it to receive an Int matrix for input, and program converts all numbers > 0 to a unique sequential char, while 0's convert into a '_' (doesn't matter, just any character not in the sequence).
eg.
main> matrixGroupings [[0,2,1],[2,2,0],[[0,0,2]]
[["_ab"],["cd_"],["__e"]]
The best I've been able to achieve is
[["_aa"],["aa_"],["__a"]]
using:
matrixGroupings xss = map (map (\x -> if x > 0 then 'a' else '_')) xss
As far as I can tell, the issue I'm having is getting the program to remember what its last value was, so that when the value check is > 0, it picks the next char in line. I can't for the life of me figure out how to do this though.
Any help would be appreciated.
回答1:
Your problem is an instance of an ancient art: labelling of various structures with a stream of labels. It dates back at least to Chris Okasaki, and my favourite treatment is by Jeremy Gibbons.
As you can see from these two examples, there is some variety to the way a structure may be labelled. But in this present case, I suppose the most straightforward way will do. And in Haskell it would be really short. Let us dive in.
The recipe is this:
- Define a polymorphic type for your matrices. It must be such that a matrix of numbers and a matrix of characters are both rightful members.
- Provide an instance of
Traversableclass. It may in many cases be derived automagically. - Pick a monad to your liking. One simple choice is
State. (Actually, that is the only choice I can think of.) - Create an action in this monad that takes a number to a character.
- Traverse a matrix with this action.
Let's cook!
A type may be as simple as this:
newtype Matrix a = Matrix [[a]] deriving ShowIt is entirely possible that the inner lists will be of unequal length — this type does not protect us from making a "ragged" matrix. This is poor design. But I am going to skim over it for now. Haskell provides an endless depth for perfection. This type is good enough for our needs here.
We can immediately define an example of a matrix:
example :: Matrix Int example = Matrix [[0,2,1],[2,2,0],[0,0,2]]How hard is it to define a
Traversable? 0 hard.{-# language DeriveTraversable #-} ... newtype Matrix a = Matrix [[a]] deriving (Show, Functor, Foldable, Traversable)Presto.
Where do we get labels from? It is a side effect. The function reaches somewhere, takes a stream of labels, takes the head, and puts the tail back in the extra-dimensional pocket. A monad that can do this is
State.It works like this:
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 lI hope the code explains itself. When a function "creates" a monadic value, we call it "effectful", or an "action" in a given monad. For instance,
printis an action that, well, prints stuff. Which is an effect.labelis also an action, though in a different monad. Compare and see for youself.Now we are ready to cook a solution:
matrixGroupings m = evalState (traverse label m) ['a'..'z']
This is it.
λ matrixGroupings example
Matrix ["_ab","cd_","__e"]
Bon appetit!
P.S. I took all glory from you, it is unfair. To make things fun again, I challenge you for an exercise: can you define a Traversable instance that labels a matrix in another order — by columns first, then rows?
来源:https://stackoverflow.com/questions/58574605/haskell-assigning-unique-char-to-matrix-values-if-x-0