Clean way to do rewrite rules - haskell
I have the following toy language:
module Lang where
data Op = Move Int -- Move the pointer N steps
| Add Int -- Add N to the value under the pointer
| Skip -- Skip next op if the value under the pointer is 0
| Halt -- End execution
deriving (Show, Eq)
type Program = [Op]
The language has a finite tape of memory cells that wraps around, and a pointer that points at some cell. All the cells are initially zero. The program is executed repeatedly until the halt instruction is read.
Now I would like to write a function that optimizes a given program. Here is the optimizations that I would like to perform:
| Original code | Optimization |
|---------------------|----------------|
| Move a : Move b : x | Move (a+b) : x |
| Add a : Add b : x | Add (a+b) : x |
| Move 0 : x | x |
| Add 0 : x | x |
| Skip : Skip : x : y | x : y |
| Halt : _ | Halt |
Additionally, I can only do an optimization on code that is not directly after a skip, because doing that would change the meaning of the program.
Is repeatedly pattern matching on the list until no more optimizations can be performed really the best/cleanest way to do this?
What if I decide that I also want to perform more advanced rewrites like these:
| Original code | Optimization |
|--------------------------------------------------------|------------------------------------------------|
| if the program begins with (Skip : a) | move it to the end of the program |
| Move x ++ no_skips : Move -x ++ no_skips' : Move w : q | Move x ++ no_skips ++ no_skips' : Move w-x : q |
Use Maybe's!
#user2407038 told me that I could use Maybe in a comment
module MaybeProg where
import Lang
import Control.Monad
type Opt = Program -> Maybe Program
optimize = untilFail step
where step p | p' <- atEveryButSkipNextWhen (==Skip) rewrite
. atEvery delNopSkip
$ untilFail moveSkips p
, p /= p' = Just p'
| otherwise = Nothing
rewrite = tryAll [joinMoves, joinAdds, delNopMov, delNopAdd, termHalt, reorder]
joinMoves p = do (Move a : Move b : x) <- pure p; Just $ Move (a+b) : x
joinAdds p = do (Add a : Add b : x) <- pure p; Just $ Add (a+b) : x
delNopMov p = do (Move 0 : x) <- pure p; Just x
delNopAdd p = do (Add 0 : x) <- pure p; Just x
delNopSkip p = do (Skip : Skip : x) <- pure p; Just x
termHalt p = do (Halt : _) <- pure p; Just [Halt]
moveSkips p = do (Skip : x : y : z) <- pure p; Just $ y : z ++ [Skip, x]
reorder p = do
(Move x : rst) <- pure p
(as, Move y : rst') <- break' isMove rst
guard $ x == -y && all (/=Skip) as
(bs, Move w : q ) <- break' isMove rst'
guard $ all (/=Skip) bs
return $ Move x : as ++ bs ++ Move (w-x) : q
where isMove (Move _) = True
isMove _ = False
--------
untilFail :: Opt -> Program -> Program
untilFail o p | Just p' <- o p = untilFail o p'
| otherwise = p
atEvery :: Opt -> Program -> Program
atEvery o p | (x:xs) <- untilFail o p = x : atEvery o xs
| otherwise = []
atEveryButSkipNextWhen c o p#(h:_)
| not $ c h
, (x:xs) <- untilFail o p = x : atEveryButSkipNextWhen c o xs
| (p1:p2:ps) <- p = p1:p2:atEveryButSkipNextWhen c o ps
| otherwise = p
atEveryButSkipNextWhen _ _ [] = []
tryAll :: [Opt] -> Opt
tryAll os p = do
Just x : _ <- pure . dropWhile (==Nothing) $ ($p) <$> os
return x
break' f p | (x, y) <- break f p
, not $ null y = Just (x, y)
| otherwise = Nothing
Use pattern matching!
This is what I wanted to avoid, I am posting it for reference
module Pattern where
import Lang
optimize :: Program -> Program
optimize p
| p' <- reorder $ rewrite $ moveSkip p
, p /= p' = optimize p'
| otherwise = p
rewrite :: Program -> Program
rewrite (Move a : Move b : x) = rewrite $ Move (a+b) : x
rewrite (Add a : Add b : x) = rewrite $ Add (a+b) : x
rewrite (Move 0 : x) = rewrite x
rewrite (Add 0 : x) = rewrite x
rewrite (Skip : Skip : x) = rewrite x
rewrite (Halt : _) = [Halt]
rewrite (Skip : x : xs) = Skip : x : rewrite xs
rewrite (x : xs) = x : rewrite xs
rewrite [] = []
moveSkip :: Program -> Program
moveSkip (Skip : a : x) = x ++ [Skip, a]
moveSkip x = x
reorder :: Program -> Program
reorder (Move x : xs)
| (no_skips , Move y : xs') <- break isMove xs
, (no_skips' , Move w : q ) <- break isMove xs'
, x == -y
, all (/=Skip) no_skips
, all (/=Skip) no_skips'
= Move x : no_skips ++ no_skips' ++ Move (w-x) : reorder q
| otherwise = Move x : reorder xs
reorder (Skip : x : xs) = Skip : x : reorder xs
reorder (x:xs) = x : reorder xs
reorder [] = []
isMove (Move _) = True
isMove _ = False
Related
I/O how can i put somehing in screen withouth being string?
So im doing this function and i need her to display on the screen the result of (premio ap x) , the problem is that (premio ap x)::Maybe Int , so its not a string. joga :: Aposta -> IO () joga x= do ap <- leAposta; let arroz = (premio ap x) putStr ^^^^^^^^^^ return () How can i convert this to a string? Or there is another way to display on the screen things that are not strings. update :full code comuns :: Aposta -> Aposta -> (Int,Int) comuns (Ap a (b,c)) (Ap k (l,ç)) = (cnum a k, cnum [b,c] [l,ç]) cnum::[Int]->[Int]->Int cnum [] l2 = 0 cnum (x:xs) l2 | elem x l2 = 1 + cnum xs l2 |otherwise = cnum xs l2 premio :: Aposta -> Aposta -> Maybe Int premio l1 l2 | x == (5,2)= Just 1 | x == (5,1)= Just 2 | x == (5,0)= Just 3 | x == (4,2)= Just 4 | x == (4,1)= Just 5 | x == (4,0)= Just 6 | x == (3,2)= Just 7 | x == (2,2)= Just 8 | x == (3,1)= Just 9 | x == (3,0)= Just 10 | x == (1,2)= Just 11 | x == (2,1)= Just 12 | x == (2,0)= Just 13 |otherwise = Nothing where x = comuns l1 l2 leAposta :: IO Aposta leAposta = do putStrLn "Insira como lista as 5 estrelas" num <-getLine putStrLn "Insira em par as 2 estrelas" es<-getLine let ap = (Ap (read num) (read es)) if (valida ap) then return ap else do putStrLn "Aposta invalida" leAposta
Since arroz is premio ap x which has type Maybe Int, you can simply print arroz. print works on any type that can be printed, i.e. on those types in class Show. (You probably don't want to use print on values that are already strings, though, since that will print the escaped string, with quotes around. Use putStr and putStrLn for strings.)
Getting parse error while doing list comprehensions in haskell
I'm writing a function like this: testing :: [Int] -> [Int] -> [Int] testing lst1 lst2 = let t = [ r | (x,y) <- zip lst1 lst2, let r = if y == 0 && x == 2 then 2 else y ] let t1 = [ w | (u,v) <- zip t (tail t), let w = if (u == 2) && (v == 0) then 2 else v] head t : t1 What the first let does is: return a list like this: [2,0,0,0,1,0], from the second let and the following line, I want the output to be like this: [2,2,2,2,1,0]. But, it's not working and giving parse error!! What am I doing wrong?
There are two kinds of lets: the "let/in" kind, which can appear anywhere an expression can, and the "let with no in" kind, which must appear in a comprehension or do block. Since your function definition isn't in either, its let's must use an in, for example: testing :: [Int] -> [Int] -> [Int] testing lst1 lst2 = let t = [ r | (x,y) <- zip lst1 lst2, let r = if y == 0 && x == 2 then 2 else y ] in let t1 = [ w | (u,v) <- zip t (tail t), let w = if (x == 2) && (y == 0) then 2 else y] in return (head t : t1) Alternately, since you can define multiple things in each let, you could consider: testing :: [Int] -> [Int] -> [Int] testing lst1 lst2 = let t = [ r | (x,y) <- zip lst1 lst2, let r = if y == 0 && x == 2 then 2 else y ] t1 = [ w | (u,v) <- zip t (tail t), let w = if (x == 2) && (y == 0) then 2 else y] in return (head t : t1) The code has other problems, but this should get you to the point where it parses, at least.
With an expression formed by a let-binding, you generally need let bindings in expressions (there are exceptions when monads are involved). So, your code can be rewritten as follows (with simplification of r and w, which were not really necessary): testing :: [Int] -> [Int] -> [Int] testing lst1 lst2 = let t = [ if y == 0 && x == 2 then 2 else y | (x,y) <- zip lst1 lst2] t1 = [ if (v == 0) && (u == 2) then 2 else v | (u,v) <- zip t (tail t)] in head t : t1 (Note, I also switched u and v so that t1 and t has similar forms. Now given a list like [2,0,0,0,1,0], it appears that your code is trying to replace 0 with 2 if the previous element is 2 (from the pattern of your code), so that eventually, the desired output is [2,2,2,2,1,0]. To achieve this, it is not enough to use two list comprehensions or any fixed number of comprehensions. You need to somehow apply this process recursively (again and again). So instead of only doing 2 steps, we can write out one step, (and apply it repeatedly). Taking your t1 = ... line, the one step function can be: testing' lst = let t1 = [ if (u == 2) && (v == 0) then 2 else v | (u,v) <- zip lst (tail lst)] in head lst : t1 Now this gives: *Main> testing' [2,0,0,0,1,0] [2,2,0,0,1,0] , as expected. The rest of the job is to apply testing' as many times as necessary. Here applying it (length lst) times should suffice. So, we can first write a helper function to apply another function n times on a parameter, as follows: apply_n 0 f x = x apply_n n f x = f $ apply_n (n - 1) f x This gives you what you expected: *Main> apply_n (length [2,0,0,0,1,0]) testing' [2,0,0,0,1,0] [2,2,2,2,1,0] Of course, you can wrap the above in one function like: testing'' lst = apply_n (length lst) testing' lst and in the end: *Main> testing'' [2,0,0,0,1,0] [2,2,2,2,1,0] NOTE: this is not the only way to do the filling, see the fill2 function in my answer to another question for an example of achieving the same thing using a finite state machine.
haskell error parse error (possibly incorrect indentation)
this is my code font a = let x= ord a in if x>=0 || x<=31 || x>=126 then ["*****","*****","*****","*****","*****","*****","*****"] else auxfont (fontBitmap!!(x-32)) where auxfont b = let y = map trns (map rInt (map show b)) in convertir y [] trns z = modA [] 1 z modA o l k | l < 8 = modA (o++[(k `mod` 2)]) (l+1) (k `div` 2) | otherwise o convertir (e1:e2:e3:e4:e5) f | e1==[] = f | otherwise convertir [tail(e1),tail(e2),tail(e3),tail(e4),tail(e5)] (f++[(psr(head(e1)))++(psr(head(e2)))++(psr(head(e3)))++(psr(head(e4)))++(psr(head(e5)))]) psr 0 = " " psr 1 = "*" and i had and this error in convertir: [1 of 2] Compiling Pixels ( Pixels.hs, interpreted ) Pixels.hs:122:13: parse error (possibly incorrect indentation) Failed, modules loaded: none.
Why the error Every (normal) guard is of the form | boolean expression = value You missed this out for your otherwise cases. It works like this because otherwise is defined as otherwise = True so it's not a keyword like else, it's just a human-readable "always", and since the guards are tried top-to-bottom, this is a catch-all for anything that wasn't true above. Some corrections font a = let x= ord a in if x>=0 || x<=31 || x>=126 then ["*****","*****","*****","*****","*****","*****","*****"] else auxfont (fontBitmap!!(x-32)) where auxfont b = let y = map trns (map rInt (map show b)) in convertir y [] trns z = modA [] 1 z modA o l k | l < 8 = modA (o++[(k `mod` 2)]) (l+1) (k `div` 2) here: | otherwise = o -- added = convertir (e1:e2:e3:e4:e5) f | e1==[] = f and here: | otherwise = convertir [tail(e1),tail(e2),tail(e3),tail(e4),tail(e5)] (f++[(psr(head(e1)))++(psr(head(e2)))++(psr(head(e3)))++(psr(head(e4)))++(psr(head(e5)))]) psr 0 = " " psr 1 = "*" Some abbreviations By the way, ["*****","*****","*****","*****","*****","*****","*****"] is replicate 7 "*****" and map trns (map rInt (map show b)) is map (trns.fInt.show) b. Also [tail(e1),tail(e2),tail(e3),tail(e4)] is map tail [e1,e2,e3,e4,e5] but I think you have a type error with :e5, because it has to be a list of lists in the pattern (e1:e2:e3:e4:e5) but you've used it like an element tail(e5). Also [(psr(head(e1)))++(psr(head(e2)))++(psr(head(e3)))++(psr(head(e4)))++(psr(head(e5)))] is map (psr.head) [e1,e2,e3,e4,e5].
FizzBuzz cleanup
I'm still learning Haskell, and I was wondering if there is a less verbose way to express the below statement using 1 line of code: map (\x -> (x, (if mod x 3 == 0 then "fizz" else "") ++ if mod x 5 == 0 then "buzz" else "")) [1..100] Produces: [(1,""),(2,""),(3,"fizz"),(4,""),(5,"buzz"),(6,"fizz"),(7,""),(8,""),(9,"fizz"),(10,"buzz"),(11,""),(12,"fizz"),(13,""),(14,""),(15,"fizzbuzz"),(16,""),(17,""),(18,"fizz"),(19,""),(20,"buzz"),(21,"fizz"),(22,""),(23,""),(24,"fizz"),(25,"buzz"),(26,""),(27,"fizz"),(28,""),(29,""),(30,"fizzbuzz"), etc It just feels like I'm fighting the syntax more than I should. I've seen other questions for this in Haskell, but I'm looking for the most optimal way to express this in a single statement (trying to understand how to work the syntax better).
We need no stinkin' mod... zip [1..100] $ zipWith (++) (cycle ["","","fizz"]) (cycle ["","","","","buzz"]) or slightly shorter import Data.Function(on) zip [1..100] $ (zipWith (++) `on` cycle) ["","","fizz"] ["","","","","buzz"] Or the brute force way: zip [1..100] $ cycle ["","","fizz","","buzz","fizz","","","fizz","buzz","","fizz","","","fizzbuzz"]
If you insist on a one-liner: [(x, concat $ ["fizz" | mod x 3 == 0] ++ ["buzz" | mod x 5 == 0]) | x <- [1..100]]
How's about... fizzBuzz = [(x, fizz x ++ buzz x) | x <- [1..100]] where fizz n | n `mod` 3 == 0 = "fizz" | otherwise = "" buzz n | n `mod` 5 == 0 = "buzz" | otherwise = ""
Couldn't resist going in the other direction and making it more complicated. Look, no mod... merge as#(a#(ia,sa):as') bs#(b#(ib,sb):bs') = case compare ia ib of LT -> a : merge as' bs GT -> b : merge as bs' EQ -> (ia, sa++sb) : merge as' bs' merge as bs = as ++ bs zz (n,s) = [(i, s) | i <- [n,2*n..]] fizzBuzz = foldr merge [] $ map zz [(1,""), (3,"fizz"), (5,"buzz")]
Along the same lines as larsmans' answer: fizzBuzz = [(x, f 3 "fizz" x ++ f 5 "buzz" x) | x <- [1..100]] where f k s n | n `mod` k == 0 = s | otherwise = ""
I think the reason why you feel like you are fighting the syntax is because you are mixing too many types. Instead of trying to print: [(1, ""), (2,""), (3,"Fizz")...] Just think of printing strings: ["1","2","Fizz"...] My attempt: Prelude> let fizzBuzz x | x `mod` 15 == 0 = "FizzBuzz" | x `mod` 5 == 0 = "Buzz" | x `mod` 3 == 0 = "Fizz" | otherwise = show x Prelude> [fizzBuzz x | x <-[1..100]] ["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz"...] In order to convert an Int to String you use the: show x
Just for studying zipWith (\a b -> b a) (map show [1..100]) $ cycle [id,id,const "fizz",id,const "buzz",const "fizz",id,id,const "fizz",const "buzz",id,const "fizz",id,id,const "fizzbuzz"] produces ["1","2","fizz","4","buzz","fizz","7","8","fizz","buzz","11","fizz","13","14","fizzbuzz","16","17","fizz","19","buzz","fizz","22","23","fizz","buzz","26","fizz","28","29","fizzbuzz","31","32","fizz","34","buzz","fizz","37","38","fizz","buzz","41","fizz","43","44","fizzbuzz","46","47","fizz","49","buzz","fizz","52","53","fizz","buzz","56","fizz","58","59","fizzbuzz","61","62","fizz","64","buzz","fizz","67","68","fizz","buzz","71","fizz","73","74","fizzbuzz","76","77","fizz","79","buzz","fizz","82","83","fizz","buzz","86","fizz","88","89","fizzbuzz","91","92","fizz","94","buzz","fizz","97","98","fizz","buzz"]
Writer monad may look nice (if you don't like concat): fizzBuzz = [(x, execWriter $ when (x `mod` 3 == 0) (tell "fizz") >> when (x `mod` 5 == 0) (tell "buzz")) | x <- [1..100]] It's not particularly succinct though.
How to check that I'm dealing with a list in Haskell?
I'm learning Haskell, and I'm trying to add preconditions to a (trivial, as an exercise) element_at function (code below). I've created a "helper" elem_at_r because otherwise, len x fails at some point (when x is a 'literal' rather than a list? - I still have trouble parsing ghci's error messages). elem_at now has all the error checking, and elem_at_r does the work. In elem_at, I'd like to add a check that x is indeed a list (and not a 'literal'). How can I do that? len x = sum [ 1 | a <- x] elem_at_r x n | n == 0 = head x | 0 < n = elem_at_r (tail x) (n-1) elem_at x n | x == [] = error "Need non-empty list" | len x <= n = error "n too large " ++ show (len x) | n < 0 = error "Need positive n" | otherwise = elem_at_r x n Thanks! Frank
Due to Haskell's type system, elem_at can only take a list as its first argument (x); if you try to pass a non-list, GHC will detect this and give an error at compile time (or interpretation time in GHCi). I don't know why len would "fail"; could you post the error message that GHCi gives you?
It looks like you were getting errors because of the "x == []" line. The code below pattern matches for that condition and adds a few signatures. Otherwise it is the same. Hope it helps. len x = sum [ 1 | a <- x] elem_at_r :: [a] -> Int -> a elem_at_r x n | n == 0 = head x | 0 < n = elem_at_r (tail x) (n-1) elem_at :: [a] -> Int -> a elem_at [] _ = error "Need non-empty list" elem_at x n | len x <= n = error ("n too large " ++ show (len x)) | n < 0 = error "Need positive n" | otherwise = elem_at_r x n You could also make your helper functions part of this function using a where clause: elem_at :: [a] -> Int -> a elem_at [] _ = error "Need non-empty list" elem_at x n | len x <= n = error ("n too large " ++ show (len x)) | n < 0 = error "Need positive n" | otherwise = elem_at_r x n where len :: [a] -> Int len x = sum [ 1 | a <- x] elem_at_r :: [a] -> Int -> a elem_at_r x n | n == 0 = head x | 0 < n = elem_at_r (tail x) (n-1)