Archive | The C Programming Language RSS for this section

Again, This Time In Reverse!

Today we’ll be doing Exercise 5 from The C Programming Language. Today’s exercises aren’t terribly challenging, but there is some good stuff in here. The problem is:

Modify the temperature conversion program to print the table in reverse order, that is, from 300 degrees to 0.

I bet you thought we were done with temperature conversion, didn’t you? I’m right there with you, but luckily for us, this is also the section that introduces the for loop, so this is much less tedious. The new program for modification:

#include <stdio.h> int main (int argc, char ** argv) { for (int fahr = 0; fahr <= 300; fahr = fahr + 20) { printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); } }

I made a few minor modifications. I gave main the correct signature to keep gcc from complaining, and I also moved the initialization of fahr to the loop. As you may know this is not valid C, and the compiler will throw an error. You might also know that this was made to be valid C in the C 99 revision. To compile this, you just need to add the -std=c99 flag to your gcc call.

To complete this exercise, you only need to modify the loop declaration. Change this line…

for (int fahr = 0; fahr <= 300; fahr = fahr + 20)

…to this…

for (int fahr = 300; fahr >= 0; fahr = fahr - 20)

Pretty straight forward stuff here. fahr starts at 300 instead of 0, and the loop continues so long as it remains greater than 0. Instead of adding 20 each iteration, we subtract it. This can be compiled like so:

gcc -std=c99 -Wall ex5.c -o ex5

In Haskell

Unfortunately, the authors of The C Programming Language did not see fit to include a starting Haskell program for us to modify. This seems like a pretty serious oversight to me, but we’ll just have to make due. Luckily for us, we can use the temperature conversion program I wrote for the last entry in this series. This program handles conversions, and we can use it to produce a table of all conversions between 0 and 300 degrees.

While I’ve established that Haskell does in fact have loops, we won’t be using them here. Instead we’ll be using ranges, functors, and sequencing to solve this problem in a more functional way.

First, we need a list of every 20th number between 0 and 300:

[300.0, 280.0 .. 0.0]

The .. there basically says “You get the idea, do that.” If you put enough information into it so that the compiler can figure out the pattern, and start and end points, the compiler will fill the rest in. In this case, I gave the first two values, telling the compiler I want increments of 20, and I gave it the last value so it knows where to stop.

Unfortunately, as you recall, my conversion program needs members of the Temperature typeclass. Surely I don’t plan to make Double a member, right? Right. We need to map a function over this list to produce a list of Temperatures. But what function should we use?

Something that beginning Haskellers may not realize is that constructors are in fact functions!

Prelude> :t Just Just :: a -> Maybe a Prelude> :t Left Left :: a -> Either a b

…and of course…

*Main> :t Fahrenheit Fahrenheit :: Double -> Fahrenheit

That’s right, we can map Fahrenheit over a functor just like any other function!

map Fahrenheit [300.0, 280.0 .. 0.0]

This will produce a list of every 20th Fahrenheit temperature between 300 and 0. However, we aren’t done yet. We actually need a list of Conversions, because this type is required for our insertConv function. To get this we can map toConversion over this list:

map toConversion $ map Fahrenheit [300.0, 280.0 .. 0.0]

…but that’s starting to get a bit ugly, there must be a better way. Normally, I’m not terribly fond of function composition, in this case it will make our life easier.

Function Composition

Haskell provides an operator . that is used for function composition. Let’s take a look at its type:

Prelude> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c

The composition operator takes a function that takes some type b as an argument, and that returns some type c. It takes a second function that takes some type a as an argument, and that returns some type b. Finally, it returns some type c.

What does this do for us? Basically, it takes a function a -> b, and a function b -> c, and turns them into a function a -> c. Let’s see an example:

Prelude> :t show show :: Show a => a -> String

The function show takes a member of the Show typeclass and returns a String.

Prelude> :t putStr putStr :: String -> IO ()

The function putStr takes a String, and returns an IO action.

Prelude> :t putStr . show putStr . show :: Show a => a -> IO ()

When we compose the two functions, we get a function that takes a member of the Show typeclass, and returns an IO action.

Logically, calling putStr . show someShowable is the same as calling putStr $ show someShowable or putStr (show someShowable). However, putStr . show is a valid function, where putStr $ show and putStr (show) are compiler errors if you don’t give show an argument.

How does this help us?

*Main> :t Fahrenheit Fahrenheit :: Double -> Fahrenheit *Main> :t toConversion toConversion :: (Temperature a, Temperature b) => a -> (a, b) *Main> :t toConversion . Fahrenheit toConversion . Fahrenheit :: Temperature b => Double -> (Fahrenheit, b)

By composing toConversion and Fahrenheit, we end up with a function that takes a Double as an argument and returns a Conversion from Fahrenheit. We can then map this function over our list of Doubles:

map (toConversion . Fahrenheit) [300.0, 280.0 .. 0.0]

Next, we need to turn these Conversions into TableBuilders. This is a simple matter of composing insertConv:

map (insertConv . toConversion . Fahrenheit) [300.0, 280.0 .. 0.0]

The type of this new function is:

:t (insertConv . toConversion . Fahrenheit) (insertConv . toConversion . Fahrenheit) :: Double -> TableBuilder ()

…or at least it would be if the type inferrer could figure out the type of toConversion. Unfortunately for us, it can’t because the implementation is purposely ambiguous. We need to add a type signature to it:

(insertConv . (toConversion :: Fahrenheit -> (Fahrenheit, Celsius)) . Fahrenheit)

Sequencing

Now we are ready to create our table. This part is actually very easy. Thanks to the library function sequence_ we won’t even need a do block. What does this function do? Let’s look at its signature:

Prelude> :t sequence_ sequence_ :: Monad m => [m a] -> m () Prelude> :t sequence sequence :: Monad m => [m a] -> m [a]

There are two variations of this function. The first, sequence_ evaluates each monadic action, and ignores the results. Basically, suppose we have some function: func :: a -> Monad b

let mList = map func ["foo", "bar", "baz"] in sequence_ mList

…is equivalent to…

do func "foo" func "bar" func "baz" return ()

The second variation evaluates each monadic action, and returns a list of all the results. Suppose we have our same function: func :: a -> Monad b

let mList = map func ["foo", "bar", "baz"] in sequence mList

…is equivalent to…

do foo <- func "foo" bar <- func "bar" baz <- func "baz" return [foo, bar, baz]

If you have a list of monads, you can use sequence or sequence_ to have them all evaluated. Use sequence if you care about the resulting values, use sequence_ if you only care about the monad’s side effect. Which do we want? Let’s look at the signature of insertConv:

*Main> :t insertConv insertConv :: (Temperature a, Temperature b) => Conversion a b -> TableBuilder ()

If we were to use sequence, we’d get a resulting value with the type TableBuilder [()]. Since nobody has any use for a list of empty tuples, we’ll be using sequence_.

So, what does our main look like?

main = do temps <- return (map (insertConv . (toConversion :: Fahrenheit -> (Fahrenheit, Celsius)) . Fahrenheit) [300.0, 280.0 .. 0.0]) putStrLn $ prettyPrint $ buildTable $ do sequence_ temps

This produces the following output:

------------------------------ || Fahrenheit || Celsius || ------------------------------ || 300.0 |> 148.9 || || 280.0 |> 137.8 || || 260.0 |> 126.7 || || 240.0 |> 115.6 || || 220.0 |> 104.4 || || 200.0 |> 93.3 || || 180.0 |> 82.2 || || 160.0 |> 71.1 || || 140.0 |> 60.0 || || 120.0 |> 48.9 || || 100.0 |> 37.8 || || 80.0 |> 26.7 || || 60.0 |> 15.6 || || 40.0 |> 4.4 || || 20.0 |> -6.7 || || 0.0 |> -17.8 || ------------------------------

K&R Challenge 3 and 4: Functional Temperature Conversion

The other day, I implemented the C solution to exercises 3 and 4 in The C Programming Language, today I’ll be implementing the Haskell solution. As a reminder, the requirements are:

Modify the temperature conversion program to print a heading above the table

… and …

Write a program to print the corresponding Celsius to Fahrenheit table.

I could take the easy way out, and implement the Haskell solution almost identically to the C solution, replacing the for loop with a call to map, but that’s neither interesting to do, nor is it interesting to read about. I’ll be taking this problem to the next level.

Requirements

For my temperature program, I’d like it to be able to convert between any arbitrary temperature unit. For the purposes of this post, I will be implementing Celsius, Fahrenheit, Kelvin (equivalent to Celsius, except 0 degrees is absolute zero, not the freezing point of water), and Rankine (the Fahrenheit version of Kelvin).

That said, nothing about my solution should rely on the fact that these four temperature scales are being implemented. A programmer should be able to implement a new temperature unit with minimal changes to the program. Ideally, just by implementing a new type.

Additionally, given a bunch of conversions, the program should be able to output a pretty table showing any number of temperature types and indicating what converts to what.

First, Conversion

This problem is clearly broken up into two sub-problems: conversion, and the table. First, we need to handle conversion. This being Haskell, the right answer is likely to start by defining some types. Let’s create types for our units:

newtype Celsius = Celsius Double deriving (Show) newtype Fahrenheit = Fahrenheit Double deriving (Show) newtype Kelvin = Kelvin Double deriving (Show) newtype Rankine = Rankine Double deriving (Show)

I’ve chosen to make a type for each unit, and since they only contain one constructor with one field, I use a newtype instead of a data. Now, how to convert these? A straightforward solution to this would be to define functions that convert each type to each other type. Functions that look like:

celsiusToFahrenheit :: Celsius -> Fahrenheit celsiusToKelvin :: Celsius -> Kelvin ... rankineToKelvin :: Rankine -> Kelvin rankineToCelsius :: Rankine -> Celsius

A diagram for these conversions looks like this:

many-to-many

That’s a lot of conversions! One might argue that it’s manageable, but it certainly doesn’t meet requirement #1 that implementing a new unit would require minimal work; to implement a new conversion, you’d need to define many conversion functions as well! There must be a better way.

Let’s think back to chemistry class. You’re tasked with converting litres to hours or somesuch. Did you do that in one operation? No, you used a bunch of intermediate conversion to get to what you needed. If you know that X litres are Y dollars, and Z dollars is one 1 hour, then you know how many litres are in 1 hour! These are called conversion factors.

Luckily for us, our conversions are much simpler. For any temperature unit, if we can convert it to and from celsius, then we can convert it to and from any other unit! Let’s define a typeclass for Temperature:

class Temperature a where toCelsius :: a -> Celsius fromCelsius :: Celsius -> a value :: a -> Double scaleName :: a -> String convert :: (Temperature b) => a -> b convert f = fromCelsius $ toCelsius f

Our Temperature typeclass has five functions: functions to convert to and from celsius, a function to get the value from a unit, a function to get the name of a unit, and a final function convert. This final function has a default implementation that converts a unit to celsius, then from celsius. Using the type inferrer, this will convert any unit to any other unit!

convert Rankine 811 :: Kelvin convert Celsius 123 :: Fahrenheit convert Kelvin 10000 :: RelativeToHeck

Now to implement a new temperature, you only need to implement four functions, as convert is a sane solution for all cases. This arrangement gives us a conversion diagram that looks like:

many-to-one

Much better. Let’s go through our Temperature implementations for our types:

instance Temperature Celsius where toCelsius c = c fromCelsius c = c value (Celsius c) = c scaleName _ = "Celsius"

Of course Celsius itself has to implement Temperature. It’s implementation is trivial though; no work needs to be done.

instance Temperature Fahrenheit where toCelsius (Fahrenheit f) = Celsius ((5 / 9) * (f - 32)) fromCelsius (Celsius c) = Fahrenheit ((9 / 5) * c + 32) value (Fahrenheit f) = f scaleName _ = "Fahrenheit"

Now things are heating up. The conversion functions are identical to the C implementation.

instance Temperature Kelvin where toCelsius (Kelvin k) = Celsius (k - 273.15) fromCelsius (Celsius c) = Kelvin (c + 273.15) value (Kelvin k) = k scaleName _ = "Kelvin"

The Kelvin implementation looks much like the Fahrenheit one.

instance Temperature Rankine where toCelsius (Rankine r) = toCelsius $ Fahrenheit (r - 459.67) fromCelsius c = Rankine $ 459.67 + value (fromCelsius c :: Fahrenheit) value (Rankine r) = r scaleName _ = "Rankine"

The conversion between Fahrenheit and Rankine is much simpler than the conversion between Celsius and Rankine; therefore I will do just that. After converting to and from Fahrenheit, it’s a simple matter of calling toCelsius and fromCelsius.

Bringing The Monads

Now that the easy part is done, we get to create the table. Our table should have as many columns as it needs to display an arbitrary number of conversions. To that end, let’s define a data structure or two:

data ConversionTable = ConversionTable [String] [[TableRowElem]] deriving (Show) data TableRowElem = From Double | To Double | NullConv Double | Empty deriving (Show)

The ConverstionTable, like the name suggests, is our table. The list of strings is the header, and the list of lists of TableRowElem are our conversions. Why not just have a list of Double? We need to have our cells contain information on what they mean.

To that end, I created a TableRowElem type. From is an original value, To is a converted value, NullConv represents the case were we convert from some type to the same type, and Empty is an empty cell. The problem of how to place elements into this data structure still remains however. To solve that, things are going to get a bit monadic. Let’s define some intermediate builder types:

type Conversion a b = (a, b) toConversion :: (Temperature a, Temperature b) => a -> (a, b) toConversion a = (a, convert a)

First we have Conversion, and the corresponding toConversion function. This simply takes a unit, and places it in a tuple with its corresponding conversion. Next, we have the TableBuilder:

type TableBuilder a = WriterT [[TableRowElem]] (State [String]) a

Here we have a WriterT stacked on top of a State monad. The writer transformer contains the list of table rows, and the state monad contains the header. The idea is that as rows are “logged” into the writer, the header is checked to make sure no new units were introduced. To this end, if only two units are introduced, the table will have two columns. If 100 units are used, then the table will have 100 columns.

NOTE: I realize that WriterT and State are not in the standard library. I only promised to limit the usage of libraries for Haskell solutions. This means avoiding the use of things like Parsec or Happstack. Frameworks and libraries that vastly simplify some problem or change the way you approach it. To this end, if I feel a monad transformer or anything along these lines are appropriate to a problem, I will use them. I’ll try to point out when I do though. Besides, I could have just re-implemented these things, but in the interest of not being a bad person and re-inventing the wheel, I’ve decided to use a wheel off the shelf.

So, how do we use this TableBuilder? I’ve defined a function for use with this monad:

insertConv :: (Temperature a, Temperature b) => Conversion a b -> TableBuilder () insertConv (a, b) = do oldHeader <- lift $ get workingHeader <- ensureElem a oldHeader finalHeader <- ensureElem b workingHeader lift $ put finalHeader tell [buildRow a b finalHeader []] where ensureElem a h = return $ case ((scaleName a) `elem` h) of True -> h False -> h ++ [(scaleName a)] buildRow _ _ [] r = r buildRow a b (h:xs) r | (scaleName a) == (scaleName b) && (scaleName a) == h = r ++ [NullConv $ value a] | (scaleName a) == h = buildRow a b xs (r ++ [From $ value a]) | (scaleName b) == h = buildRow a b xs (r ++ [To $ value b]) | otherwise = buildRow a b xs (r ++ [Empty])

Yeah, that one is kind of a doosey. Let me walk you through it. This function takes a Conversion, and returns a TableBuilder.

In the first four lines of the do block, we update the header. We lift the State monad, then get we call ensureElem with the first and second units, then we put the new updated header back into the State monad.

The ensureElem function checks the header list to see if the current unit is a member. If it is, the header list is returned unchanged, if it’s not the unit is appended to the end and the new list is returned. In this way, whenever a conversion is added to the table, the header is updated.

After updating the header, we call tell with the result of buildRow, “writing” the row into the Writer monad. The buildRow function recursively adds TableRowElems to the result list depending on the current heading. In this way, conversions are placed in the appropriate column.

In addition to that function, I’ve defined a function to simplify working with the TableBuilder:

buildTable :: TableBuilder a -> ConversionTable buildTable b = let result = runState (runWriterT b) [] in ConversionTable (snd result) (snd $ fst result)

Working with some of these MTL monads can be confusing for people coming from imperative backgrounds. I’ve been working with Haskell for almost a year now and I still get extremely confused by them. It can take some muddling through haddoc pages to work them out, but the good news is that you mainly just need to define one function that takes a monad (in the form of a do block), and returns a whatever. The buildTable function takes a TableBuilder, and returns a ConversionTable. It handles calls to runState and runWriterT, and then unwraps the resulting tuple and builds the ConversionTable.

This function can be called like this:

buildTable $ do insertConv someConversion insertConv someOtherConversion

… and so on. The only thing to remember is that the final value of a for the do block must be (). Conveniently, insertConv return a value of type TableBuilder (), so if the last call is to this function, then you are good. You can also always end it with return () if you like.

Pretty Printing

Finally, we have the matter of printing a nice pretty table. For that, we need yet another function:

prettyPrint :: ConversionTable -> String prettyPrint (ConversionTable h r) = let widestCol = last $ sort $ map length h columnCount = length h doubleCell = printf ("%-" ++ (show widestCol) ++ ".1f") stringCell = printf ("| %-" ++ (show widestCol) ++ "s |") emptyCell = replicate widestCol ' ' horizontalR = (replicate (((widestCol + 4) * columnCount) + 2) '-') ++ "\n" formatRow row = "|" ++ (concat $ map formatCell row) ++ "|\n" formatCell (From from) = "| " ++ (doubleCell from) ++ " |" formatCell (To to) = "> " ++ (doubleCell to) ++ " |" formatCell Empty = "| " ++ emptyCell ++ " |" formatCell (NullConv nc) = "| " ++ (doubleCell nc) ++ " |" in horizontalR ++ ("|" ++(concat $ map stringCell h) ++ "|\n") ++ horizontalR ++ (concat $ map formatRow (normalizeRowLen (columnCount) r)) ++ horizontalR where normalizeRowLen len rows = map (nRL' len) rows where nRL' len' row | (length row) < len' = nRL' len' (row ++ [Empty]) | otherwise = row

Yeah… Sometimes the littlest things take the most work. You’d think all this plumbing we’ve been doing would be the most complecated bit, but you’d be wrong. Let’s try to make sense of this mess function by function:

widestCol = last $ sort $ map length h

This function determines the widest column based on the header. Typically, this is going to be “Fahrenheit”, but it doesn’t have to be. It should be noted that if a data cell is wider than this, then the pretty printer will mess up. Like most things in life, there is room for improvement here. That said, unless you’re converting the temperature of the core of the sun, you probably won’t have an issue here.

columnCount = length h

Returns the number of columns in the table. Used by the horizontal rule function.

doubleCell = printf ("%-" ++ (show widestCol) ++ ".1f")

Ahh, our old friend printf. It exists in Haskell and works in much the same way as it did in C. The doubleCell function converts a temperature value to a string, left aligns it, pads it by widestCol, and has it show one decimal place.

stringCell = printf ("| %-" ++ (show widestCol) ++ "s |")

Much like with doubleCell, this function pads, and left-aligns a string. This is used by the header.

emptyCell = replicate widestCol ' '

This one is pretty self-explanatory. It prints an empty cell of the appropriate width.

horizontalR = (replicate (((widestCol + 4) * columnCount) + 2) '-') ++ "\n"

This function prints a horizontal rule. This will be a solid line of “-” across the width of the table.

formatRow row = "|" ++ (concat $ map formatCell row) ++ "|\n"

This function formats a table data row. It maps formatCell over the list of cells, flattens it, then adds a pretty border around it.

formatCell (From from) = "| " ++ (doubleCell from) ++ " |" formatCell (To to) = "> " ++ (doubleCell to) ++ " |" formatCell Empty = "| " ++ emptyCell ++ " |" formatCell (NullConv nc) = "| " ++ (doubleCell nc) ++ " |"

In this function, much of the work is done. It formats the cell using doubleCell or emptyCell, the applies a border to the cell. It denotes a cell containing a To by adding a > on the left.

Now that we’ve covered the let-bound functions, let’s talk about the actual function body:

horizontalR concat $ map stringCell h) ++ "|\n") horizontalR concat $ map formatRow (normalizeRowLen (columnCount) r)) horizontalR

This bit is prett straightforward. First, it prints a horizontal line. Second, it maps stringCell over the header list, flattens it, and gives it a border. Third it prints another horizontal line. Fourth is maps formatRow over the normalized row list, then flattens it. Finally, one last horizontal line. After this is all said and done, it concats it all together.

You may be wondering about that normalizeRowLen function. If you were paying particularly close attention to the insertConv function, you may have noticed an issue. Let’s walk through it in ghci:

*Main> let fc = toConversion (Fahrenheit 100) :: (Fahrenheit, Celsius) *Main> buildTable $ do insertConv fc ConversionTable ["Fahrenheit","Celsius"] [[From 100.0,To 37.77777777777778]]

We add one conversion, we get two columns. Everything seems to be in order here, but let’s add another conversion and see what happens:

*Main> let fc = toConversion (Fahrenheit 100) :: (Fahrenheit, Celsius) *Main> let cr = toConversion (Celsius 100) :: (Celsius, Rankine) *Main> buildTable $ do {insertConv fc; insertConv cr;} ConversionTable ["Fahrenheit","Celsius","Rankine"] [[From 100.0,To 37.77777777777778],[Empty,From 100.0,To 671.6700000000001]]

See the problem? Let’s add some newlines to make it clearer:

ConversionTable ["Fahrenheit","Celsius","Rankine"] [[From 100.0,To 37.77777777777778], [Empty,From 100.0,To 671.6700000000001]]

As we add more columns, the rows with less columns are never updated to have the new column count. Logically, this is fine, since the extra entries would just be Empty anyways, but our pretty printer would print this table like so:

-------------------------------------------- || Fahrenheit || Celsius || Rankine || -------------------------------------------- || 100.0 |> 37.8 || || || 100.0 |> 671.7 || --------------------------------------------

As you add more and more columns, the problem gets worse and worse. Enter our normalizeRowLen function:

normalizeRowLen len rows = map (nRL' len) rows where nRL' len' row | (length row) < len' = nRL' len' (row ++ [Empty]) | otherwise = row

This is another fairly straightforward function. If the row has the same number of columns as the header, it is returned unchanged. If it doesn’t, Empty is added to the end until it does.

With that, our program is complete. Let’s try it out:

main = do k <- return (toConversion $ Kelvin 100 :: (Kelvin, Rankine)) f <- return (toConversion $ Fahrenheit 451 :: (Fahrenheit, Kelvin)) r <- return (toConversion $ Rankine 234 :: (Rankine, Celsius)) c <- return (toConversion $ Celsius 9 :: (Celsius, Fahrenheit)) nc <- return (toConversion $ Rankine 123 :: (Rankine, Rankine)) putStrLn $ prettyPrint $ buildTable $ do insertConv k insertConv f insertConv r insertConv c insertConv nc

In our main, we create a bunch of conversions. Then we prettyPrint them and putStrLn the result. The following will be printed to the console:

---------------------------------------------------------- || Kelvin || Rankine || Fahrenheit || Celsius || ---------------------------------------------------------- || 100.0 |> 180.0 || || || |> 505.9 || || 451.0 || || || || 234.0 || |> -143.2 || || || |> 48.2 || 9.0 || || || 123.0 || || || ----------------------------------------------------------

Any type that implements Temperature can be put into a table this way. To add a new unit to the program, it’s as easy as implementing four one-line functions!

K&R Challenge 3 and 4: Fiddling With Formatting Strings

Today I’ll be doing things a bit differently. Since exercise 3 and 4 are quite trivial, and because the Haskell implementation will differ significantly from the C solution, I’m going to break them up into separate posts. In this post, I will implement the C solution to problems 3 and 4, and another day I’ll implement the Haskell solution.

The problem for exercise 3 is:

Modify the temperature conversion program to print a heading above the table

The program in question is as follows:

int main (int argc, char ** argv) { float fahr, celsius; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr = lower; while (fahr <= upper) { celsius = (5.0/9.0) * (fahr-32.0); printf("%3.0f %6.1f\n", fahr, celsius); fahr = fahr + step; } return 0; }

If compiled and executed, this will print the following:

0 -17.8 20 -6.7 40 4.4 ... 280 137.8 300 148.9

Strictly speaking, this is a trivial problem. The solution is to add the following line before the while loop:

printf("F C\n");

Now, we have our header:

F C 0 -17.8 20 -6.7 ... 280 137.8 300 148.9

Problem 4 is as follows:

Write a program to print the corresponding Celsius to Fahrenheit table.

This can be implemented by simply changing the line…

celsius = (5.0/9.0) * (fahr-32.0)

…to…

fahr = (9.0/5.0) * celsius+32;

…and changing the corresponding printf calls to swap the pairs. Nothing groundbreaking here. Now, let’s talk about my combined solution.

The Solutions

First, we tackle exercise 3. Technically, my previous answer fit the requirements, but I wouldn’t call it “good”. Clearly some pretty printing is in order. Additionally, I’d like to make a table with four columns to print the Fahrenheit to Celsius conversion next to the Celsius to Fahrenheit conversion. To that end, let’s add the following above the loop:

printf("Celsius <- Fahrenheit | Celsius -> Fahrenheit\n"); printf("----------------------|----------------------\n");

This is pretty self-explanatory. We Make some nice pretty columns with a horizontal line between the header and the data. On the left is the Fahrenheit to Celsius conversion, and the right is the Celsius to Fahrenheit conversion. Next, the table.

We’ll need to add extra variables to store the exercise 3 and 4 temperatures. Let’s modify our variables:

float fahr_ex3, celsius_ex3, fahr_ex4, celsius_ex4;

Now we need to calculate both conversions given the same interval:

celsius_ex3 = (5.0/9.0) * (fahr_ex3-32.0); fahr_ex4 = (9.0/5.0) * celsius_ex4+32;

After that’s done, we need to print the table row we just calculated:

printf("%7.1f <- %-10.1f | %7.1f -> %-10.1f\n", celsius_ex3, fahr_ex3, celsius_ex4, fahr_ex4);

Here we’ve updated our printf statement. We now print all four variables on one line. Additionally we’ve updated the formatting options to line the entries up neatly within the columns.

Finally, we need to modify some loop bookkeeping:

fahr_ex3 = lower; celsius_ex4 = lower; while (fahr_ex3 <= upper && celsius_ex4 <= upper) { /*Do Stuff*/ fahr_ex3 = fahr_ex3 + step; celsius_ex4 = celsius_ex4 + step; }

And we’re done! If you compile and execute this new program, the following should print to the terminal:

Celsius <- Fahrenheit | Celsius -> Fahrenheit ----------------------|---------------------- -17.8 <- 0.0 | 0.0 -> 32.0 -6.7 <- 20.0 | 20.0 -> 68.0 4.4 <- 40.0 | 40.0 -> 104.0 15.6 <- 60.0 | 60.0 -> 140.0 26.7 <- 80.0 | 80.0 -> 176.0 37.8 <- 100.0 | 100.0 -> 212.0 48.9 <- 120.0 | 120.0 -> 248.0 60.0 <- 140.0 | 140.0 -> 284.0 71.1 <- 160.0 | 160.0 -> 320.0 82.2 <- 180.0 | 180.0 -> 356.0 93.3 <- 200.0 | 200.0 -> 392.0 104.4 <- 220.0 | 220.0 -> 428.0 115.6 <- 240.0 | 240.0 -> 464.0 126.7 <- 260.0 | 260.0 -> 500.0 137.8 <- 280.0 | 280.0 -> 536.0 148.9 <- 300.0 | 300.0 -> 572.0

Aside from just looking nicer, I’d say this is more in line with the spirit of the exercise. After all, when faced with a requirements document, you can either give a customer exactly what they asked for, or you can give them what they actually want. Which is more likely to get sent back for rework?

The complete source for this solution:

#include <stdio.h> int main (int argc, char ** argv) { float fahr_ex3, celsius_ex3, fahr_ex4, celsius_ex4; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr_ex3 = lower; celsius_ex4 = lower; printf("Celsius <- Fahrenheit | Celsius -> Fahrenheit\n"); printf("----------------------|----------------------\n"); while (fahr_ex3 <= upper && celsius_ex4 <= upper) { celsius_ex3 = (5.0/9.0) * (fahr_ex3-32.0); fahr_ex4 = (9.0/5.0) * celsius_ex4+32; printf("%7.1f <- %-10.1f | %7.1f -> %-10.1f\n", celsius_ex3, fahr_ex3, celsius_ex4, fahr_ex4); fahr_ex3 = fahr_ex3 + step; celsius_ex4 = celsius_ex4 + step; } return 0; }

UPDATE: The Haskell solution can be found here.

K&R Challenge 2: The Specter of Undefined Behavior

Continuing with the K&R Challenge, today we’ll be doing exercise 2:

Experiment to find out what happens when printf‘s argument string contains \c, where c is some character not listed above.

Basically, we’ll be trying to print “\c” to the screen, and seeing what C does.

In C

The code for this is very simple:

int main (int argc, char ** argv) { printf("Trying to print: \c\n"); return 0; }

Like last time, we’ll compile it using:

gcc -Wall ex2.c -o ex2

Right away, you should get a compiler warning:

ex2.c: In function ‘main’: ex2.c:10:11: warning: unknown escape sequence: '\c' [enabled by default] printf("Trying to print: \c\n");

Notice that it is a warning, not an error, and that the compilation still succeeded and produced an executable. My friends, you have just witnessed Undefined Behavior. Undefined behavior is one of the defining aspects of C. Technically, it’s a feature, not a bug, and it is both a blessing and a curse.

It’s a blessing in that it’s one of the things that enables C’s unmatched performance. It’s a curse in that the compiler is allowed by the spec to do whatever it wants when it encounters undefined behavior: it could do something really helpful, it could segfault, or it could delete your hard drive. It could even unleash lions in your house and there’s nothing the police or congress could do about it.

Moral of the story: don’t use undefined behavior unless you really know what you’re doing. Even then, I’d argue it’s a code smell.

So, what happens when you run the compiled executable? Always fearless in the pursuit of truth, I ran it. The following was printed to the console:

Trying to print: c

The GNU C Compiler decided to be nice and just strip the “\”, then print the message. It’s a good thing I have managed to avoid angering Richard Stallman, because gcc could very well have kidnapped my wife. That would have been a problem.

And In Haskell

Nothing about this exercise relies on the behavior of printf, therefore even though Haskell has it, I’ve elected not to use it. The implementation of this exercise in Haskell looks like this:

main :: IO () main = putStrLn "Trying to print: \c\n"

As before, this can be run using:

runhaskell ex2.hs

…but when you try it, the following error is displayed, and compilation fails:

ex2.hs:2:62: lexical error in string/character literal at character 'c'

Where C allowed the undefined escape sequence as undefined behavior, Haskell throws a compilation error. This is a Good Thing. Undefined behavior is a prolific source of bugs, and while I understand why it’s allowed, I can’t say that I agree that the tradeoff is worth it. Luckily for us, this isn’t a feature unique to Haskell. Most high level languages don’t have undefined behavior. Unfortunately, this is one of the reasons that most programming languages are orders of magnitude slower than C.

Regardless, undefined behavior is a fact of life for the C programmer. It’s one of the tradeoffs they make to use the language. These design decisions were made decades ago, and are unlikely to change. For this reason, this is a good exercise.

The families of those who were turned to stone by their compilers after doing this exercise are in our prayers…

Introducing The K&R Challenge

Recently, I inherited a copy of The C Programming Language, aka “K&R”. I was borrowing it, and today I tried to give it back, but the book’s former owner didn’t want it back. I was going to put it back in it’s deep dark box, likely to never been seen again, when I had a thought. Surely there’s something in this ancient tome of knowledge waiting for me.

On a whim, I decided to go through the exercises. This post marks the first task in the K&R Challenge.

The rules: the book is about the C programming langauge. The book is not about glib, linux programming, gtk, or any other library or framework. Therefore, the solutions to the exercises should only use the standard library, unless the book specifically calls out a library. I will attempt to stay as true to the wording of the excercise as possible.

Seems pretty straightforward, right? Well this brings us to part two; after doing an excercise in C, I will do it in Haskell. For these examples, I will attempt to implement the program in idiomatic Haskell. The ends are more important than the means here, so if the example calls out a specific function or technique, I will only use it if I feel it’s the best way to do it. For the Haskell portion, I will minimize the use of libraries as well.

So, without further ado…

Excercise 1.1

Excercise 1.1 is simply to implement “Hello World” Shockingly this is a trivial problem in C:

#include "stdio.h" int main (int argc, char ** argv) { printf("Hello World!\n"); return 0; }

First, we include stdio.h to gain access to printf, then we say hello to the world. You’ll notice I put curly braces on their own lines. Sue me.

To compile this, assuming you’re using gcc, enter the following:

gcc -Wall ex1.c -o ex1

Running the produced program produces the output:

Hello World!

Amazing!

In Haskell, this is even easier:

main :: IO () main = putStrLn "Hello World!"

As putStrLn and the IO monad are in prelude, this program does not require any imports.

To run this, simply type the following at the prompt:

runhaskell ex1.hs

Which produces the following output:

Hello World!

If you were able to follow all of that, then congratulations! You are an elite hacker! Your black fedora is in the mail.

%d bloggers like this: