111 / 85 / 21
Регистрация: 06.06.2011
Сообщений: 411
Записей в блоге: 1
1

Продолжаю мучать Writer

05.12.2014, 08:32. Показов 794. Ответов 10
Метки нет (Все метки)

Доброе утро!

Вот опять интересный вопрос по монаде Writer:
Есть ли у нее возможность перед записью, получить свое некое текущее состояние?
Например, в примере ниже, я хочу, чтобы я давал на вход write две координаты, а она записывала не их, и их сумму со всеми предыдущими значениями.
т.е. в итоге я хочу получить, не (1,2), (2,3), (3,4), (4,5), а (1,2), (3,5), (6,9), (10,14)?

Haskell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Control.Monad.Writer
 
data MyState = MyState Int Int
 
type CNC = Writer [(Int, Int)] (MyState)
 
write :: Int -> Int -> CNC
write a b = writer (MyState 1 2, [(a, b)]) --вот тут надо получить текущее значение writera и записать что-то новое
 
export f = 
    mapM_ (putStrLn.show) ff
    where (_, ff) = runWriter f
 
f = do
    write 1 2
    write 2 3
    write 3 4
    write 4 5
 
main = do
    export f
И еще один вопрос: почему нельзя в функцию export написать дополнительный putStr, ведь export имеет тип IO()?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.12.2014, 08:32
Ответы с готовыми решениями:

Стоит ли мучать gtx460?
Здравствуйте. Подскажите стоит ли мучать gtx460 работает лет 5, вчера начались проблемы. Поработал...

Продолжаю предыдущую тему с удалением
Дважды уже ловил синий экран. svchost грузит проц на 70%. В прошлый раз не всё удалилось видимо....

При выключении ПК продолжаю работать кулера на процессоре и БП
С недавних пор при выключении ПК БП и центральный кулер процессора дальше работают, помогает только...

Продолжаю усвоение Ruby и опять яма с Ру язом
1) command =' ' while command != 'пока' puts command command = gets.chomp end puts...

10
_Ivana
05.12.2014, 09:49
  #2

Не по теме:

Вот поэтому я решил связаться с более простой и имхо понятной IORef, чем Writer/State/Reader и т.п. Ссылку на мою вполне удачную попытку уже давал вам в прошлой вашей теме.

0
111 / 85 / 21
Регистрация: 06.06.2011
Сообщений: 411
Записей в блоге: 1
05.12.2014, 12:05  [ТС] 3
Не, для моей задачи нужен не то, чтобы IO(). Просто нужно накапливать результат. Writer для этого - оптимален. Но вот, видать, не совсем. Подумаю еще, может переделаю на State, внутри которого будет Writer.
0
4265 / 2806 / 410
Регистрация: 01.06.2013
Сообщений: 5,892
Записей в блоге: 9
05.12.2014, 13:54 4
Цитата Сообщение от aaleksander Посмотреть сообщение
я хочу, чтобы я давал на вход write две координаты, а она записывала не их, и их сумму со всеми предыдущими значениями
Возможно с помощью Writer-а, но понадобится ещё создать свой экземпляр моноида. И используя tell, а не writer, или, скажем, sin.

_Ivana, у Вас есть шанс понять как это сделать, моноид уже проходили.

Цитата Сообщение от aaleksander Посмотреть сообщение
почему нельзя в функцию export написать дополнительный putStr
Haskell
1
2
3
export f = do
    mapM_ (putStrLn.show) ff
    putStr "..."
Добавлено через 14 минут
Соврамши я насчёт моноида. Так удастся только пару из сумм накопить. А такой, куммулятивный, список нет. Writer для этого не годится.
0
111 / 85 / 21
Регистрация: 06.06.2011
Сообщений: 411
Записей в блоге: 1
05.12.2014, 16:47  [ТС] 5
Получил, что хотел. И предыдущее состояние всегда могу подсмотреть и всю последовательность сохранить.

Haskell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import Control.Monad.State
 
data Command= Plus Int | Minus Int deriving (Show)
 
type CNC = State (Int, [Command]) ()  -- тип состояния - Int, тип результата - список команд
 
isEven n = (mod n 2) == 0 --я все время путаюсь в переводе even/odd, поэтому на удачу :)
--функция изменения состояния (от фонаря)
newState :: Int -> Command -> Int
newState old (Plus new) = if isEven old then old + new else old - new
newState old (Minus new) = if isEven old then old + new else old - new
 
write com = do
    (a, b) <- get                      -- берем из состояния
    put (newState a com, b ++ [com])   -- пересчитывае результат и сохраняем вместе с историей
 
plus :: Int -> CNC
plus n = write $ Plus n
 
minus :: Int -> CNC
minus n = write $ Minus n
 
export :: CNC -> IO()
export f = do
    mapM_ (putStrLn.show) y
    putStr "Total: " 
    putStrLn $ show a
    putStrLn "The end"
    where (_, (a, y)) = runState f (0, [])
 
f:: Int -> CNC
f n = do      -- состояние (для n == 1)
    plus  $ 1*n  -- -1
    plus  $ 2*n  --  1
    minus $ 3*n  --  4
    plus  $ 4*n  --  0
 
f2 = do --можно вызывать пару раз с параметрами
    f 1
    f 2
 
main :: IO()
main = do
    putStrLn "Begin"
    export f2
0
4265 / 2806 / 410
Регистрация: 01.06.2013
Сообщений: 5,892
Записей в блоге: 9
05.12.2014, 17:15 6
Я, таки, написал пример с использованием Writer и моноида, который сохраняет список пар с суммированием предыдущих значений. Вот только моноид получается не удовлетворяющий законам моноида. Но пример работает. Подожду, может кто самостоятельно решит. Потом выложу.

Добавлено через 17 минут
А, с другой стороны. Спрячу-ка под спойлер. Кто захочет сам решить - удержится и не подсмотрит.
Кликните здесь для просмотра всего текста
Haskell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Control.Monad.Writer
import Data.Monoid
 
newtype CumulativeTuple a = CumulativeTuple { getCumulativeTuple :: [(a,a)] }
 
instance Num a => Monoid (CumulativeTuple a) where
        mempty = CumulativeTuple []
        CumulativeTuple [] `mappend` y = y
        CumulativeTuple x `mappend` CumulativeTuple y = 
            let (l,r) = last x in CumulativeTuple $ x ++ map (\(a,b) -> (a+l,b+r)) y
 
type CumulativeTupleWriter = Writer (CumulativeTuple Int) ()
 
write :: Int -> Int -> CumulativeTupleWriter
write a b = tell $ CumulativeTuple [(a,b)]
 
main = mapM_ print $ getCumulativeTuple $ execWriter $ do
    write 1 2
    write 2 3
    write 3 4
    write 4 5
0
4771 / 2231 / 283
Регистрация: 01.03.2013
Сообщений: 5,872
Записей в блоге: 25
06.12.2014, 01:04 7
Хочу конечно сам решить. Вот эту статью http://fprog.ru/2009/issue1/da... heir-uses/ читал неоднократно, но каждый раз как первый раз. Без понимания базовых основ и без практики применения это в лучшем случае выливается в обезьянье копирование "делай так". а в худшем вообще забывается. А в данном примере дело усложняется тем, что я не понял, как надо накапливать координаты в списке - что к чему прибавлять и аккумулировать. Моноид, даже не удовлетворяющий своим условиям, думаю, реализовать смогу.
Haskell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
newtype CBA = CBA [(Int,Int)] deriving (Show)
 
instance Monoid CBA where
    mempty  = CBA []
    CBA l1 `mappend` CBA l2 = CBA $ l1 ++ l2
 
addCoord :: Int -> Int -> Writer CBA Bool
addCoord x y
    | x==0 || y==0 = do
        return True
    | otherwise = do
        tell $ CBA [(x,y)]
        addCoord (x-1) (y-1)
 
main = do
    print $ (\(CBA x) -> x).snd.runWriter $ addCoord 5 10
Добавлено через 22 минуты
Правильно ли я понимаю, что суть Writer - волшебная функция tell, которая работает только с моноидом (и больше ни с чем), используя его mempty и mappend? И нам важна независимость результата от порядка маппендирования, т.к. мы не знаем в каком порядке ленивый кот будет все маппендировать?

Добавлено через 15 минут
И аргумент tell передается первым операндом в mappend, что позволяет писать в нашем примере такое
Haskell
1
2
3
instance Monoid CBA where
    mempty  = CBA []
    CBA [(x,y)] `mappend` CBA l = CBA $ map (\(xl,yl) -> (xl+x,yl+y)) l ++ [(x,y)]
или какой-нибудь другой вариант обработки входящей пары координат - по вкусу. И неверное не важно, что это не моноид поскольку mappend не ассоциативен.
0
4265 / 2806 / 410
Регистрация: 01.06.2013
Сообщений: 5,892
Записей в блоге: 9
06.12.2014, 01:16 8
_Ivana, Моноидом и монадой Writer уже можете пользоваться. Остаётся сделать такой моноид, который требуется в стартовом топике
Цитата Сообщение от aaleksander
я хочу, чтобы я давал на вход write две координаты, а она записывала не их, и их сумму со всеми предыдущими значениями.
т.е. в итоге я хочу получить, не (1,2), (2,3), (3,4), (4,5), а (1,2), (3,5), (6,9), (10,14)?
newtype по сути правильный, но его можно сделать поудобнее (и по традиционее). Посмотрите исходники Data.Monoid. Хотя и с таким можно работать. mempty правильная. Реализация требуемого преобразования списка возможна в mappend.

Цитата Сообщение от _Ivana
Правильно ли я понимаю, что суть Writer - волшебная функция tell, которая работает только с моноидом
У монады [B]Writer//MonadWriter[/QUOTE] первый тип (в нашем случае CBA) - всегда должен быть моноидом. Иначе компилятор не пропустит.
Цитата Сообщение от _Ivana
И нам важна независимость результата от порядка маппендирования, т.к. мы не знаем в каком порядке ленивый кот будет все маппендировать?
Вроде бы никак не должно зависеть от лени, хотя есть ленивая и строгая реализация Control.Monad.Trans.Writer.Lazy и Control.Monad.Trans.Writer.Strict. Но, при реализации mappend надо или посмотреть как внутри работает tell , или (как я сделал), несколько раз методом научного тыка.

Добавлено через 3 минуты
Цитата Сообщение от _Ivana Посмотреть сообщение
CBA [(x,y)] `mappend` CBA l = CBA $ map (\(xl,yl) -> (xl+x,yl+y)) l ++ [(x,y)]
Не-а. В обоих аргументах могут быть списки из произвольного числа элементов. И пустые тоже. И местами их менять не надо. Добавляется правый к левому.
1
4771 / 2231 / 283
Регистрация: 01.03.2013
Сообщений: 5,872
Записей в блоге: 25
06.12.2014, 01:21 9
Цитата Сообщение от KolodeznyDiver Посмотреть сообщение
Не-а. В обоих аргументах могут быть списки из произвольного числа элементов. И пустые тоже.
Ну это я просто демонстрировал свои способности в научном тыке Кстати, пример работает, необязательно реализовывать общий случай маппенда - мы же всегда добавляем только одну пару координат.

А слова ТС что он хочет получить я читал многократно, но их смысл так и не морфировал в область моего понимания. Как мы конкретно добавляем очередную координату к уже имеющимся в списке?
0
4265 / 2806 / 410
Регистрация: 01.06.2013
Сообщений: 5,892
Записей в блоге: 9
06.12.2014, 01:27 10
Цитата Сообщение от _Ivana Посмотреть сообщение
Как мы конкретно добавляем очередную координату к уже имеющимся в списке?
покоординатно складываем добавляемую с предыдущей.
0
4771 / 2231 / 283
Регистрация: 01.03.2013
Сообщений: 5,872
Записей в блоге: 25
06.12.2014, 02:09 11
Второе приближение метода тыка (почитал немного код ТС, сделал предположение) - но маппенд не универсальный
Haskell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
newtype CBA = CBA [(Int,Int)] deriving (Show)
 
instance Monoid CBA where
    mempty  = CBA []
    CBA [(x,y)] `mappend` CBA l = CBA $ (x,y):(map (\(xl,yl) -> (xl+x,yl+y)) l)
 
addCoord :: Writer CBA Bool
addCoord = let write x y = tell $ CBA [(x,y)] in do
    write 1 2
    write 2 3
    write 3 4
    write 4 5
    return True
 
main = do
    print $ (\(CBA x) -> x).snd.runWriter $ addCoord
Добавлено через 36 минут
KolodeznyDiver, наконец не удержался - заглянул под ваш спойлер - и с неким удовлетворением обнаружил очень много знакомого Конечно, жонглирование типами я еще совершенно не освоил, и остальное у вас покрасивше, но общая идея кумулятивного маппенда одна. Хотя, имхо все равно у вас он будет неправильно кумулятивить, если добавлять не одну координату а список из нескольких - вы кумулятивите только хвост добавляемого списка, по хорошему надо его рекурсивно поэлементно добавлять с аккумуляцией на каждом шаге. Но все это неважно, если мы будем пользоваться функцией-оберткой. которая добавляет только по одной точке, в таком случае наши варианты совпадут. А то, что мой код вывалится по ошибке при добавлении более одной точки даже имеет плюс - мы гарантированно видим, что кто-то использовал не нашу обертку для добавления, и вдобавок не получим неверный результат без предупреждения

Добавлено через 1 минуту
ЗЫ не прошло и полгода, а я начинаю понимать суть Writer-а через tell с моноидом Хотя до ощущения полной ясности понимания еще ой как далеко.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
06.12.2014, 02:09

Вопрос по Writer
Хочу, чтобы в Writer'e у меня вот здесь (я выделил красным) всегда стояли те значения полей,...

RFID writer SIC7888
Китайцы прислали RFID writer с прогой для записи SIC7888. Помогите запустить и настроить...

File Reader Writer
Доброго времени суток. Видел некую реализацию работы с файлами. Суть такова. Пользователь выбирает...

Поля страницы документа Writer
Необходимо МАКРОСОМ установить поля страницы для документа Writer. Как? Добавлено через 14 минут...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru