Erlang day two wasn't quite as swearing free as Erlang day one. You see I tend to swear when I’m stuck or baffled by syntax. And I git stuck a bit on the bonus problem. Turns out that putting []s round my variable made everything turn out nice. Who knew?
Day two looks at branching, anonymous functions, higher order functions and a fair bit of list syntax. A lot of the conceptual part made sense coming from a perl background where I use map almost every time I write code. Erlang introduces a rather natty uber-map (to coin a phrase) called a list comprehension. I found the structure rather beautiful and possibly over used it in my answers.
Overall, I felt much more stretched and occasionally frustrated by day two, but mostly by the tic tac toe question, which I am not sure I’ve answered as elegantly as I could have. But Erlang is actually quite a cute language and I am having fun coding in it.
listz.erl
- Consider a list of keyword-value tuples, such as [{erlang, "a functional language"},{ruby,"an OO language"}]. Write a function that accepts the list and a keyword and returns the associated value for the keyword.
- Consider a shopping list that looks like [{item, quantity, price},…]. Write a list comprehension that builds a list of items of the form [{item, total_price}] where total_price is quantity times price.
Like I said, I may well have overdone the list comprehensions. I used a comprehension for boith answers. Note that the function shopping is just a wrapper for the comprehension, even though that wasn't part of the task, it made life simpler for me. I’ve taken to putting the closing ull stop of my functions on a new line as I find it a little easier to read this way. I might look in to what others do. -module(listz).
-export([tuple_filter/2]).
-export([shopping/0]).
shopping() ->
[{Item, Qty*Price} || {Item, Qty, Price} <- [{beer,8,1.5},{cans_beans,3,0.44},{oranges,6,0.38}]]
.
tuple_filter(Keyword,List) ->
[Y || {X,Y} <- List, X==Keyword ]
.
tictactoe.erl
Bonus problem Write a program that reads a tic-tac-toe board presented as a list or tuple of size nine. Return the winner (X or O) if a winner has been determined, cat if there are no more possible moves, or no_winner if no player has won yet.
As I said, I’m not sure if the approach that I’ve taken is the most elegant. I want there to be some way that I can make a nested list comprehension or something that can determine that the board is full at the same time as detectine winning lines. But I’ve not figured that out quite yet.
There is two fiunctions in this module. board_state takes a list of 9 squares (relying on erlang to scream if the number of squares is too few). I use a list comprehension to determine if the board has a winner on it. It goes through all the rows columns and diagonals on the board, and for each of these returns the row if all the pieces on that "line" are either X or O.
Now if there is no winner (i.e. the returned array is [] ), we check to see if the bord is full, in the board_full function, which filters the list, removing all the Xs and Os if there is anything left then the board is not full. Once that returns we know whether to say "no_winner" or "cat". If there is a winner, we just grab and print the head of the list, which we know must be "X" or "O". There is a a subtle bug that I can see, which is that if there are two winning lines only the first detected will be reported as Winner. Commercial games houses will no doubt be shocked at my slapdash approach to game development!
-module(tictactoe).
-export([is_winner/1]).
-export([board_full/1]).
% Return X or O if X or O have won
% Otherwise if the board is full return cat
% Otherwise return no_winner
board_state( [ S1,S2,S3,
S4,S5,S6,
S7,S8,S9
] ) ->
Row1 = [S1,S2,S3],
Row2 = [S4,S5,S6],
Row3 = [S7,S8,S9],
Col1 = [S1,S4,S7],
Col2 = [S2,S5,S8],
Col3 = [S3,S6,S9],
Diag1 = [S1,S5,S9],
Diag2 = [S3,S5,S7],
Board = [Row1,Row2,Row3],
Winner = [ Piece ||
Piece <- ["O","X"],
Possibles <- [ Row1, Row2, Row3, Col1, Col2, Col3, Diag1, Diag2 ],
lists:all(fun(S) -> S==Piece end, Possibles)
],
case Winner of
[] ->
case board_full(lists:flatten(Board)) of
true -> "cat";
false -> "no_winner"
end;
[H|_] -> H
end
.
% Return true if all elems are either X or O
board_full(List) ->
length(
lists:filter(
fun(S) ->
([S]/="X" andalso [S]/="O")
end, List)
) < 1
.