Functional Programming

Lisp

(+ 1 1) => 2

(* (+ 1 (- 4 3)) (/ 6 (+ 2 1))) => 4

Clojure

Conway's Game of Life

Any live cell with fewer than two live neighbours dies, as if caused by under-population. Any live cell with two or three live neighbours lives on to the next generation. Any live cell with more than three live neighbours dies, as if by overcrowding. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.

Still lifes Block Boat Oscillators Blinker Pulsar Spaceships Glider Lightweight spaceship

Game of Life in Clojure

(defn create-world "Creates rectangular world with the specified width and height. Optionally takes coordinates of living cells." [w h & living-cells] (vec (for [y (range w)] (vec (for [x (range h)] (if (contains? (first living-cells) [y x]) "X" " "))))))

defn

(defn name "optional doc-string" [arguments] body)

w

h

&

living-cells

living-cells

first

vec

for

(for [bindings] body)

if

(if test then-form else-form)

test

then-form

else-form

(contains? (first living-cells) [y x])

living-cells

contains?

[y x]

"X"

" "

(create-world 4 4) => [[" " " " " " " "] [" " " " " " " "] [" " " " " " " "] [" " " " " " " "]] (create-world 4 4 #{[0 0], [1 1], [2 2], [3 3]}) => [ ["X" " " " " " "] [" " "X" " " " "] [" " " " "X" " "] [" " " " " " "X"]]

(defn neighbours "Determines all the neighbours of a given coordinate" [[x y]] (for [dx [-1 0 1] dy [-1 0 1] :when (not= 0 dx dy)] [(+ dx x) (+ dy y)]))

for

:when

dy

dx

dy

for

(neighbours [1 1]) => ([0 0] [0 1] [0 2] [1 0] [1 2] [2 0] [2 1] [2 2])

(defn stepper "Returns a step function for Life-like cell automata. neighbours takes a location and return a sequential collection of locations. survive? and birth? are predicates on the number of living neighbours." [neighbours birth? survive?] (fn [cells] (set (for [[loc n] (frequencies (mapcat neighbours cells)) :when (if (cells loc) (survive? n) (birth? n))] loc))))

fn

(fn [arguments] body)

set

loc

n

mapcat

neighbours

cells

frequencies

mapcat

:when

loc

if

loc

cells

n

survive?

loc

loc

cells

loc

n

birth?

loc

((stepper neighbours #{3} #{2 3}) #{[1 0] [1 1] [1 2]}) => #{[2 1] [1 1] [0 1]}

#{}

#{3}

#{2 3}

(#{2 3} 3) => 3

(#{2 3} 1) => nil

(def glider #{[2 0] [2 1] [2 2] [1 2] [0 1]}) (def light-spaceship #{[2 0] [4 0] [1 1] [1 2] [1 3] [4 3] [1 4] [2 4] [3 4]}) (def conway-stepper (stepper neighbours #{3} #{2 3}))

Line 1: def has the form (def name body) . name is then assigned the value of evaluating body . In this case a set of vectors containing two integers each, which are the coordinates of the living cells.



We now have two patterns, a glider and a lightweight spaceship, and one function that manipulates cells according to the rules of Conway. The last function, which we will write now, just helps us to determine the evolution according to Conway's Game of Life by giving it the size of the world, an initial pattern and the number of the generation we want to see.

(defn conway "Generates world of given size with initial pattern in specified generation" [[w h] pattern iterations] (->> (iterate conway-stepper pattern) (drop iterations) first (create-world w h) (map println)))

->>

iterate

(iterate function value)

value

(function value)

(function (function value))

drop

(drop n collection)

n

collection

first

(first collection)

collection

create-world

map

println

println

(conway [5 15] light-spaceship 0) Output:

[ ] [ X X X X ] [X X ] [ X ] [X X ] (conway [5 15] light-spaceship 1) Output:

[ X X ] [ X X X X ] [ X X X X ] [ X X ] [ ] (conway [5 15] light-spaceship 2) Output:

[ X X ] [ X ] [ X X ] [ X X X X ] [ ] (conway [5 15] light-spaceship 3) Output:

[ ] [ X X ] [ X X X X ] [ X X X X ] [ X X ] (conway [5 15] light-spaceship 4) Output:

[ ] [ X X X X ] [ X X ] [ X ] [ X X ]

Output:Output:Output:Output:Output:

(conway [4 4] glider 0)

[ X ] [ X ] [X X X ] [ ]

(conway [4 4] glider 1)

[ ] [X X ] [ X X ] [ X ]

(conway [4 4] glider 2)

[ ] [ X ] [X X ] [ X X ]

(conway [4 4] glider 3)

[ ] [ X ] [ X X] [ X X ]

(conway [4 4] glider 4)

[ ] [ X ] [ X] [ X X X]

(ns conways-game-of-life.core) (defn create-world "Creates rectangular world with the specified width and height. Optionally takes coordinates of living cells." [w h & living-cells] (vec (for [y (range w)] (vec (for [x (range w)] (if (contains? (first living-cells) [y x]) "X" " ")))))) (defn neighbours "Determines all the neighbours of a given coordinate" [[x y]] (for [dx [-1 0 1] dy [-1 0 1] :when (not= 0 dx dy)] [(+ dx x) (+ dy y)])) (defn stepper "Returns a step function for Life-like cell automata. neighbours takes a location and return a sequential collection of locations. survive? and birth? are predicates on the number of living neighbours." [neighbours birth? survive?] (fn [cells] (set (for [[loc n] (frequencies (mapcat neighbours cells)) :when (if (cells loc) (survive? n) (birth? n))] loc)))) ; patterns (def glider #{[2 0] [2 1] [2 2] [1 2] [0 1]}) (def light-spaceship #{[2 0] [4 0] [1 1] [1 2] [1 3] [4 3] [1 4] [2 4] [3 4]}) ; steppers (def conway-stepper (stepper neighbours #{3} #{2 3})) (defn conway "Generates world of given size with initial pattern in specified generation" [[w h] pattern iterations] (->> (iterate conway-stepper pattern) (drop iterations) first (create-world w h) (map println)))

Game of Life in Functional Python

from collections import Counter from itertools import chain def mapcat(func, iter): "Maps func over iter and returns concanated list" return chain.from_iterable(func(*args) for args in iter) def create_world(w, h, living_cells=()): """Creates a rectangular world with the specified width and height. Optionally takes coordinates of living cells. """ return [["X" if (y, x) in living_cells else " " for x in range(h)] for y in range(w)] def neighbours(y, x): "Determines all the neighbours of a given coordinate" return [(dy + y, dx + x) for dy in [-1, 0, 1] for dx in [-1, 0, 1] if not (dy == 0 and dx == 0)] def stepper(neighbours, birth, survive): """Returns a step function for Life-like cell automata. neighbours takes a y/x-coordinates and returns a sequential collection of locations. survive and birth are constraints on the number of living neighbours.""" return lambda cells: set( [loc for (loc, n) in Counter(mapcat(neighbours, cells)).items() if (loc in cells and n == birth) or n == survive]) # pattern definitions glider = set(((2, 0), (2, 1), (2, 2), (1, 2), (0, 1))) light_spaceship = set(((2, 0), (4, 0), (1, 1), (1, 2), (1, 3), (4, 3), (1, 4), (2, 4), (3, 4))) # steppers conway_stepper = stepper(neighbours, 2, 3) def conway(w, h, pattern, iterations): "Generates world of given size with initial pattern in specified generation" cells = pattern for unused in range(iterations): cells = conway_stepper(cells) for row in create_world(w, h, living_cells=cells): print(row)

Conclusion

My first blog post about declarative programming explained how to write a Sudoku solver in the logic programming language Prolog. This time I'll show you how to implement Conway's Game of Life in the functional programming language Clojure But before that, let me explain a few general things. The first three paragraphs are for readers who are not familiar with certain concepts. People who already know what Clojure or Conway's Game of Life is, may feel free to skip those paragraphs. It starts getting serious at "Game of Life in Clojure".Functional Programming is a style of programming (a so called programming paradigm ), that tries to avoid state and mutable data and emphasizes writing and using functions that have small or no side effects. This results in programs which are easier to test, maintain, understand and predict. One result of this rules is that functions are first-class, which means that they are treated like any other data. They can be created, passed around and modified just like a string.Some languages, such as Lisp, are made for using this paradigma. Others may not be made for it, but they still can profit from this style.In order to understand the code below, I have to tell you a few things about how to write code in Lisp.The code consists of s-expressions , which can be looked at as a list. This is the root of Lisp's syntax or , better say, lack of syntax. This is what allows Lisp to treat everything as data and manipulate it - even code itself. And that's the heart of the powerful macro system, which is as far as I know unique to Lisp. We will not care too much about the macro system now, but it's basically another level of abstraction which most other programming languages lack.A s-expression can be as simple as:which just adds 1 to 1. As you can see, s-expressions are written in prefix notation. Most other (imperative) languages use infix notation, but Lisp does everything for a reason. It's also strictly regular about its notation, unlike many other languages. A more complex s-expression could look like this:This notation might seem awkward at first, but as I said it has its cause. Since all those expressions are just simple lists, they can also be treated like that and hence manipulated by macros just like a normal list. This gives Lisp great metaprogramming and code generation capabilities.Clojure is a young Lisp dialect which can run in the Java Virtual Machine, the Common Language Runtime and JavaScript Engines. Since it's a Lisp, it's a functional programming language, but not purely functional such as Haskell , which means that certain functions can have side effects. It also has the beloved macro system known from other Lisp's, but we won't see that in action in this article.One more thing rather special to Clojure as a Lisp dialect is the massive use of lazy sequences . Lazy sequences are only evaluated as far as they are needed, which avoids needless calculations and allows constructing potentially infinite data structures. We'll see them throughout the code.This zero-player "game" basically consist of a cellular automaton following four rules which were devised by John Horton Conway . These rules together with the initial input determine the whole evolution of the cells in this game.The world in which the evolution of those cells takes place is an infinite two-dimensional grid. Each cell is either dead or alive and has exactly eight neighbours.The four rules are:The game starts by giving the world an initial state with certain cells being alive. Then those four rules are applied and by doing so, one of three things happen to each cell: Nothing, death or birth. After applying those rules the first time, we have the second generation of our world. The same procedure starts over and over again, until either we decide to stop it or all cells are dead.There are some special patterns which are known to have certain behaviour. There are still lifes, oscillators, spaceships and so on. Some fancy graphics to visualize them:You can find some more patterns at Wikipedia I'm currently learning Clojure with the book Clojure Programming and the authors decided to show how you can work with Clojure's collection by implementing Conway's Game of Life.Let's go through a slightly modified version of the code in the book.The first thing we need is a representation of the world, which is a two-dimensional grid. The most obvious and simple one is just a vector of vectors (or array of arrays in other languages). Inside these vectors dead cells are indicated by a whitespace (" ") and living cells by a "X" (just because it's nicer to print them later on). Let's start by writing a function that creates an the world, which optionally accepts a set of y/x coordinates to settle living cells.Use ufto define a function. The form is: Argument list consisting of two fixed argumentsand. Theallows us to create the optional argument, which is then wrapped into a list, because there could be more variables than just. This is why we have to useat line 7.creates a new vector from a given collection (in this case from a lazy sequence).is a list comprehension with the form. We use both of them twice in order to create our nested vector of vectors.has the form. If thestatement evaluates to true, theis executed and otherwhise thechecks whether thecollectionthecoordinates of our current position. If it does, we set that cell toand otherwhise toWe now can generate empty worlds and worlds with certain cells being alive:The next two functions are the core of this implementation. In order to determine the next generation, we need a function that manipulates the cells according to the rules. Since the rules always depend on the neighbours of a given cell, we need a function to determine all neighbours of a cell. The neighbours function looks like this:: Usage ofto create a lazy sequence using two variables, which iterate over -1, 0 and 1.both of those variables are not equal to 0, the body form is executed. The rightmost binding,, is iterated as the fastest one.: Calculating new coordinates based on the offsetsandgiven by thebindings.What are all the neighbours of the location (1/1)?Now, instead of writing a function which behaves exactly according to the rules of Conway's Game of Life, we could write a high-order function that takes three arguments: A function to determine the neighbours, a predicate which determines whether a cell shall be given life (e.g. #{3} for Conway's rules) and one predicate that determines whether a cell survives to the next generation (#{2 3}). It then returns a function which calculates the next generation according to the given rules.: Use ofto create and then return a new, unnamed function. The form is: Creating afrom the return value of the list comprehension which iterates over the locationsand the number of living neighboursreturns the concatanated list of the result of callingon all coordinates inthen returns a hash table (map) of each distinct item returned by theand associates it with the number of occurence.makes sure only those coordinatesare returned that pass thetest. Ifis inand the number of living neighboursis high enough according to the predicate, theis returned. Ifis not in, only thoseare returned that have enough living neighboursaccording to the preidcateto give that cell life.: Just return thosethat apply to the rules described before.Let's try to generate the second state of a blinker pattern. To do so, we give our stepper the coordinates of the livings cells for the vertical position of the blinker ( --- ):As we can see, we end up with the coordinates of the horizontal position of the blinker ( | ).is a set, which can be used as a predicate (and) to check whether the arguments given to it are in that set. For example:andThose two functions are quite straighforward in Clojure and actually everything you need to solve Conway's Game of Life. create-world is just for the representation of the world.For the sake of a nicer interface, we will create some definitions.inserts the first form as the last item into the second form, then this new form as the last item into the third form and so on.has the formand returns the lazy sequence, etc.has the formand returns a lazy sequence of all but the firstitems ofhas the formand returns only the first item of the: Usage of our self-defined functionto create the world with the living cells given by the 3 lines above.callsover all the rows in our world.just prints out the row and then a newline.So, let's see whether all those brackets can actually generate the evolution of some cells.And the glider:Output:Output:Output:Output:Output:You can download the full source code here or copy it from here:Since I'm coming from Python , I was curious whether this solution could be implemented in Python. Python does support functional programming to some extent. This might also help some people understanding the Clojure code above a bit better.Note that this is NOT idiomatic Python code and probably no real-world Python developer would write code like this. I was just curious how far you can push functional programming in Python, which was actually not designed to be used this way.The code is almost a transliteration of the Clojure code, so I don't have to explain all the functions again:Thanks to nedbat from #python for helping me with this.None of the functions have side effects (beside conway(), which does printing), as emphasized by the functional programming style. Also, no function depends on some global state - hence they are all deterministic. This means that those functions always return the same output when called with a specific input. They are a mapping of input to output.You can download the full Python source code here I hope I could make you interested in functional programming and Lisp, while giving you a vague idea what these things are about. The most widely used Lisp at the moment is probably Common Lisp and there is a good and free book out there called Practical Common Lisp Depending on what you want to do with Lisp, it might be a good idea to take a closer look at Clojure too. It has the potential to be more widely used in the "real world", since it's running in the JVM and is often used for things like web developing. Two good books on Clojure would be Clojure Programming and The Joy Of Clojure Discussion on Hacker News (thanks to samrat for submitting)Discussion on Reddit: r/coding