Haskell: присвоение уникального символа значениям матрицы, если x> 0 - PullRequest
1 голос
/ 26 октября 2019

Таким образом, моя цель для программы - получить матрицу 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 она выбирает следующий символ в строке. Хотя я не могу понять, как это сделать.

Любая помощь будет признательна.

1 Ответ

1 голос
/ 27 октября 2019

Ваша проблема является примером древнего искусства: маркировка различных структур потоком этикеток. Это относится, по крайней мере, к Крису Окасаки , и мое любимое лечение - Джереми Гиббонс .

Как вы можете видеть из этих двух примеров, существует некоторое разнообразиеспособ, которым структура может быть маркирована. Но в данном случае, я полагаю, подойдет самый простой способ. А в Хаскеле это было бы очень коротко. Давайте углубимся.

Рецепт таков:

  • Определите полиморфный тип для ваших матриц. Должно быть так, что матрица чисел и матрица символов являются законными членами.
  • Предоставляет экземпляр класса Traversable. Во многих случаях это может быть получено автоматически.
  • Выберите монаду по своему вкусу. Один простой выбор - State. (На самом деле, это единственный выбор, который я могу придумать.)
  • Создать действие в этой монаде, которое переводит число в символ.
  • Перейдите матрицу с помощью этого действия.

Давайте приготовим!

  • Тип может быть таким простым:

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

    Вполне возможно, что внутренние списки будут иметь неодинаковую длину - этот тип не защищает нас от создания «рваной» матрицы. Это плохой дизайн. Но я собираюсь рассмотреть это сейчас. Haskell обеспечивает бесконечную глубину для совершенства. Этот тип достаточно хорош для наших нужд.

    Мы можем сразу определить пример матрицы:

    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.

  • Откуда мы получаем этикетки? Это побочный эффект. Функция достигает где-то , берет поток меток, берет голову и кладет хвост обратно в каркас. Монада, которая может сделать это: 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
    

    Я надеюсь, что код сам по себе объяснит. Когда функция "создает" монадическое значение, мы называем его "эффектным" или "действием" в данной монаде. Например, print - это действие, которое хорошо печатает материал. Который является эффектом. label также является действием, хотя и в другой монаде. Сравните и убедитесь сами.

  • Теперь мы готовы приготовить решение:

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

Вот и все.

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

Приятного аппетита!

PS Я забрал у тебя всю славу, это несправедливо. Чтобы снова повеселиться, я призываю вас выполнить упражнение: можете ли вы определить экземпляр Traversable, который помечает матрицу в другом порядке - сначала по столбцам, а затем по строкам?

...