Chris
Chris

Reputation: 63

While loop in Haskell with a condition

I'm having a little Haskell Situation over here. I'm trying to write two functions with monads. First one is supposed to iterate through a function as long as the condition is true for the input / output of the function. Second one is supposed to use the first one to take a number as input and write it as output until you enter a space.

I'm stuck with this, any help?

module Test where

while :: (a -> Bool) -> (a -> IO a) -> a -> IO a
while praed funktion x = do
                         f <- praed (funktion x)
                         if f == True then do
                                             y <- funktion x
                                             while praed funktion y
                         else return x



power2 :: IO ()
power2 = do putStr (Please enter a number.")
            i <- getChar
            while praed funktion
            where praed x = if x /= ' ' then False else True
                  funktion = i

Upvotes: 6

Views: 28088

Answers (2)

JJJ
JJJ

Reputation: 2791

For a pure version:

{-# LANGUAGE BangPatterns #-}

while :: (a -> Bool) -> (a -> a) -> a -> a
while p f = go where go !x = if p x then go (f x) else x

test1 :: Int
test1 = while (< 1000) (* 2) 2
-- test1 => 1024

for monadic:

import Control.Monad

whileM :: (Monad m, MonadPlus f) => (a -> m Bool) -> m a -> m (f a)
whileM p f = go where
  go = do
    x <- f
    r <- p x
    if r then (return x `mplus`) `liftM` go else return mzero

test2 :: IO [String]
test2 = whileM (return . (/= "quit")) getLine
-- *Main> test2
-- quit
-- []
-- *Main> test2
-- 1
-- 2
-- 3
-- quit
-- ["1","2","3"]

power2 :: IO (Maybe Char)
power2 = whileM (return . (/= 'q')) getChar
-- *Main> power2
-- q
-- Nothing
-- *Main> power2
-- 1
-- 2
-- 3
-- q
-- Just '\n'

see also:

http://hackage.haskell.org/package/monad-loops, http://hackage.haskell.org/package/loop-while, http://hackage.haskell.org/package/control-monad-loop.

http://www.haskellforall.com/2012/01/haskell-for-c-programmers-for-loops.html

Upvotes: 4

Petr
Petr

Reputation: 63359

import Control.Monad

while :: (a -> Bool) -> (a -> IO a) -> a -> IO a
while praed funktion x
    | praed x   = do
        y <- funktion x
        while praed funktion y
    | otherwise = return x


power2 :: IO ()
power2 = do
    putStr "Please enter a number."
    i <- getChar
    let praed x = x /= ' '
    let f x = do
        putChar x
        getChar
    while praed f '?'
    return ()

Some notes:

  • Using if x then True else False is redundant, it's equivalent to just x.
  • Similarly if x == True ... is redundant and equivalent to if x ....
  • You need to distinguish between IO actions and their results. For example, if yo do

    do
        i <- getChar
        ...
    

    then in ... i represents the result of the action, a character, so i :: Char. But getChar :: IO Char is the action itself. You can view it as a recipe that returns Char when performed. You can pass the recipe around to functions etc., and it is only performed when executed somewhere.

  • Your while called funktion twice, which probably isn't what you intend - it would read a character twice, check the first one and return the second one. Remember, your funktion is an action, so each time you "invoke" the action (for example by using <- funktion ... in the do notation), the action is run again. So it should rather be something like

    do
        y <- funktion x
        f <- praed y
        -- ...
    

    (My code is somewhat different, it checks the argument that is passed to it.)

Upvotes: 12

Related Questions