tag:blogger.com,1999:blog-21500237.post5997106011461231166..comments2023-04-29T04:08:32.247-04:00Comments on Praise, Curse, and Recurse: The Revised Dot-Matrix PrintheadPaul R. Pottshttp://www.blogger.com/profile/04401509483200614806noreply@blogger.comBlogger9125tag:blogger.com,1999:blog-21500237.post-52815557038821102792006-12-18T18:23:00.000-05:002006-12-18T18:23:00.000-05:00Glad you figured it out - sorry for the one charac...Glad you figured it out - sorry for the one character typo in the original :)<br /><br />I did consider suggesting [bit-1,bit-2..0] instead of the reverse, but it doesn't look as clean. It would be possible to add a GHC rule to convert:<br /><br />reverse (fromEnumTo ...)<br /><br />to switch the arguments, which is always safe, and would give you better performance, without having to duplicate bit.<br /><br />I understand your comments about trying to keep "in C mindset" as well, it can get very confusing. I tend to either type semi-colons in Haskell, or miss them in C, depending on which transition I'm making.Neil Mitchellhttps://www.blogger.com/profile/13084722756124486154noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-66059884752638515602006-12-17T16:33:00.000-05:002006-12-17T16:33:00.000-05:00Some further followup:
GHC is confused by Literat...Some further followup:<br /><br />GHC is confused by Literate Haskell programs that have lines starting with #. The Haskell 98 report does not mention anything special about the # character, but a bug report about this issue on the GHC site indicates it has something to do with special support for C preprocessor directives and will not likely be changed. I remembered to indent the output lines starting with # in my first article, but not the second. I have fixed that and will try to remember to test the .lhs content as-is in the future.<br /><br />Neil, you are right that the line<br /><br />bitsAtIndex idx = map (`testBit` idx) (intGen bits)<br /><br />works. I should have been able to figure that out. I thought I had tried making that change but I must have broken something else and just made myself more confused. I have also followed your steps and feel like I still understand, so I think that's a good sign!Paul R. Pottshttps://www.blogger.com/profile/04401509483200614806noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-27536614283624678012006-12-17T00:15:00.000-05:002006-12-17T00:15:00.000-05:00Thanks for the ongoing advice!
I had a conversati...Thanks for the ongoing advice!<br /><br />I had a conversation with another Paul here in Ann Arbor who is a Ph.D. student in Computer Science. It turns out Haskell is his favorite language, so we had an interesting and helpful discussion on currying, and he mentioned flip. Of course, everyone else at that get-together quickly found other people to talk to! I have also been studying Bird's book, which has interesting material on how list comprehensions are actually understood. I'm getting there...Paul R. Pottshttps://www.blogger.com/profile/04401509483200614806noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-65372695012108142172006-12-16T09:51:00.000-05:002006-12-16T09:51:00.000-05:00Regarding:
bitsAtIndex idx = [ testBit n idx | n ...Regarding:<br /><br />bitsAtIndex idx = [ testBit n idx | n <- intGen bits ]<br /><br />changed to<br /><br />bitsAtIndex idx = map (`testBit` n) (intGen bits)<br /><br />I think you might be misunderstanding things a little. 'n' should be 'idx'. Here, we'll start simple, using a lambda to simulate the list comprehension:<br /><br />bitsAtIndex idx = map (\n -> testBit n idx) (intGen bits)<br /><br />Then we'll use the standard function 'flip' to flip the arguments:<br /><br />bitsAtIndex idx = map (\n -> flip testBit idx n) (intGen bits)<br /><br />Now, by the definition of lambda, we know that (\x -> f x) is equivalent to (f), so we remove the lambda (in this case f is (flip testBit idx)):<br /><br />bitsAtIndex idx = map (flip testBit idx) (intGen bits)<br /><br />This could also be written:<br /><br />bitsAtIndex idx = map (`testBit` idx) (intGen bits)<br /><br />since (x `f` y) is equivalent to (f x y). Figuring out why is left as a learning experience. :)Unknownhttps://www.blogger.com/profile/05423466002788031695noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-26146244507253210382006-12-15T23:04:00.000-05:002006-12-15T23:04:00.000-05:00Paul, I'd say the comprehension is more efficient,...Paul, I'd say the comprehension is more efficient, as it just becomes a enumFromThenTo, and does not involve reversing a list.The Alternate Moebyushttps://www.blogger.com/profile/04163363935773353768noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-17818049588538670512006-12-15T16:32:00.000-05:002006-12-15T16:32:00.000-05:00Oh, I found what I was looking for... a way to use...Oh, I found what I was looking for... a way to use ".." to express a descending list. Instead of:<br /><br />reverse [0..(bits - 1)]<br /><br />you can say<br /><br />[bits - 1, bits - 2..0]<br /><br />Clearer? More efficient? I'm not sure.Paul R. Pottshttps://www.blogger.com/profile/04401509483200614806noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-67689567899096387822006-12-15T15:53:00.000-05:002006-12-15T15:53:00.000-05:00Hi Neil,
Thanks for your suggestions!
I think yo...Hi Neil,<br /><br />Thanks for your suggestions!<br /><br />I think you are correct that there is no need for the explicit typing if a type signature is provided.<br /><br />Although it seems to work correctly, the expression <br /><br />1 <= bits && bits <= 16 <br /><br />(with no parentheses) makes me a little nervous. I work a lot C++, in which some operators have the wrong precedence. The above parses correctly in C++, but the expression<br /><br />x & 1 == 0 <br /><br />doesn't say what you mean, unless you mean "always false," because "==" binds more tightly than bitwise "and." C programmers who endeavor to make life easier for their successors tend to fully parenthesize expressions that mix different kinds of operators. I don't really want to break that habit for Haskell or I'll be writing expressions that are silently incorrect when I go back to C/C++.<br /><br />I have a similar problem with variable names; in Dylan, names like "gen-total" and "*module-var*" are not just legal but conventional, and if you want the "-" and "*" to be interpreted as a separate token you have to leave spaces around them. When I go from Dylan back to C I tend to write illegal variable names!<br /><br />The conversion of<br /><br />bitPicture intGen bits = unlines $ map stringAtIndex (reverse [0..(bits - 1)])<br /><br />to<br /><br />bitPicture intGen bits = unlines $ map stringAtIndex $ reverse [0..(bits - 1)]<br /><br />seems entirely good, using the $ operator again; but the change from<br /><br />bitsAtIndex idx = [ testBit n idx | n <- intGen bits ]<br /><br />to<br /><br />bitsAtIndex idx = map (`testBit` n) (intGen bits)<br /><br />loses the definition for n, so I get "Not in scope: 'n'." I have played around a bit with other possible ways to get rid of the comprehension here but they did not seem to work right. In Scheme I would consider using rcurry to make testBit idx into a function I could map the list onto, but I am not quite sure how to get that effect in Haskell.Paul R. Pottshttps://www.blogger.com/profile/04401509483200614806noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-84350091033785373582006-12-14T16:28:00.000-05:002006-12-14T16:28:00.000-05:00Oh, and don't worry about $ -its useful in continu...Oh, and don't worry about $ -its useful in continuation passing style, but its a lot more useful to avoid some brackets!<br /><br />f (big thing) == f $ big thing<br /><br />f (big (thing here)) = f $ big $ thing hereNeil Mitchellhttps://www.blogger.com/profile/13084722756124486154noreply@blogger.comtag:blogger.com,1999:blog-21500237.post-78389084695740513782006-12-14T16:25:00.000-05:002006-12-14T16:25:00.000-05:00A couple of comments, just tiny refinements:
>un...A couple of comments, just tiny refinements:<br /><br /><br />>unsignedInts :: Int -> [Int]<br />>unsignedInts bits | (1 <= bits) && (bits <= 16) = <br />> [(0::Int)..(bound::Int)] <br />> where bound = 2 ^ bits - 1<br /><br />I'd write this as:<br /><br />unsignedInts :: Int -> [Int]<br />unsignedInts bits | 1 <= bits && bits <= 16 = [0..bound]<br /> where bound = 2 ^ bits - 1<br /><br />The brackets are unnecessary.<br /><br />I'm pretty sure the type signature is entirely redundant? The type sig on unsignedInts should make it all Int's. If you did need a type sig, I'd put it on bound<br /><br />where bound = (2 ^ bits - 1) :: Int<br /><br />signedInt's could be refined in a similar way.<br /><br />In a similar way:<br /><br />>bitPicture intGen bits = unlines $ map stringAtIndex (reverse [0..(bits-1)])<br />> where<br />> stringAtIndex = map boolToChar . bitsAtIndex<br />> bitsAtIndex idx = [ testBit n idx | n <- intGen bits ]<br /><br />bitPicture intGen bits = unlines $ map stringAtIndex $ reverse [0..bits-1]<br />where<br /> stringAtIndex = map boolToChar . bitsAtIndex<br /> bitsAtIndex idx = map (`testBit` n) (intGen bits)<br /><br />No need for a list comp in the second one, and a few less brackets all round :)Neil Mitchellhttps://www.blogger.com/profile/13084722756124486154noreply@blogger.com