Как обновить элементы на карте Haskell? - PullRequest
0 голосов
/ 04 июля 2018

Я новичок в Хаскеле и пытаюсь найти разумного способ записи в карты (при подготовке к решению конкретного Проблема проекта Эйлера)

Я надеюсь написать функцию, которая будет заполнять Карта с записью. Но я не могу заставить его работать.

let, кажется, создает локальные переменные вместо
рассматривая smap как глобальный.

Должен быть какой-то способ сделать это.

Мой код:

import Data.Map (Map)
import qualified Data.Map as Map 

smap = Map.fromList [("cocoa",23)]


newdata str n = do  
   let cpy  = Map.insert str n  smap
   cpy 

main = do
     let smap = newdata "pennywise" 16  
     let smap = newdata "krusty" 18  

обновление по комментариям: Позже я хочу посчитать, сколько углов прямоугольного треугольника равно периметру. Поэтому я подумал, что карта будет хорошим способом для хранения счетчиков, например. p10 -> 5 способов, p15 -> 6 способов и т. д. Таким образом, при запуске программы будут увеличиваться значения уже обнаруженных периметров.

Ответы [ 2 ]

0 голосов
/ 08 июля 2018

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

import Control.Lens
import Data.Map
import Control.Monad.State

example :: State (Map String Int) Int
example = do
    -- set value
    at "pennywise" ?= 16
    at "krusty" ?= 18
    -- get value
    Just krusty <- use $ at "krusty"
    pure krusty

main :: IO ()
main = do
    let r = evalState example empty
    print r

Объектив предоставляет общий интерфейс для работы со структурами данных, такими как Map, поэтому мне это нравится.

Другой пример:

countString :: String -> Map Char Int
countString str = flip execState empty $
    forM_ str $ \c -> 
        at c %= Just . maybe 1 (+1)

-- countString "asasdsas"
-- fromList [('a',3),('d',1),('s',4)]
0 голосов
/ 04 июля 2018

Вы не можете изменить Map на месте (поскольку Haskell - чисто функциональный язык), но вы можете создать новый, который почти равен старой карте, за исключением нескольких измененных записей.

(Не беспокойтесь об эффективности: нелогично, новый Map не требует полной копии старого.)

Например, предположим, что мы хотим посчитать частоты каждого символа в строке. Давайте напишем функцию, которая, учитывая char c, увеличивает его количество, хранящееся в Map

import qualified Data.Map.Strict as M

countChar :: Char -> M.Map Char Int -> M.Map Char Int
countChar c oldMap = newMap
   where
   newMap = M.insertWith (+) c 1 oldMap

Переменная newMap не нужна, она показана выше для ясности.

Функция insertWith создает новую карту таким образом, чтобы при индексе c она сохраняла 1, если на старой карте нет значения, или 1 + x, если на старой карте есть предыдущее значение x. .

Для обработки полной строки мы используем рекурсию:

countString :: String -> M.Map Char Int
countString ""     = M.empty
countString (c:cs) = countChar c (countString cs)

Небольшой тест в GHCi:

> countString "here's an example"
fromList [(' ',2),('\'',1),('a',2),('e',4),('h',1),('l',1),('m',1)
         ,('n',1),('p',1),('r',1),('s',1),('x',1)]

Для более продвинутого решения, countString также может быть переписано как сгиб, если требуется. Использование левого строгого сгиба также улучшило бы эффективность.

countString = foldl' (flip countChar) M.empty

Можно даже использовать государственную монаду, чтобы избежать обхода Map. Если вы изучаете Haskell, не беспокойтесь об этом и начните с изучения способов решения таких задач с помощью рекурсии, сопоставления с образцом и нескольких библиотечных функций для Map s.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...