In today’s Programming Praxis exercise our task is to print the weather forecast for a given city in the United States. Let’s get started, shall we?
The Scheme solution uses wget to download the file first. In Haskell, however, we can just use a library for this purpose.
import Network.HTTP
Thanks to the library, the actual function is trivial: download the file and print either the error message if it can’t be found or the weather forecast if it can.
showWeather :: String -> String -> IO () showWeather state city = either print (putStrLn . rspBody) =<< simpleHTTP (getRequest $ "http://weather.noaa.gov/pub/data/\ \forecasts/city/" ++ state ++ "/" ++ city ++ ".txt")
A quick to see if everything works correctly:
main :: IO () main = showWeather "mo" "st_louis"
Not very warm at 47 degrees Fahrenheit, but at least it’s dry.
Tags: bonsai, code, forecast, Haskell, kata, praxis, programming, weather
November 5, 2010 at 9:25 pm |
Nice solution, short and simple. However, the question states to get the weather forecast ‘for a requested city’. I’m not sure they mean what I think they mean, but just for the sake of challenge, could you retrieve the weather forecast without knowing the state? You’d have to iterate through all the state subfolders.
November 5, 2010 at 10:02 pm |
PS – to that effect, I wrote a method in C# that would get a list of all the states from the directory itself. I’m quite sure this is an extremely sloppy and inefficient implementation, though:
private static List GetStates(string url)
{
string result=String.Empty;
try
{
WebClient client = new WebClient();
result = client.DownloadString(url);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return null;
}
string[] split = result.Split(‘\n’);
string identifier = ” <a href=\"";
List states = new List();
for (int i = 0; i < split.Length; i++)
{
if (split[i].StartsWith(identifier))
{
split[i] = split[i].Substring(identifier.Length, 2);
states.Add(split[i]);
}
}
return states;
}
November 5, 2010 at 10:03 pm |
In the above code fragment, the argument passed in is :
http://weather.noaa.gov/pub/data/forecasts/city/
November 5, 2010 at 10:48 pm |
Here’s what I came up with:
import Network.HTTP import Text.Regex.Posix get :: String -> IO String get = fmap (either show rspBody) . simpleHTTP . getRequest showWeather :: String -> IO () showWeather city = do let base = "http://weather.noaa.gov/pub/data/forecasts/city/" states <- fmap (map last . (=~ "href=\"(../)\"")) $ get base cities <- fmap (zip states) $ mapM (get . (base ++)) states let state = fst . head $ filter ((=~ city) . snd) cities putStrLn =<< get (base ++ state ++ city ++ ".txt")