Creating A Binary Search Tree In Racket

Firstly, what is a binary tree?

A binary tree is a simple data structure where every node points to two more nodes, culminating in some type of final data type (usually null or nil).

1
2 3
4 5 6 7

A badly unbalanced binary tree might look more like this:

1
2 null
3 null
4 5 null
null null null 6 7

Both of these examples are not really sorted, and thus are not very useful as binary search trees. They look sorted in this format, and there is an order to them, but it’s not what I’ll be talking about when I’m trying to get to grips with the data structure in this post. A properly-sorted balanced binary tree would look like this:

4
2 6
1 3 5 7

This is much more useful, as it means that we can find our way around the tree by checking the value held by the current node against the target value. For instance, in the case of 5, we check to see if it’s larger than (4). It is, so we go right to the node with the value (6). 5 is smaller than 6 so we go left, and we’re there!

In a sorted list, we would need to make 5 checks to find our number, while for the BST we only need to make 3 (2 to traverse the tree, one to make sure we’ve ended on the correct value).

In an unsorted or unbalanced tree this would take a lot longer, and if the value didn’t exist in an unsorted tree the operation would take O(n) time to return #f. In a balanced, sorted binary search tree it takes O(log n) time to search for the value, even if the value doesn’t exist.

So, trees are most useful when they’re already sorted and balanced – or ideally automatically self-sorting and balancing.

Binary trees are closely related to a clever data structure called a skip list. The concept behind a skip list (crudely) is that a list is created:

(list 1 2 3 4 5 6 7 8 9)

Followed by a second list with only every other number, to speed up traversal times:

(list 1 3 5 7 9)

If we reach a number in the skip list which is larger than the target number, we know that we’ve moved one place past the target number. If the number matches, we’ve found the number. At a stroke, we’ve halved the search time for this list!

If you build your skip list to its intended height…

(list 1)
(list 1 5)
(list 1 5 9)
(list 1 3 5 7 9)
(list 1 2 3 4 5 6 7 8 9)

Looks a lot like a balanced binary tree!

So how do we make a binary tree in Racket?
(struct node (x left right)
#:transparent)

Ta-da.

Obviously there’s more to a binary tree than this, but that’s the basics of it. Once you have a node with a place for values, a left node and a right node, you’ve basically got a binary tree.

You can also extend the trees by hand:

(define tree (node 1 (node 2 null null) (node 3 null null)))

This is pretty rubbish though. If we treated linked lists like this in Racket, we’d have to make everything using (cons x (cons y null)), when what people actually do in the real world is write (list x y).

For this next part, I first created a leaf struct. Leaves should typically be very populous, so I wanted to identify them to save a large number of function calls.

(define-struct/contract leaf ([x (not/c null?)])
#:transparent)

Just the value, and it’s not allowed to be null.

Next, we create a function which takes a list and spits out a sorted, unbalanced tree.

;Modified quicksort to create ordered (but not balanced) binary search tree
(define (unsorted-list->binary-tree xs)
    (if (null? xs)
        null
        (if (null? (cdr xs))
            (leaf (car xs))
            (let* ([hd (car xs)]
                   [tail (cdr xs)]
                   [left (filter (lambda (x) (< x hd)) tail)]
                   [right (filter (lambda (x) (>= x hd)) tail)])
              (node hd (unsorted-list->binary-tree left) (unsorted-list->binary-tree right))))))

A simple modified racket quicksort function, similar to the quicksort I’ve discussed here before.

Sample input/output:

> (unsorted-list->binary-tree (list 1 2 3 4 5 6))
(node 1 '() (node 2 '() (node 3 '() (node 4 '() (node 5 '() (leaf 6))))))
>(unsorted-list->binary-tree (list 4 5 7 12 1 2))
(node 4 (node 1 '() (leaf 2)) (node 5 '() (node 7 '() (leaf 12))))

Meanwhile, turning a sorted list into a balanced tree is just as easy:

(define (sorted-list->balanced-tree xs)
    (if (null? xs)
        null
        (if (null? (cdr xs))
            (leaf (car xs))
            (let* ([n (floor (/ (length xs) 2))]
                   [mid (list-ref xs n)]
                   [left (take xs n)]
                   [right (drop xs (+ n 1))])
             (node mid (sorted-list->balanced-tree left) (sorted-list->balanced-tree right))))))

We don’t know what the median of a list is until it’s sorted, meaning I can’t balance a list that isn’t sorted without seeking out a different algorithm or data structure. What we could do is use a slightly different data structure, the Red-Black tree, but I’m still getting to grips with how and why exactly that structure works, so I’d rather not get into that now.

In other words, we might as well take an already-sorted list, then balance it.

If we don’t want to just feed lists into our tree, we make our tree-making function variadic. A variadic function is a function able to take a variable number of arguments.

In Racket, it’s simple:

;To create a binary tree from a variadic argument
(define binary-tree
  (lambda xs
    (letrec ([f (lambda (xs)
            (if (null? xs)
            null
            (let* ([hd (car xs)]
                   [tail (cdr xs)]
                   [left (filter (lambda (x) (< x hd)) tail)]
                   [right (filter (lambda (x) (>= x hd)) tail)])
             (if (null? tail)
                 (leaf hd)
                 (node hd (f left) (f right))))))])
    (f xs))))

If you’re wondering why I’ve bothered with the local recursive function (f xs) here, it’s because the variadic argument is a list as soon as it is passed to the function. Thus, if we tried recursing on it our function would get confused and think there was only one argument and stop recursing – unless we put a cond statement in to differentiate between lists and other types of input and…yes. This could get counter-productive.

This function works in the exact same way as the one we saw earlier, only it takes different numbers of arguments rather than different lengths of lists.

Finally, here is how I create a balanced tree with variadic arguments and of different types, and also a kth-select algorithm for any ordered tree in Racket:

;Requires a sign (e.g. <, >, <=, string<?) as the first argument to sort the rest of the arguments
(define balanced-tree
  (lambda xs
    (letrec ([f (lambda (xs)
                    (if (null? xs)
                        null
                        (let* ([n (floor (/ (length xs) 2))]
                               [mid (list-ref xs n)]
                               [left (take xs n)]
                               [right (drop xs (+ n 1))])
                          (if (null? (cdr xs))
                              (leaf mid)
                              (node mid (f left) (f right))))))])
        (f (sort (cdr xs) (car xs))))))
;count-tree-members - useful for kth selection algorithm
(define (count-tree-members ts)
  (cond
    [(null? ts) 0]
    [(leaf? ts) 1]
    [(node? ts)
     (+ 1 (count-tree-members (node-left ts)) (count-tree-members (node-right ts)))]))
;selection algorithm on tree
(define  (exn:fail "Error! Value k is too large or small for the search tree." (current-continuation-marks)))
(define (kth-select ts k)
 (letrec ([f (lambda (ts k)
              (cond
               [(null? ts) (raise )]
               [(leaf? ts) (if (= k 1) (leaf-x ts) (raise ))]
               [(node? ts) (let ([left (add1 (count-tree-members (node-left ts)))])
                             (cond
                               [(= k left) (node-x ts)]
                               [(> k left) (f (node-right ts) (- k left))]
                               [(< k left) (f (node-left ts) k)]))]))])
   (f ts k)))

In the mean-time, I’ve been fiddling around with small improvements to the Nearest Neighbour algorithm for the Travelling Salesman Problem (for fun, I’m not a crank I promise!), and continuing to work on my game in Java.

Here is the worst video this is it it is the worst one there is nothing worse than this.

Tagged , , , ,

Learning From Project Euler

If you’re just starting out with programming, like me, and haven’t yet checked out Project Euler and GitHub, please do. They’re not hard to get into, and I’ve found them really useful!

Project Euler is a particularly powerful impetus for you to improve on your programming skills. Like Codecademy, you start out simple and work your way up through challenges – unlike Codecademy, the challenge is actually worthwhile.

Github, meanwhile, is really useful for storing the code you’re working on and encouraging commentary/interaction with your fellow programmers. It’s also an introduction to version control, which is a useful thing to understand.

I’d urge you to listen to the people at Project Euler and avoid committing Problem answers to Github, as it kind of takes away from the challenge.

Project Euler

In the in-between, a problem with Project Euler forced me to re-design the function I built here, and I’m pretty happy with the results.

My original prime-finding function was taking well over a minute, so I had no choice but to refactor.

It’s a great demonstration of how Project Euler forces you out of your comfort zone – in my case, into Racket vectors. Even though they’re essentially the same as arrays in most other languages, something in me balked at using vectors in Racket. Something about their mutability struck me as not-right in a language so defined by its immutability.

Here’s the first draft of what I wrote, working with vectors to create a static sieve of eratosthenes of length (n):

(define (primes-to x)
  (letrec ([f (lambda (count vs n)
                (cond
                  [(= n 0) (begin (vector-set! vs 0 #f) (f (+ count 1) vs (+ n 1)))]
                  [(= n 1) (begin (vector-set! vs 1 #f) (f (+ count 1) vs (+ n 1)))]
                  [#t
                   (if (= n x)
                       vs
                       (if (< count x)
                           (if (= n count)
                               (f (+ count n) vs n)
                               (begin (vector-set! vs count #f)  (f (+ count n) vs n)))
                           (f (+ n 1) vs (+ n 1))))])

                )])
    (f 0 (make-vector x #t) 0)))

Since you can access vector members using vector-ref, and this program currently runs all the way to (= n x) – which means that we check almost every item on the list – there’s a lot of optimisation which could be done, and done profitably.

The important thing is (if (= n count), which takes away progressively larger chunks of things to check as the formula grows – I believe this puts it in O(n log n) space, or close to it. With this formula, I can check two million digits in ~6 seconds and I could cut that down substantially with some quick tweaks.

Eventually, I’ll hit a wall with the sieve of eratosthenes, and be forced to use the sieve of atkin – which I do not understand and actually dread facing. When I do have to face it, though, it will magically transform into a fun and interesting challenge. That’s the magic of Project Euler!

Last day, Capricorn-15s

Tagged , , ,

Merge Sort In Racket

I talked briefly about merge sort only yesterday, in my post about quicksort.

I also mentioned Lisp, which in the form of “Common Lisp” is a really nice practical language I really intend to learn properly one day. The more I learn about it, the more startled I am at how far ahead of its time it was – or more accurately, at how primitive popular ‘modern’ programming languages such as C, Java and C++ can be. Note that primitive doesn’t mean easy to grasp – if anything, the lower levels of abstraction possible with OOP and procedural languages mean that it’s harder to build the same kinds of things and more difficult to understand what’s going on once you have assembled a simulacrum of the abstraction you intended.

The things people build in these languages constantly astound me, and they have the added bonus that they’re often backed by powerful enough compilers that they’ll run much faster than their FP brethren. I’ve kind of got away from the topic though.

Merge Sort

So yes. Merge sort. Off we go.

I had a naive view that merge sort was quicksort’s less glamorous older brother, until watching the videos on the preview of my Coursera course in algorithm design, I realised that algorithm design and speed were incredibly complex issues subject to a whole host of factors.

I also realised, on trying to implement a quick and ugly merge sort in Racket, that it was harder to do than I had anticipated.

Merge sort pretty much forces you to create at least two different functions:

  • A zipping-up function, which merges ordered arrays, lists or vectors into bigger ordered listy things
  • An unzipping function, which splits the initial lists until they are small enough that ordering them is a trivial operation

Generally, either the zipping-up function or the unzipping function can also take care of ordering the tiny little lists.

For the tiny little lists, we either order them at length 2 by a simple conditional check, reversing the list if they’re “out of order”, at length 7 by doing an insertion sort (I’ve chosen this length because it’s the same length Java apparently use in the default Java sort function (source: Wikipedia) or at length 1 by simply having the zipping-up function handle them.

Length 1 is the easiest for me, so I go with that. I’d probably go with the length 7 option if I was actually building a sort function of my own for god-knows-what reason.

Let’s build our zippy function then – it’s going to be really simple. As before, comments are marked out with a semi-colon.

(define (merge-lists xs ys)
  (cond
    [(null? xs) ys]
;if xs is empty, return ys [(null? ys) xs]
;if ys is empty, return xs [(< (car xs) (car ys))
;if the head of list "xs" is bigger than head of list "ys" (cons (car xs) (merge-lists (cdr xs) ys))]
;cons head xs to (recurse) [#t ;I use true as an else cond here - possibly bad style? (cons (car ys) (merge-lists xs (cdr ys)))]))
;cons head ys to (recurse)

Sure, it only sorts ordered lists – but we’re gonna make sure these are ordered in the sort function.

(define (merge-sort xs)
  (cond
    [(or (null? xs) (null? (cdr xs))) xs]
    [(null? (cddr xs))
     (merge-lists (list (car xs)) (cdr xs))]
    [#t
     (let ([x (ceiling (/ (length xs) 2))])
       (merge-lists (merge-sort (take xs x))
                    (merge-sort (drop xs x))))]))

Wow, we’re done. That was SO easy. I love Racket.

All we do is check if the list is empty (null?) or has a length of 1 (if its tail is null? it must have a length of 1). If either of these is the case, we return the list.

The next bit uses (cddr), which checks if the tail of the tail is null. Similarly, (cdddr) would return the tail of the tail of the tail of the list, or (cadr) would return the head of the tail of the list – it’s simply a matter of combining (car) – head – and (cdr) – tail – in the proportions you desire. If this returns true, we merge-lists on a list of the head of list and tial of list.

But why do we specifically turn (car xs) into a list, and keep (cdr xs) as-is?

Well, that’s due to how linked lists work.

A pair in Racket, and other “tuples” in other languages, are constructed by (cons)ing values together. So (cons 1 2) results in a pair ‘(1 2). A linked list can be constructed by (cons)ing another value to this pair (e.g. (cons 1 (cons 2 3))).

However, a list is only officially a list in Racket if it ends in a null value, for many reasons – we’ve seen some of them here! When we find where the null value is, we know to end the recursive function for sure. So (cons 1 (cons 2 3)) – not a list. (cons 1 (cons 2 null)) – a list.

This affects us here because the head of our list cannot end in null – it is by definition a single value. Although the tail looks like a single value, ‘(x), it’s actually a list ‘(cons x null). So the head goes in a brand new list, while the tail remains as it is.

Finally, we define a local variable equal to half the list’s length (rounded up with the (ceiling) function) and we call (merge-list) on the first half and second half of our list, divided up using the (take) and (drop) functions from the Racket library. They do more-or-less what you’d expect – take the list up to position x, or drop the list up to position x. We keep merging and merging our merge-sort function until we finally end up with a 2-length list, at which point we order and merge them – and merge this with the other list that has been merged – and so on, all the way back until the function returns.

It’s nice and fast, we separate our concerns prettily, and it’s not even as complex as quicksort – merge sort is awesome.

As always, if you’ve seen something egregious in this post, please correct me. I make these posts primarily to learn, so anything you can let me know is gratefully accepted!

Tagged , , , ,

Standard ML: Pattern Matching And Datatypes

SML is another functional language, but its syntax is a lot more familiar than Racket’s to the average newbie programmer. Values are assigned using the “=” sign, addition works the way you’d expect it to —

(i + 1)

not

(+ i 1)

— and there is a comforting similarity to English/mathematical phrasing (“let val x = 2 in x + 3 end”). That said, I found the syntax and semantics of Standard ML far harder to pick up than I did Racket’s.

We learned using Emacs and a Read-Eval-Print-Loop. So; I can now fumble my way through a REPL, create and delete files, switch buffers, copy and paste in Emacs with some small amount of speed. I suppose that’s another little life skill which might come in handy at some point.

To look at what I learned from Standard ML, I chose to use datatypes and pattern matching. These stood out most strongly on the course as essential parts of the SML language’s ‘feel’.

As ever, I’m just showing this code because it puts pressure on me to keep improving, not as a demonstration of what you should do. If you’re just getting interested in SML, don’t use my code as an example!

datatype Count = Num of int
		  | Fizz
		  | Buzz
		  | FizzBuzz
		  | NoCount

This is how we start off, defining a datatype “Count”.

We need to define “Count” because we’re going to be returning a list from our FizzBuzz function, and in SML every list element must be the same. We could have tried turning everything into one long string, or a list of lots of short strings, but that would create problems later.

All this means is that something called “Num 1”, “Num 2”, “Fizz”, “Num 4”, “Buzz” … and “Fizzbuzz” are all of the same type of thing, counts, just like the numbers 1 2 3 4 5…15 are all ints.

In theory in Standard ML you could have a “One True Datatype” which encompassed absolutely everything. It’s at that point that it’s really unlikely that SML was ever the language you should have been using in the first place.

fun fizzbuzz (x:int,y:int) =
    let fun fb(x,acc) =
	    if x<=0
	    then acc
	    else
		if x mod 15 = 0
		then fb(x-1,FizzBuzz::acc)
		else
		    if x mod 3 = 0
		    then fb(x-1,Fizz::acc)
		    else
			if x mod 5 = 0
			then fb(x-1,Buzz::acc)
			else fb(x-1,Num (x+y)::acc)
    in fb(x,[])
    end

This is the fizzbuzz-making function.

It takes two ints, an int for length (x) and an int to determine our starting point (y, where “0” will start us off at “1”), and then creates a sequence from FizzBuzz in accordance with those two ints.

“::” in SML is the same as “cons” in the Lisp family, and “[]” is effectively the same as “null” or “empty”. Confusingly, “null” in SML is a boolean operator equivalent to “null?” in Racket.

The logic of it’s pretty simple. We create a temporary tail-recursive function with an accumulator, and kick it off (without using “y” as an argument — “y” is going to remain constant throughout the process so that’d be pointless).

If we hit one of our targets (multiple of 3, multiple of 5, multiple of 15), we cons a “Count” datatype of the appropriate sort to our accumulator. If we don’t, we simply cons a “Count” datatype of “Num i” to our accumulator list, with i equal to x + y.

Once x has counted down to 0, we return the accumulator list, nicely sorted from lowest to highest:

fizzbuzz(20,4);
val it =
  [Num 5,Num 6,Fizz,Num 8,Buzz,Fizz,Num 11,Num 12,Fizz,Buzz,Num 15,Fizz,...]
  : Count list

Simple.

Now before we translate it back using nested pattern matching, we quickly create some exceptions.

exception NoListEntered
exception ImpossibleListEntered

Well, that was thrilling.

On to the function I wrote:

The arguments this time are a list of Counts “xs”, and a starting point “y” (which should match the starting point of Counts, or you will probably get an error – there are ways of improving this function).

It was at this point that I realised that the function was pretty damn stupid in this form, as we could get the same results with a function which simply, well, counted for the length of a list from a given starting point. Creating a “proper” reverse fizzbuzz would be more complicated than this.

I decided to go ahead with the function in this form for the reason that these posts are not supposed to be solving problems — they’re demonstrations that I understand certain concepts in a language.

In that respect, this function does what I want it to. It type-checks, it returns the correct result, it uses pattern-matching and it uses datatypes. It really doesn’t have to do those things, and it’s possibly the most silly way of solving this “problem”, but it does anyway.

dealwithit.jpeg

fun reverseFizzbuzz (xs:Count list, y:int) =
    let fun fb(xs, acc, lastNum, lastCount) =
	    if null xs
            then
		case lastCount of
		    NoCount => raise NoListEntered
		  | _ => rev acc
	    else
		let val x = hd xs
		in
		    case x of
			Num i    => fb(tl xs, i::acc, i, Num i)
		     | Fizz      =>
		       (case lastCount of
			    Num i => fb(tl xs, (i + 1)::acc, i, Fizz)
			  | Buzz => fb(tl xs, (lastNum + 2)::acc, lastNum, Fizz)
			  | NoCount =>
			    if y mod 3 = 0
			    then fb(tl xs, y::acc, y, Fizz)
			    else raise ImpossibleListEntered
			  | _     => raise ImpossibleListEntered
		       )
		     (*end of Fizz case*)
		     | Buzz      =>
		       (case lastCount of
			    Num i => fb(tl xs, (i + 1)::acc, i, Buzz)
			  | Fizz => fb(tl xs, (lastNum + 2)::acc, lastNum, Buzz)
			  | NoCount =>
			    if y mod 5 = 0
			    then fb(tl xs, y::acc, y, Buzz)
			    else raise ImpossibleListEntered
			  | _     => raise ImpossibleListEntered
		       )
		     (*end of Buzz case*)
		     | FizzBuzz  => 
		       (case lastCount of
			    Num i   => fb(tl xs, (i + 1)::acc, i, Buzz)
			  | NoCount =>
			    if y mod 15 = 0
			    then fb(tl xs, y::acc, y, FizzBuzz)
			    else raise ImpossibleListEntered
			  | _       => raise ImpossibleListEntered
		       )
		(*end of FizzBuzz case*)
		     | _ => raise ImpossibleListEntered
		end
    in fb(xs, [], y, NoCount)
    end

All pattern matching does is check what type we’re dealing with (for instance “What was passed to the function “fb” as the argument “lastCount”?”) and then act on that information. This is why we introduced “NoCount” as a datatype up top, so we can introduce a NONE value without breaking type rules.

Pattern matching would be a great way to deal with the real reverse fizzbuzz function, but I’ll get to that another day.

Welp, there’s SML. I know what pattern-matching is, and can implement it in a (very) silly fashion, and I know how to use datatypes. Also, as long as this post remains up, it’ll push me to do better — seriously, it’s like a dull ache in the back of my mind.

I’m going to find a way of showing something about Ruby next — and then I’ll be done with these for a little while, focussing on building a small frontend-only site for my girlfriend.

Hopefully I won’t come back to SML for some time yet

Tagged , , , , ,

Racket: FizzBuzz, Thunks And Streams

It’s all very well talking about what I’ve learned, but keeping up with it and providing proof is another issue entirely.

So, I’m going to provide variations on what I’ve done with FizzBuzz, a simple programming test to see if you understand the basics of a language – to see if you can be taught the rest from there, really.

The Rules Of FizzBuzz:
1. You start counting.
2. If a number is a multiple of 3, it must be replaced by the word "Fizz".
3. If a number is a multiple of 5, it must be replaced by the word "Buzz".
4. If a number is a multiple of both, it must be replaced by the word "FizzBuzz".

Like I said, simple.

I’m hoping to write up quick but unique implementations in Racket, Ruby, and SML that highlight a little of what I’ve learned from each language.

I’m starting with Racket, a programming language developed from Scheme, which in turn developed from Lisp. All together you’ve got a bit of a mafia movie going.

Racket was by far my favourite language studied on the course, which is probably why I’m starting with it. It’s often seen as a teaching language, but you can definitely do some pretty practical stuff with some easily-available libraries – I’ve been playing around with its JSON abilities during my lunch breaks at work, and it seems very natural (especially as I’ve not done any work with web APIs before).

The basic syntax is:

(define a-symbol a-thing)

This develops pretty quickly and naturally into recursive functions:

(define (multiply-recurse-silly x y)
  (letrec ([f (lambda (y acc)
            (if (<= y 0)
             acc
            (f (- y 1) (+ x acc))))])
    (f y 0)))

This is a silly and impractical use for recursion, using repeated addition to multiply two numbers together. To explain what’s going on here, if you don’t speak Racket:

The main function is defined as “multiply-recurse-silly” and given two arguments “x” and “y”.

We need a recursive function, so we “let” a new symbol, “f” be a new, local function. This function won’t need to change “x”, but it will need to count the number of times it’s added “x” to itself (the multiplication), and we’ll give it an accumulator called “acc” to help it keep track of what “x” is so far.

Once “y” has hit 0 (or below, through some error or inappropriate input), the function returns what the accumulator has saved up.

Otherwise, the function calls itself with the value of “y” – 1 (so “y” is like a countdown timer going towards zero) and “x” added to itself.

Finally, none of this will work unless we call our new “f” function in the first place, so we call it with the last line and then close all the brackets up.

Not too complicated. There’s some things I don’t like about this function looking back on it though:

1.
It’s bad that it isn’t an error for y to be less than 0. This is clearly a function to multiply only positive numbers together, so returning “0” when we try to multiply by a negative number is a big mistake.

What I should have done is either (a) added a new conditional branch so that if y is less than 0 the multiplication works (and works properly) for negative numbers or (b) added a new conditional branch so that if y is less than 0 we raise an error.

2.
For this function we don’t really need a local recursive function. I wanted practice using local functions because I haven’t been using them while designing my game in Java, but this was completely unnecessary. The entire function could have fit on one line, if I’d simply written it to call itself.

Moving quickly on from that:

(define (fizz-buzz start finish)
  (letrec ([f (lambda (strt acc)
                (cond [(> strt finish) (reverse acc)]
                      [(and (= (modulo strt 3) 0) (= (modulo strt 5) 0)
                       (f (+ strt 1) (cons "FizzBuzz" acc)))]
                      [(= (modulo strt 3) 0) (f (+ strt 1) (cons "Fizz" acc))]
                      [(= (modulo strt 5) 0) (f (+ strt 1) (cons "Buzz" acc))]
                      [(integer? strt) (f (+ strt 1) (cons strt acc))]
                      [#t (f 1 acc)]))])
    (f start null)))

My first FizzBuzz function in Racket. It seems to work OK.

Just a simple recursive function, only this time I am using cond, which in practice works like a switch statement, instead of if. “cons”, not to be confused (ha) with “cond”, simply means that we are adding something to a list. At the very end, we see a “#t” condition – this means that for any value other than “#f”, if the conditions before it haven’t executed, the condition at the end will execute. This is because Racket interprets any value other than false (#f) as true (#t).

This means that if an incorrect value is passed by the user as the start value we assume they just meant “start from the bottom!” and go from there.

Again, this is probably not something I would include in a program meant to be used seriously.

Because of the way we are joining the list together, we need to reverse it at the end to make sure it comes out the right way round for human eyes.

This function is able to count from a start number to a finish number, and gives back a Racket list depending on those numbers, in accordance with the FizzBuzz rules.

So:

(fizz-buzz 1 15)

Gives:

'(1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz")

All working as expected.

Of course, if we put the inputs in the wrong way round, it gives us:

'()

A null or empty list.

Going with the bodge-job aesthetic I’ve employed so far, I decided I wanted it to be able to handle both ways. Simple but unsatisfying to implement:

(define (fizz-buzz-up-or-down x y)
  (letrec ([f (lambda (countdown goal acc)
                (cond [((< countdown goal) acc]
                      [(and (= (modulo countdown 3) 0) (= (modulo countdown 5) 0)
                       (f (- countdown 1) goal (cons "FizzBuzz" acc)))]
                      [(= (modulo countdown 3) 0) (f (- countdown 1) goal (cons "Fizz" acc))]
                      [(= (modulo countdown 5) 0) (f (- countdown 1) goal (cons "Buzz" acc))]
                      [(integer? countdown) (f (- countdown 1) goal (cons countdown acc))]
                      [#t (raise "Inappropriate input!")]))])
    (if (> x y)
        (f x y null)
        (f y x null))))

Giving us:

(fizz-buzz 15 1)
'(1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz")

Just to confuse everything, this time I’ve changed the function so that the accumulator doesn’t need to be reversed, and the new way of doing things means that I have to raise an error if there’s an inappropriate input – I can’t just guess what the countdown would start at.

On the other hand, with the new up-or-down-ness this function will raise an error for an inappropriate input before I even get to that stage, rendering the error-raising and checking to see if the number’s an integer entirely moot, but it’s nice to be prepared (maybe).

The last thing I did in this file was create the ability to input custom fizzes and buzzes into the function:

(define (fizz-buzz-plus pairs x y)
  (letrec ([f (lambda (xs countdown goal acc)
                (cond [(< countdown goal) acc]
                      [(letrec ([g (lambda (ys ac)
                                     (cond [(null? ys)
                                            (if (null? ac)
                                                (f pairs (- countdown 1) goal (cons countdown acc))
                                                (f pairs (- countdown 1) goal (cons (reverse ac) acc)))]
                                           [(= (modulo countdown (cdar ys)) 0) (g (cdr ys) (cons (caar ys) ac))]
                                           [#t (g (cdr ys) ac)]))])
                         (g xs null))]))])
    (if (> x y)
        (f pairs x y null)
        (f pairs y x null))))

We’ve got an inner loop and an outer loop here.

(fizz-buzz-plus (list (cons "Snark" 7) (cons "Flunge" 4) (cons "Grontch" 9)) 1 25)

This time we pass in a list of pairs and get back:

'(1 2 3 ("Flunge") 5 6 ("Snark") ("Flunge") ("Grontch") 10
11 ("Flunge") 13 ("Snark") 15 ("Flunge") 17 ("Grontch") 19 ("Flunge")
("Snark") 22 23 ("Flunge") 25)

More-or-less what we expected.

(fizz-buzz-plus (list (cons "Snark" 3) (cons "Flunge" 6) (cons "Grontch" 9)) 1 25)
'(1  2  ("Snark")
  4  5  ("Snark" "Flunge")
  7  8  ("Snark" "Grontch")
  10  11  ("Snark" "Flunge")
  13  14  ("Snark")
  16  17  ("Snark" "Flunge" "Grontch")
  19  20  ("Snark")  22  23  ("Snark" "Flunge") 25)

I can’t remember why I chose to cons them together rather than string-append them. That’s this line:

[(= (modulo countdown (cdar ys)) 0) (g (cdr ys) (cons (caar ys) ac))]

It worked for my purposes. I suppose it makes it easier to parse the list as well, when we’re looking back over it.

Yeah, that was probably my reasoning. Let’s go with that.

Now to what I really learned: thunks and streams.

A “Thunk” is a function without arguments which we create only so that we can delay it ever being called, hopefully forever. Thunks are therefore prone to existentialist angst, but they are very useful in many ways.

I remember what they do by thinking of them as an onomatopoeia for a program slamming up against one (THUNK) or alternatively as a “then-function” – a function to do later. Then-func – Th-unc – Thunk. It’s a stretch but it helped me remember it.

Probably the most useful place for a Thunk (at least, at my current level of programming knowledge) is a Stream. A Stream is a Thunked pair, of which pair the latter part is a call to the Stream usually but not always with a different argument. This enables us to create infinite patterns, which we can read at a later date. Naturally this is very useful, and naturally I will only use it to mess around (for now at least).

Here is a simple stream I wrote while on the course. It shows alternately dan.jpg then dog.jpg, but only when called:

(define dan-then-dog (lambda () (cons "dan.jpg" (lambda () (cons "dog.jpg" dan-then-dog)))))

And here is how we read any given stream for a certain number of steps (starting from step 1):

(define (stream-for-n-steps s n)
   (if (<= n 0)
       null
       (cons (car (s)) (stream-for-n-steps (cdr (s)) (- n 1)))))

So, this is how we adapt our simple FizzBuzz function from earlier (I changed modulo to remainder, but it’s otherwise a very similar idea):

(define fizz-buzz-stream
  (letrec ([f (lambda(x)
                (cons
                 (if (and (= (remainder x 3) 0) (= (remainder x 5) 0))
                     "FizzBuzz"
                     (if (= (remainder x 3) 0)
                         "Fizz"
                         (if (= (remainder x 5) 0)
                             "Buzz"
                             x)))
                 (lambda () (f (+ x 1)))))])
                (lambda () (f 1))))

Finally, as an extension of the fizz-buzz-plus function defined earlier, here is a stream for prime numbers.

(define prime-numbers-stream
  (letrec ([f (lambda(x xs ys)
                (if (< x 3)
                    (cons x
                          (lambda () (f (+ x 1) ys ys)))
                    (if (null? xs)
                        (cons x
                              (lambda () (f (+ x 1) (cons x ys) (cons x ys))))
                        (if (= (remainder x (car xs)) 0)
                            (f (+ x 1) ys ys)
                            (f x (cdr xs) ys)))))])
    (lambda () (f 1 (list 2) (list 2)))))

This is really what FizzBuzz is all about. Once you’ve assigned enough multipliers to miscellaneous noises, you end up with only the prime numbers left. If you played FizzBuzz in Primary school, like me, you might have just realised it was a sneaky way of teaching you to think about the patterns involved in multiplication.

Someone once said that prime numbers are what’s left when all the patterns are taken away. I prefer to think of them as the seeds of their own patterns. Each prime fulfils its own unique function, creating a pattern that tessellates perfectly with every other prime, stretching out into infinity.

My method of reading this infinite stream is a lot less elegant and looks like this:

(define (read-prime-stream x)
  (begin (if (equal? x "quit")
             (display "Prime Number Generator Exited.")
             (display (stream-for-n-steps prime-numbers-stream x)))
         (display "\r")
         (if (equal? x "quit")
             (display "\r")
             (read-prime-stream (read)))))

(display "Type \"quit\" (including quotes) to exit. \r")
(read-prime-stream (read))

I learned a lot by writing all of this, though, and it’s really helped solidify some of the concepts of the course in my mind.

Did I get anything wrong? Was I inaccurate somewhere? Should I have expanded on something? Let me know in the comments.

Tagged , , ,

Is Water A Ghost?

I have created this helpful infographic for people who are not sure whether water is a ghost or not.

Is Water A Ghost? Infographic

Photos by Steven Depolo and Adam & Tess, some rights reserved.

So what did you do with your day?

Tagged , , ,