Here is a bit of code I’ve been tinkering with, it’s a simple CSV parser in Scheme that I am using to parse Stock data so that I can back test trading strategies.

(define (csv->list file_name)
  (with-input-from-file file_name
	(lambda ()
	  (let reading ((chars '()) (rows '()) (cols '()))
		(let ((char (read-char)))
		  (cond
			((eof-object? char) (reverse rows))
			((char=? char #\newline)
			 (reading '() (cons 
					(reverse 
					  (cons (list->string (reverse chars)) cols)) 
					rows) '()))
			((char=? char #\,)
			 (reading '() rows
			   (cons (list->string (reverse chars)) cols)))
			(else
			  (reading (cons char chars) rows cols))))))))
(csv->list "D:/Projects/StockMarket/NASDAQ_20101105.txt")

The above is why I love Scheme. Such a succinct language.

How to design functions in Scheme

The above small function follows the basic function design pattern, we begin with the target or most significant goal state: EOF. What do we do?

((eof-object? char) (reverse rows))

We return the reverse of the rows. This is because cons prepends new elements. Now we could call (append) instead of (cons) but (append) has to find the end of the list each time, adding unnecessary overhead.

Our next goal, or major target is the end of line, or #\newline character.

((char=? char #\newline)
  (reading '() (cons 
    (reverse 
	(cons (list->string (reverse chars)) cols)) 
     rows) '()))

If the current char equals a new line, we need to:

  1. Produce the column value (newline works like comma)
  2. Prepend the column value to the lists (cols)
  3. Prepend the (reverse cols) list onto (rows)
  4. Recurse into (reading) with the new (rows) and ‘() for both chars and cols

The next goal state, is encountering a comma, or the delimiter:

((char=? char #\,)
    (reading '() rows
      (cons (list->string (reverse chars)) cols)))

In this case we need to recurse into (reading) with ‘() for chars, as well as prepending the (reverse) of (chars) stringified to (cols).