LINUX.ORG.RU

[haskell][создать хитрую монаду]

 , monad


0

1

Привет лор! решаю тут задачку про отмеривание определенного количества жидкости с помощью дух сосудов разных объемов.

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

Вот быдлокод того что уже есть

module Main where

import Control.Monad.Trans.RWS
import Control.Monad.RWS.Class
import Data.Monoid
import Control.Arrow ((&&&))

data Bottle a = Bottle {getSize :: a,
                        getMax :: a} deriving Show

getFree :: (Num a) => Bottle a -> a
getFree b = (getMax b) - (getSize b)

emptyBottle :: (Num a) => Bottle a -> Bottle a
emptyBottle (Bottle {getSize = _, getMax = m}) = Bottle {getSize = 0, getMax = m}

fillBottle :: (Ord a, Num a) => a -> Bottle a -> Bottle a
fillBottle am (Bottle {getSize = s, getMax = m}) = Bottle {getSize = max 0 $ min m $ am + s, getMax = m}

data Action a = Flow {getFrom :: Bottle a,
                      getTo :: Bottle a,
                      getAmount :: a}
              | Fill {getWhat :: Bottle a,
                      getAmount :: a}
              | Empty {getWhat :: Bottle a,
                       getAmount :: a} deriving Show
                      

type SolveMonad m a ret = RWST () (Endo [Action a]) (Bottle a, Bottle a) m ret

endotell :: (Monad m) => [Action a] -> SolveMonad m a ()
endotell a = tell $ Endo $ (++ a)

     
leftToRight, rightToLeft :: (Monad m, Num a, Ord a) => SolveMonad m a ()
leftToRight = do
  (l, r) <- get
  if (getFree r) >= (getSize l)
    then do endotell [(Flow {getFrom = l, getTo = r, getAmount = (getSize l)})]
            put (emptyBottle l, fillBottle (getSize l) r)
    else do endotell [(Flow {getFrom = l, getTo = r, getAmount = (getFree r)})]
            put (fillBottle (negate $ getFree r) l, fillBottle (getSize l) r)

execSwapped :: (Monad m) => SolveMonad m a () -> SolveMonad m a ()
execSwapped mex = do
  modify (snd &&& fst)
  mex
  modify (snd &&& fst)

rightToLeft = execSwapped leftToRight

fillLeft, fillRight :: (Monad m, Num a, Ord a) => SolveMonad m a ()
fillLeft = do
  (l, r) <- get
  put (fillBottle (getFree l) l, r)

fillRight = execSwapped fillLeft

emptyLeft, emptyRight :: (Monad m, Num a) => SolveMonad m a ()
emptyLeft = do
  (l, r) <- get
  put (emptyBottle l, r)
emptyRight = execSwapped emptyLeft

Собственно тут описаны только действия которые можно совершать на емкостями, перелить из одной в другую пока в одной не кончится либо другая не наполниться, опустошить либо наполнить одну из бутылок.

Хочу теперь монаду, которая похожа на RWS но совершает действия НАД моей RWS, то есть чтобы оператор >>= в этой монаде выполнял дополнительную проверку на то, что в одном из сосудов нужное нам количество жидкости (то есть проверял состояние) и останавливал вычисление как нибудь, то есть хочу вот такое

magic :: SomeMagicType
magic = do
  lift fillLeft
  lift leftToRight
  (l, r) <- get
  if (isFull r) then do
    lift emptyRight
    else return ()               -- ну как то так, типа то еще не знаю точного
  magic

а потом вычислять это нечто как то вот так

(runMagic magic) (\(l, r) -> r == 3 || l == 3)

и получить свою RWS монаду в которую потом подставить окружение и состоятие, но как такое сделать ? Готовую монаду проверки состояния не нагуглил чето. Или может это инчаче делается ?

Ответ на: комментарий от netcat

Да ладно, просто синтаксис чуток непохож на остальные, особенно из за кастомных бинарных операторов да передачу через пробел без скобок: func param1 param2.

theos ★★★
()
Ответ на: комментарий от s9gf4ult

Причина ?

потому что берем ц-шарп и решаем не используя страшного слова «монада». Вот что такое эта ваша монада, и зачем её применять если можно не применять?

RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

Потому что берем монаду и решаем не используя страшного слова ц-шарп. Вот что такое этот ваш ц-шарп и зачем его применять?

Толсто же.

theos ★★★
()
Ответ на: комментарий от s9gf4ult

кажеться

Всякий раз, когда ты сомневаешься надо ли писать мягкий знак, задай к слову вопрос «что делать\делает\делают\etc». Если в вопросе нет мягкого знака, то в слове его тоже нет. Например, что делает? - Кажется.
Насчет хаскелла - я сейчас учу его с помощью http://learnyouahaskell.com/ Там функции нужно создавать в отдельных файлах, а потом уже в интерпретаторе их подключать и использовать. Если в интерпретаторе создать функцию, то он выдаст 'parse error'. Как можно это обойти?
Пример:

[inish777@laptop]$ ghci
GHCi, version 7.0.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> sqare_root x = x * x

<interactive>:1:14: parse error on input `='
Prelude> 

netcat ★★
()
Ответ на: комментарий от RedPossum

Вот я беру C и решаю всё без делегатов, итераторов, исключений, интерфейсов и прочих страшных слов. Вот на кой хрен эти ваши интерфейсы и делегаты и зачем их применять, если можно не применять?

KblCb ★★★★★
()
Ответ на: комментарий от theos

Толсто же.

Ничего не толсто. С# интуитивно понятен и не требует дополнительно, хоть и приятного, изучения матчасти.

Собственно я вообще попросить хотел какую-нибудь теорию по монадам почитать, а то у меня в real world haskell уже скоро эта самая глава наступит.

RedPossum ★★★★★
()
Ответ на: комментарий от netcat

А еще есть потрясающая haskell-mode для емакса, но это частности. Меня щас интересует моя монада, господа !

s9gf4ult ★★
() автор топика

Зачем тебе еще одна монада, если в RWS уже есть стейт?

бтв

else return ()

нормальные люди пользуются when

anonymous
()
Ответ на: комментарий от RedPossum

С# интуитивно понятен и не требует дополнительно, хоть и приятного, изучения матчасти.

Приблизительно в тойже степени что и хаскелл: он предствляет типичную машину тьюринга со всеми вытикающими CS теоремами, которые не худо бы знать. И на хаскелле писать можно без знания математики.

theos ★★★
()
Ответ на: комментарий от theos

Да он же жырнота! Сидит и слюни вытирает с подбородка. Не отвечай на его сообщения.

s9gf4ult ★★
() автор топика
Ответ на: комментарий от netcat

Что-то было такое. Но я не помню. ghci использую только для того чтобы комбинировать готовые функции между собой, подсматривать сигнатуры функций и информацию о типах данных.

KblCb ★★★★★
()
Ответ на: комментарий от anonymous

Я же написал, надо чтобы в каждом действии проверялось состояние, на подобие того, как это делает монада Maybe, только мэйби сама выполняет проверку, а тут надо выполнить проверку состояния внутри монады RWS и остановить вычисление как только сосотояние подходит под определенные условия.

Может быть что то типа такого ?

data (Monad m, Monoid w) => CheckStateT r w s m a =
  CheckStateT {checkState :: s -> Bool,
               getRWS :: RWST r w s m a}

Но только как тогда имплементить return не имея функции проверки ?

s9gf4ult ★★
() автор топика
Ответ на: комментарий от netcat

Prelude> sqare_root x = x * x

Так можно делать, начиная с 7.4, нужно подождать, когда 7.4 станет достаточно распространённой (войдёт в platform).

quasimoto ★★★★
()
Ответ на: комментарий от netcat

О, благодарю. А в несколко строк функции создавать можно?

Можно даже документацию читать.

Prelude> :{
Prelude|   let f x =
Prelude|         x * x
Prelude| :}
Miguel ★★★★★
()
Ответ на: комментарий от qnikst

а можно взять gentoo, где есть ghc-7.4 и @haskell-platform и пользоваться всеми бонусами 7.4 уже сейчас.

можно подумать не в генте нельзя:

~$ apt-cache show ghc | grep Version
Version: 7.4.1-1ubuntu2
~$ apt-cache search ghc | wc -l
1830
vaino
()
Ответ на: комментарий от qnikst

а можно взять gentoo, где есть ghc-7.4 и @haskell-platform и пользоваться всеми бонусами 7.4 уже сейчас.

emerge =ghc-7.4.1? Уже сняли маску? Я когда делал emerge ghc - поставился 7.0. Ну и разный софт всё ещё хочет 7.0 и не собирается с 7.4.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

сняли маску пару дней назад, когда сделали бинари для 7.4 (под все архитектуры?).

сейчас практически всё, что есть в оверлее работает и с 7.4, а что не собирается, то патчится, пишутся патчи в апстрим. Если что не работает, то issue на github оверлея и починится быстро (ну не считая некоторых излишне запущенных случаев).

qnikst ★★★★★
()
Ответ на: комментарий от netcat

Я хотел учить Хаскелл, а ты сделал меня раздумать.

Ты еще посты quasimoto почитай... Здесь сложность не соотвествует задаче, по-крайней мере видимой ее части (вероятно человек просто хотел RWS попробовать).

anonymous
()
Ответ на: комментарий от RedPossum

Ничего не толсто. С# интуитивно понятен и не требует дополнительно, хоть и приятного, изучения матчасти.

ты с джавой не попутал?

tensai_cirno ★★★★★
()
Ответ на: комментарий от tensai_cirno

ты с джавой не попутал?

Нет конечно. У жабы либы не такие вылизанные. Ну и всякие там замыкания на анонимных классах, опять же, кривовато для новичков. А шикарные коллекции и LINQ в шарпе так вообще делают этот язык идеальным для новичков. Жаба таки посложнее.

RedPossum ★★★★★
()

отмеривание определенного количества жидкости с помощью дух сосудов разных объемов

Жесть...

Пока вы в своих хаскелах монады мучаете, мы в Ниале по галактике на трансформерах летаем и атласы создаем.

buddhist ★★★★★
()
Ответ на: комментарий от anonymous

Исходники интерпретатора? Увы, автор его что-то закрыл и забросил, хотя когда-то было под Apache License. А у меня все нет времени написать новый.

buddhist ★★★★★
()
Ответ на: комментарий от buddhist

В каком ниале ? на трансформерах монад чтоли ? Дак RWST и есть трансформер же. Че за атласы ? Создаем ? это как ?

s9gf4ult ★★
() автор топика
Ответ на: комментарий от buddhist

Увы, автор его что-то закрыл и забросил, хотя когда-то было под Apache License.

Забавно, сейчас попробовал - нашлось (до этого как ни искал «напосмотреть» - ничего не находилось).

Выцепляется отюда: http://peter.fedorapeople.org/nial-6.3-1.fc9.src.rpm

// кому не влом - отзеркальте

anonymous
()
module Main where

import Control.Monad (unless, when)
import Control.Monad.Trans.RWS
import Data.Monoid
import Control.Arrow ((&&&))

data Bottle a = Bottle {getSize :: a,
                        getMax :: a} deriving Show

getFree :: (Num a) => Bottle a -> a
getFree b = (getMax b) - (getSize b)

emptyBottle :: (Num a) => Bottle a -> Bottle a
emptyBottle (Bottle {getSize = _, getMax = m}) = Bottle {getSize = 0, getMax = m}

fillBottle :: (Ord a, Num a) => a -> Bottle a -> Bottle a
fillBottle am (Bottle {getSize = s, getMax = m}) = Bottle {getSize = max 0 $ min m $ am + s, getMax = m}

data Action a = Flow {getFrom :: Bottle a,
                      getTo :: Bottle a,
                      getAmount :: a}
              | Fill {getWhat :: Bottle a,
                      getAmount :: a}
              | Empty {getWhat :: Bottle a,
                       getAmount :: a} deriving Show
                      

type SolveMonad m a ret = RWST () (Endo [Action a]) (Bottle a, Bottle a) m ret

endotell :: (Monad m) => [Action a] -> SolveMonad m a ()
endotell a = tell $ Endo $ (a ++)

     
leftToRight, rightToLeft :: (Monad m, Num a, Ord a) => SolveMonad m a ()
leftToRight = do
  (l, r) <- get
  if (getFree r) >= (getSize l)
    then do endotell [(Flow {getFrom = l, getTo = r, getAmount = (getSize l)})]
            put (emptyBottle l, fillBottle (getSize l) r)
    else do endotell [(Flow {getFrom = l, getTo = r, getAmount = (getFree r)})]
            put (fillBottle (negate $ getFree r) l, fillBottle (getSize l) r)

execSwapped :: (Monad m) => SolveMonad m a () -> SolveMonad m a ()
execSwapped mex = do
  modify (snd &&& fst)
  mex
  modify (snd &&& fst)

rightToLeft = execSwapped leftToRight

fillLeft, fillRight :: (Monad m, Num a, Ord a) => SolveMonad m a ()
fillLeft = do
  (l, r) <- get
  endotell [(Fill {getWhat = l, getAmount = (getFree l)})]
  put (fillBottle (getFree l) l, r)

fillRight = execSwapped fillLeft

emptyLeft, emptyRight :: (Monad m, Num a) => SolveMonad m a ()
emptyLeft = do
  (l, r) <- get
  endotell [(Empty {getWhat =l, getAmount = (getSize l)})]
  put (emptyBottle l, r)
emptyRight = execSwapped emptyLeft

checkState :: (Eq a) => a -> (Bottle a, Bottle a) -> Bool
checkState sz (Bottle s1 _, Bottle s2 _) = s1 == sz || s2 == sz

isFull :: (Ord a) => Bottle a -> Bool
isFull (Bottle s m) = s >= m
                      
solve :: (Monad m, Eq a, Num a, Ord a) => a -> SolveMonad m a ()
solve sz = do
  s <- get
  unless (checkState sz s) $ do
    fillLeft
    s1 <- get
    unless (checkState sz s1) $ do
      leftToRight
      s2@(l, r) <- get
      unless (checkState sz s2) $ do
        when (isFull r) $ do
          emptyRight
          leftToRight
        solve sz

Вот полное решение задачи. Последняя функция написана ужасно, должно как - то решаться через монады, как ?

s9gf4ult ★★
() автор топика
Ответ на: комментарий от s9gf4ult

Сделай свою монаду инстансом MonadPlus и используй guard

unC0Rr ★★★★★
()
Ответ на: комментарий от lazyklimm

это как раз фигня

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

theos ★★★
()
Ответ на: комментарий от unC0Rr

Сделаю и нужно будет так же рекурсивно вызывать эту функцию это не решение. Проверка состояния должна происходить в кажом бинде этой монады.

Про гуарды тоже самое каждый раз писать один и тот же гуард перед каждым действием.

s9gf4ult ★★
() автор топика
Ответ на: комментарий от Bad_ptr

оно же там закопирайченное Nial Systems Limited...

Хм, вроде эта версия была в доступе под чем-то открытым (то artistic, то apache). Но в исходниках ничего не сказано, печаль.

anonymous
()
Ответ на: комментарий от anonymous

Кстати, о ниале — галактики же убрали из шестой версии, чтобы не усложнять язык. Хаскелю есть чему поучиться.

buddhist ★★★★★
()
Ответ на: комментарий от buddhist

Че в нем хорошего и чему учиться ? Хотябы генератор порождающих грамматик на нем можно замутить ?

s9gf4ult ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.