
I have a list (more precisely a string) which I'm trying to recurse through, editing sections as I come to it. It's obvious how to do it with a for loop, but I'm doing it all wrong in haskell, as this simple task is running mind-numbingly slowly. addOneNum cycles through each line, and when it gets to character 23, it calls changeNo, which edits the string. The problem is clearly the adding on to the end of the string, but the only other way I can see of doing it is writing the 'new string' backwards and then reverse it at the end. This looks like it would make the function even less legible than it already is though. addOneNum :: String -> String -> Integer -> String addOneNum x [] _ = x addOneNum x (y:ys) n = case n of 23 -> changeNo x (y:ys) n _ -> case y of '\n' -> addOneNum (x ++ [y]) ys 1 _ -> addOneNum (x ++ [y]) ys (n+1) changeNo :: String -> String -> Integer -> String changeNo v (w:x:y:z:xs) n = case length(show((read([w,x,y,z])::Integer) + 1)) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 2 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 3 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 4 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1)) xs (n+4) _ -> error "asdf" changeNo w _ n = error "changeNo error" Also, while I'm at it, it looks like I want to be using an as-pattern (or some C-style #define thing): changeNo v (w:x:y:z:xs) n = case length(s@(show((read([w,x,y,z])::Integer) + 1))) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ s ++ " ") xs (n+4) 2 -> addOneNum (v ++ s ++ " ") xs (n+4) 3 -> addOneNum (v ++ s ++ " ") xs (n+4) 4 -> addOneNum (v ++ s) xs (n+4) _ -> error "asdf" but that gives me a 'not in scope' error. Any suggestions? Cheers, PhiJ

Can you give a few simple examples of some typical inputs and outputs for your functions? There are almost certainly much better ways to do whatever you are trying to do, but I am having trouble understanding exactly what it is. -Brent On Sat, Oct 30, 2010 at 03:59:00PM +0100, Jonathan Phillips wrote:
I have a list (more precisely a string) which I'm trying to recurse through, editing sections as I come to it. It's obvious how to do it with a for loop, but I'm doing it all wrong in haskell, as this simple task is running mind-numbingly slowly.
addOneNum cycles through each line, and when it gets to character 23, it calls changeNo, which edits the string. The problem is clearly the adding on to the end of the string, but the only other way I can see of doing it is writing the 'new string' backwards and then reverse it at the end. This looks like it would make the function even less legible than it already is though.
addOneNum :: String -> String -> Integer -> String
addOneNum x [] _ = x addOneNum x (y:ys) n = case n of 23 -> changeNo x (y:ys) n _ -> case y of '\n' -> addOneNum (x ++ [y]) ys 1 _ -> addOneNum (x ++ [y]) ys (n+1)
changeNo :: String -> String -> Integer -> String changeNo v (w:x:y:z:xs) n = case length(show((read([w,x,y,z])::Integer) + 1)) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 2 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 3 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 4 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1)) xs (n+4) _ -> error "asdf" changeNo w _ n = error "changeNo error"
Also, while I'm at it, it looks like I want to be using an as-pattern (or some C-style #define thing):
changeNo v (w:x:y:z:xs) n = case length(s@(show((read([w,x,y,z])::Integer) + 1))) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ s ++ " ") xs (n+4) 2 -> addOneNum (v ++ s ++ " ") xs (n+4) 3 -> addOneNum (v ++ s ++ " ") xs (n+4) 4 -> addOneNum (v ++ s) xs (n+4) _ -> error "asdf"
but that gives me a 'not in scope' error. Any suggestions?
Cheers, PhiJ _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Saturday 30 October 2010 16:59:00, Jonathan Phillips wrote:
I have a list (more precisely a string) which I'm trying to recurse through, editing sections as I come to it. It's obvious how to do it with a for loop, but I'm doing it all wrong in haskell, as this simple task is running mind-numbingly slowly.
addOneNum cycles through each line, and when it gets to character 23, it calls changeNo, which edits the string. The problem is clearly the adding on to the end of the string,
Yes, that's bad, since the parentheses nest the wrong way. You get ((((a ++ b) ++ c) ++ d) ++ e) and to get to the start, you have to dig through all those layers of parentheses. It would be less bad if the construction built right-nested parentheses.
but the only other way I can see of doing it is writing the 'new string' backwards and then reverse it at the end. This looks like it would make the function even less legible than it already is though.
It's a fairly common technique, since consing to the front of a list is very cheap while appending to the end is expensive (especially if you repeatedly add single elements). However, here you can get much better behaviour by using another common technique, delivering the string incrementally. Since all you ever do with the accumulator is appending to the end of it, you don't need an accumulator at all.
addOneNum :: String -> String -> Integer -> String
addOneNum x [] _ = x addOneNum x (y:ys) n = case n of 23 -> changeNo x (y:ys) n _ -> case y of '\n' -> addOneNum (x ++ [y]) ys 1 _ -> addOneNum (x ++ [y]) ys (n+1)
addOneNum' :: String -> Integer -> String addOneNum' [] _ = [] addOneNum' (y:ys) n = case n of 23 -> changeNo' (y:ys) n _ -> case y of '\n' -> y : addOneNum' ys 1 _ -> y : addOneNum' ys (n+1) changeNo' :: String -> Integer -> String changeNo' (w:x:y:z:xs) n = let s = show ((read [w,x,y,z] :: Integer) + 1) in case length s of 0 -> " " ++ addOneNum' xs (n+4) -- can't happen, by the way l | l < 4 -> s ++ " " ++ addOneNum' xs (n+4) -- guessing it's the same number of spaces for those | otherwise -> error "asdf" changeNo' _ _ = error "changeNo' error" If the input is incrementally produced and the output sequentially consumed, that runs in constant space (and if not all output is needed, it'll stop early).
changeNo :: String -> String -> Integer -> String changeNo v (w:x:y:z:xs) n = case length(show((read([w,x,y,z])::Integer) + 1)) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 2 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 3 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1) ++ " ") xs (n+4) 4 -> addOneNum (v ++ show((read([w,x,y,z])::Integer) + 1)) xs (n+4) _ -> error "asdf" changeNo w _ n = error "changeNo error"
Also, while I'm at it, it looks like I want to be using an as-pattern (or some C-style #define thing):
changeNo v (w:x:y:z:xs) n = case length(s@(show((read([w,x,y,z])::Integer) + 1))) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ s ++ " ") xs (n+4) 2 -> addOneNum (v ++ s ++ " ") xs (n+4) 3 -> addOneNum (v ++ s ++ " ") xs (n+4) 4 -> addOneNum (v ++ s) xs (n+4) _ -> error "asdf"
but that gives me a 'not in scope' error. Any suggestions?
You can use as-patterns only in pattern-matching contexts. You could write case show (...) of s -> case length s of 0 -> ... but normally you bind the value to the name via a let (or where).
Cheers, PhiJ

On Nov 2, 2010, at 21:10, Daniel Fischer wrote:
On Saturday 30 October 2010 16:59:00, Jonathan Phillips wrote:
I have a list (more precisely a string) which I'm trying to recurse through, editing sections as I come to it. It's obvious how to do it with a for loop, but I'm doing it all wrong in haskell, as this simple task is running mind-numbingly slowly.
Maybe you should split the string into lines using 'lines' from Data.List, then divide each line into 23-4-rest chars somehow using 'splitAt', apply the "show ((read str :: Integer) + 1)" part, justify the answer, probably by appending four spaces and then taking the first four characters, put the 23-changed4-rest parts back together with ++, and finally join all the lines again with 'unlines'.
l | l < 4 -> s ++ " " ++ addOneNum' xs (n+4) -- guessing it's the same number of spaces for those
I suppose it shall justify the number to length 4. I.e.
changeNo v (w:x:y:z:xs) n = case length(s@(show((read([w,x,y,z])::Integer) + 1))) of 0 -> addOneNum (v ++ " ") xs (n+4) 1 -> addOneNum (v ++ s ++ " ") xs (n+4) 2 -> addOneNum (v ++ s ++ " ") xs (n+4) 3 -> addOneNum (v ++ s ++ " ") xs (n+4) 4 -> addOneNum (v ++ s) xs (n+4) _ -> error "asdf"
Cheers, Bastian

What the original author appears to want is "string rewriting". Dave Bayer had a nice implementation in this thread... http://www.haskell.org/pipermail/beginners/2010-February/003399.html His question was actually about something else, but the continuation of the thread illuminates his technique, see my reply and the messages following it up: http://www.haskell.org/pipermail/beginners/2010-February/003433.html String rewriting is not particularly straight-forward in Haskell because you are consuming the input string and generating output at "different speeds" so using direct recursion and pattern matching makes for monolithic code. By "different speeds" I mean you might want to look at one or several characters in the input to match a token, and produce zero, one or many characters as output at each rewrite step.
participants (5)
-
Bastian Erdnüß
-
Brent Yorgey
-
Daniel Fischer
-
Jonathan Phillips
-
Stephen Tetley