Kernighan & Ritchie word count example program in a functional language

clojure, common-lisp, functional-programming, haskell, scheme

Solution

Basically, in a functional styly you'll want to divide the IO operation of getting your stream of data from the pure operation of some stateful transistion based on the current character and the current state.

The Haskell solution from Tikhon is very clean but performs three passes on your input data and will result in the entire input being contained in memory until the result is computed. You can process data incrementally, which I do below using the Text package but no other advanced Haskell tools (which could clean this up at the expense of understandability by non-Haskellers).

First we have our preamble:

{-# LANGUAGE BangPatterns #-}

import Data.Text.Lazy as T
import Data.Text.Lazy.IO as TIO

Then we define our data structure to hold the state of the process (number of characters, words, and lines along with the State IN/OUT):

data Counts = Cnt { nc, nl, nw :: !Int
                  , state :: State  }
        deriving (Eq, Ord, Show)

data State = IN | OUT
        deriving (Eq, Ord, Show)

Now I define a "zero" state just for easy use. I'd normally make a number of helper functions or use a package like lense to make incrementing each field in the `Counts` structure simple, but will go without for this answer:

zeros :: Counts
zeros = Cnt 0 0 0 OUT

And now I translate your series of if/else statements into a pure state machine:

op :: Counts -> Char -> Counts
op c '\n' = c { nc = nc c + 1, nw = nw c + 1, nl = nl c + 1, state=OUT }
op c ch | ch == ' ' || ch == '\t' = c { nc = nc c + 1, state=OUT }
        | state c == OUT = c { nc = nc c + 1, nw = nw c + 1, state = IN }
        | otherwise  = c { nc = nc c + 1 }

Finally the `main` function just gets the input stream and folds our operation over the characters:

main = do
        contents <- TIO.getContents
        print $ T.foldl' op zeros contents

EDIT: You mentioned not understanding the syntax. Here is an even simpler version that I will explain:

import Data.Text.Lazy as T
import Data.Text.Lazy.IO as TIO

op (nc, nw, nl, st) ch
  | ch == '\n'              = (nc + 1, nw + 1 , nl + 1, True)
  | ch == ' ' || ch == '\t' = (nc + 1, nw     , nl    , True)
  | st                      = (nc + 1, nw + 1 , nl    , False)
  | otherwise               = (nc + 1, nw     , nl    , st)

main = do
        contents <- TIO.getContents
        print $ T.foldl' op (0,0,0,True) contents

The `import` statements give us access to the `getContents` and `foldl'` functions we use.

The `op` function uses a bunch of guards - parts like `| ch = '\n'` - which is basically like a C if/elseif/else series.

The tuples `( ... , ... , ... , ... )` contain all our state. Haskell variables are immutable, so we make new tuples by adding one (or not) to the values of the previous variables.

Problem

I have been reading a little bit about functional programming on the web lately and I think I got a basic idea about the concepts behind it. I'm curious how everyday programming problems which involve some kind of state are solved in a pure functional programing language. For example: how would the word count program from the book 'The C programming Language' be implemented in a pure functional language? Any contributions are welcome as long as the solution is in a pure functional style. Here's the word count C code from the book: ``` #include <stdio.h> #define IN 1 /* inside a word */ #define OUT 0 /* outside a word */ /* count lines, words, and characters in input */ main() { int c, nl, nw, nc, state; state = OUT; nl = nw = nc = 0; while ((c = getchar()) != EOF) { ++nc; if (c == '\n') ++nl; if (c == ' ' || c == '\n' || c = '\t') state = OUT; else if (state == OUT) { state = IN; ++nw; } } printf("%d %d %d\n", nl, nw, nc); } ```

Original source