Skip to content

Instantly share code, notes, and snippets.

@tomtung
Created June 13, 2012 08:45
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save tomtung/2922865 to your computer and use it in GitHub Desktop.
Save tomtung/2922865 to your computer and use it in GitHub Desktop.
Seven Languages in Seven Weeks
#!/usr/bin/ruby
############# Day 1 #############
# Print the string “Hello, world.”
puts 'Hello, world'
# For the string “Hello, Ruby”, find the index of the word“Ruby”.
puts 'Hello, Ruby'.index('Ruby')
# Print yourname ten times.
10.times {puts 'Tom'}
# Print the string “This is sentence number 1”,
# where the number 1 changes from 1 to 10.
(1..10).each {|i| puts "This is sentence number #{i}"}
# Bonus problem: If you're feeling the need for a little more, write
# a program that picks a random number. Let a player guess the
# number, telling the player whether the guess is too low or too high.
num = rand(10)
while true
print 'Guess: '
guess = gets().to_i
if guess < num
puts 'Too small!'
elsif guess > num
puts 'Too large!'
else
puts 'Correct!'
break
end
end
############# Day 2 #############
# Print the contents of an array of sixteen numbers, four numbers at a time
array = (1..16).to_a
array.each_slice(4){|x| p x}
# The Tree class was interesting, but it did not allow you to specify
# a new tree with a clean user interface. Let the initializer accept a
# nested structure with hashes and arrays. You should be able to
# specify a tree like this: {'grandpa' => {' dad' => {'child 1' => {}, 'child
# 2' => {} }, 'uncle' => {'child 3' => {}, 'child 4' => {} } } }.
class Tree
attr_accessor :children, :node_name
def initialize(args)
if not args.empty?
a = args.first
@node_name = a[0]
@children =
if a[1].empty?
[]
else
a[1].collect{|k,v| Tree.new({k=>v})}
end
end
end
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
end
tree = Tree.new ({'grandpa' =>
{
'dad' =>
{
'child 1' => {},
'child 2' => {}
},
'uncle' =>
{
'child 3' => {},
'child 4' => {}
}
}
})
tree.visit_all{|node| puts node.node_name}
# Write a simple grep that will print the lines of a file having any
# occurrences of a phrase anywhere in that line. You will need to do
# a simple regular expression match and read lines from a file. (This
# is surprisingly simple in Ruby.) If you want, include line numbers.
def grep(filename, pattern)
f = File.new(filename)
f.each {|line| puts "#{f.lineno}:\t#{line}" if pattern === line}
end
grep("ruby.rb", /def/)
############# Day 3 #############
# Modify the CSV application to support an each method to return a
# CsvRow object. Use method_missing on that CsvRow to return the value
# for the column for a given heading.
# For example, for the file:
# one, two
# lions, tigers
# allow an API that works like this:
# csv = RubyCsv.new
# csv.each {|row| puts row.one}
# This should print "lions".
module ActsAsCsv
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def acts_as_csv
include InstanceMethods
end
end
module InstanceMethods
class CsvRow
def method_missing(name, *args, &block)
value = self[name]
if value
value
else
super.method_missing(name, *args, &block)
end
end
def [](col_name)
index = @headers.find_index(col_name.to_s)
if index
contents[index]
else
nil
end
end
attr_accessor :headers,:contents
def initialize(headers, contents)
@headers = headers
@contents = contents
end
end
def read
@csv_contents = []
filename = self.class.to_s.downcase + '.txt'
file = File.new(filename)
@headers = file.gets.chomp.split(',')
file.each do |row|
@csv_contents << row.chomp.split(',')
end
end
attr_accessor :headers, :csv_contents
def initialize
read
end
def each(&block)
@csv_contents.
collect{|contents| CsvRow.new(@headers, contents)}.
each(&block)
end
end
end
class RubyCsv
include ActsAsCsv
acts_as_csv
end
csv = RubyCsv.new
csv.each {|row| puts row.one}
#!/usr/local/bin/io
/************ Day 1 ************/
// Execute the code in a slot given its name.
Object getSlot("list") call(1,2,3,4)
/************ Day 2 ************/
// Write a program to find the nth Fibonacci number.
// Recursion
fib := method(n, if(n < 3, 1, fib(n - 1) + fib(n - 2)))
// Loop
fib := method(n,
a := b := sum := 1
for(i, 3, n,
sum = a + b; a = b; b = sum)
sum
)
// Change / to return 0 if the denominator is zero.
Number dividedBy := Number getSlot("/")
Number / = method(divisor, if(divisor == 0, 0, self dividedBy(divisor)))
// Add up all of the numbers in a two-dimensional array.
array2d := list(list(1,2,3), list(4,5,6), list(7,8,9))
array2d sum2d := method(self map(a, a sum) sum)
// Add a slot called myAverage to a list that computes the average of all the numbers in a list.
List myAverage := method(sum / size)
// Write a prototype for a two-dimensional list:
// - dim(x, y)
// - set(x,y,value), get(x, y)
// - transpose
Matrix := Object clone do(
indexOf := method(x, y,
if(rowMajor, x * dim2 + y, y * dim1 + x))
set := method(x, y, value, content atPut(indexOf(x, y), value))
get := method(x, y, content at(indexOf(x, y)))
transposed := method(
m := Matrix clone
m dim1 = dim2; m dim2 = dim1
m rowMajor = rowMajor not
m content = list() copy(content)
m
)
save := method(path,
File with(path) open write(self serialized) close
self
)
load := method(path, doFile(path))
rowMajor := true
dim1 := 1
dim2 := 1
content := list() setSize(1)
)
dim := method(x, y,
m := Matrix clone
m rowMajor = true
m dim1 = x
m dim2 = y
m content = list() setSize(x*y)
m
)
// Write a program that gives you ten tries to guess a random number from 1-100.
number := Random value(1, 100) round
for(i, 1, 10,
guess := File standardInput readLine("Enter your guess: ") asNumber
if(guess == number, break)
distance := (guess - number) abs
if(?prevDistance and distance != prevDistance) then(
if(distance < prevDistance,
writeln("Hotter"),
writeln("Colder")
)
)
prevDistance := distance
)
writeln("The number is: ", number)
/************ Day 3 ************/
// Enhance the XML program
// - to add spaces to show the indentation structure.
// - if the first argument is a map (use the curly brackets syntax),
// add attributes to the XML program.
OperatorTable addAssignOperator(":", "toAttrStr")
Builder := Object clone do (
toAttrStr := method(key, value,
" " .. doString(key) .. "=\"" .. value .. "\""
)
curlyBrackets := method(
call evalArgs join
)
indentNum := 0
indent := method("\t" repeated(indentNum))
forward := method(
name := call message name
attr := if(
call hasArgs and call argAt(0) name == "curlyBrackets",
doMessage(call argAt(0)),
""
)
writeln(indent, "<", name, attr, ">")
indentNum = indentNum + 1
call message arguments slice(if(attr isEmpty, 0, 1)) foreach (arg,
content := self doMessage(arg)
if(content, writeln(indent, content))
)
indentNum = indentNum - 1
writeln(indent, "</", name, ">")
)
)
// Create a list syntax that uses brackets.
curlyBrackets := method(call evalArgs)
#!/usr/bin/pl -t main -s
%
% Note: SWI-Prolog (rather than GNU-Prolog) is used here.
% You may need to change the path following the shebang.
%
% For ins/2, all_distinct/2, and transpose/2
:- use_module(library(clpfd)) .
%%%%%%%%%%%%%%%%%%%% Day 1 %%%%%%%%%%%%%%%%%%%%
%
% Represent some of your favorite books and authors.
%
book_author(joel_on_software, joel_spolsky) .
book_author(javascript_the_good_parts, douglas_crockford) .
book_author(hackers_and_painters, paul_graham) .
book_author(effective_cpp, scott_meyers) .
book_author(more_effective_cpp, scott_meyers) .
book_author(godel_escher_bach, douglas_hofstadter) .
%
% Find all books in your knowledge base written by one author.
%
find_books:-
findall(Book, book_author(Book, scott_meyers), BookList),
writeln(BookList) .
%%%%%%%%%%%%%%%%%%%% Day 2 %%%%%%%%%%%%%%%%%%%%
%
% Reverse the elements of a list.
%
list_reverse(List, Reversed) :-
list_reverse(List, [], Reversed) .
list_reverse([], Reversed, Reversed) .
list_reverse([Head|Tail], Accumulator, Reversed) :-
list_reverse(Tail, [Head|Accumulator], Reversed) .
test_list_reverse :-
list_reverse([1,2,3,4,5], Reversed),
writeln(Reversed).
%
% Find the smallest element of a list.
%
list_min(Min, [Head|Tail]) :- list_min(Min, Head, Tail) .
list_min(Min, Min, []) .
list_min(Min, PrevMin, [Head|Tail]) :-
CurrMin is min(PrevMin, Head),
list_min(Min, CurrMin, Tail) .
test_list_min :-
list_min(Min, [2,4,3,1,5]),
writeln(Min) .
%
% Sort the elements of a list.
%
partition([], _, [], []) .
partition([Head|Tail], Pivot, [Head|TailLeft], Right) :-
Head =< Pivot,
partition(Tail, Pivot, TailLeft, Right) .
partition([Head|Tail], Pivot, Left, [Head|TailRight] ) :-
Head > Pivot,
partition(Tail, Pivot, Left, TailRight) .
quick_sort(List, Sorted) :- quick_sort(List, [], Sorted) .
quick_sort([], Sorted, Sorted) .
quick_sort([Pivot|Tail], PrevRightSorted, Sorted) :-
partition(Tail, Pivot, Left, Right),
quick_sort(Right, PrevRightSorted, RightSorted),
quick_sort(Left, [Pivot|RightSorted], Sorted) .
test_quick_sort :-
quick_sort([1,5,4,2,3,2,4,1,5], Sorted),
writeln(Sorted) .
%%%%%%%%%%%%%%%%%%%% Day 3 %%%%%%%%%%%%%%%%%%%%
%
% Modify the Sudoku solver to work on 9x9 puzzles.
% Make the Sudoku solver print prettier solutions.
%
split([], _, [], []) .
split(List, 0, [], List) .
split([Head|Tail], Count, [Head|TailLeft], Right) :-
Count1 is Count - 1,
split(Tail, Count1, TailLeft, Right) .
group([], _, []) .
group(List, Count, [Left|RightGrouped]) :-
split(List, Count, Left, Right),
group(Right, Count, RightGrouped) .
sudoku_n(Sudoku, N) :-
length(Sudoku, Length),
N is round(sqrt(Length)) .
sudoku_rows_columns(Sudoku, Rows, Columns) :-
sudoku_n(Sudoku, N),
group(Sudoku, N, Rows),
transpose(Rows, Columns) .
sudoku_blocks(Sudoku, Blocks) :-
sudoku_n(Sudoku, N),
M is round(sqrt(N)),
group(Sudoku, M, G1),
group(G1, M, G2),
group(G2, M, G3),
maplist(transpose, G3, T),
flatten(T, F),
group(F, N, Blocks) .
sudoku(Puzzle, Solution) :-
Solution = Puzzle,
sudoku_n(Puzzle, N),
Solution ins 1..N,
sudoku_rows_columns(Solution, Rows, Columns),
sudoku_blocks(Solution, Blocks),
maplist(all_distinct, Rows),
maplist(all_distinct, Columns),
maplist(all_distinct, Blocks) .
sudoku_print(Sudoku) :-
sudoku_rows_columns(Sudoku, Rows, _),
maplist(writeln, Rows) .
test_sudoku :-
Sudoku4x4 =
[_, _, 2, 3,
_, _, _, _,
_, _, _, _,
3, 4, _, _
],
sudoku(Sudoku4x4, Solution4x4),
sudoku_print(Solution4x4), nl,
Sudoku9x9 =
[5, 3, _, _, 7, _, _, _, _,
6, _, _, 1, 9, 5, _, _, _,
_, 9, 8, _, _, _, _, 6, _,
8, _, _, _, 6, _, _, _, 3,
4, _, _, 8, _, 3, _, _, 1,
7, _, _, _, 2, _, _, _, 6,
_, 6, _, _, _, _, 2, 8, _,
_, _, _, 4, 1, 9, _, _, 5,
_, _, _, _, 8, _, _, 7, 9
],
sudoku(Sudoku9x9, Solution9x9),
sudoku_print(Solution9x9) .
%
% Solve the Eight Queens problem by taking a list of queens.
%
n_queens(N, Columns) :-
findall(Row, between(1, N, Row), Rows),
permutation(Rows, Columns),
maplist(plus, Rows, Columns, Diag1),
maplist(plus, Rows, Diag2, Columns),
maplist(all_distinct, [Diag1, Diag2]) .
test_n_queen :-
n_queens(8, Columns),
writeln(Columns) .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
main:-
find_books, nl,
test_list_reverse, nl,
test_list_min, nl,
test_quick_sort, nl,
test_sudoku, nl,
test_n_queen, nl,
halt .
#!/bin/sh
exec scala "$0" "$@"
!#
/************ Day 1 ************/
/*
Implement the game Tic-Tac-Toe.
*/
class TicTacToe private(private val board: List[Char], val currentPlayer: Char) {
def this() = this(List.fill(9)(TicTacToe.Vacant), TicTacToe.Player1)
import TicTacToe._
lazy val winner = {
val lines = {
val rows = board.grouped(3).toList
val columns = rows.transpose
val diagonals = List(0, 4, 8, 2, 4, 6).map(board).grouped(3)
(diagonals ++ rows ++ columns).map(_.mkString).toSet
}
if (lines.contains(Player1.toString * 3)) Some(Player1)
else if (lines.contains(Player2.toString * 3)) Some(Player2)
else None
}
lazy val boardFull = board.count(_ == Vacant) == 0
def nextMove(position: Int) = {
assert(winner.isEmpty)
assert(board(position) == Vacant)
val nextBoard = board.take(position) ++ (currentPlayer :: board.drop(position + 1))
val nextPlayer = currentPlayer match {
case Player1 => Player2
case Player2 => Player1
}
new TicTacToe(nextBoard, nextPlayer)
}
override def toString = toString(indexed = false)
def toString(indexed: Boolean) = {
val b =
if (indexed)
board.zipWithIndex.map({
case ('_', i) => i.toString.charAt(0)
case (player, i) => player
})
else
board
b.grouped(3).map(_.mkString(" ")).mkString("\n")
}
}
object TicTacToe {
val Player1 = 'X'
val Player2 = 'O'
val Vacant = '_'
def play(game: TicTacToe = new TicTacToe()) {
println(game)
println()
if (game.winner.isDefined) {
println(game.winner.get + " wins!")
} else if (game.boardFull) {
println("It's a tie!")
} else {
println("" + game.currentPlayer + "'s turn! Pick a position: ")
println(game.toString(indexed = true))
val position = readInt()
println()
play(game.nextMove(position))
}
}
}
TicTacToe.play()
println()
/************ Day 2 ************/
/*
Use foldLeft to compute the total size of a list of strings.
*/
println(
List("123", "4567", "8", "90").foldLeft(0)(_ + _.length)
)
println()
/*
Write a Censor trait with a method that will replace the curse words
Shoot and Darn with Pucky and Beans alternatives. Use a map to
store the curse words and their alternatives.
*/
trait Censor {
val censorWords =
if (new java.io.File("censor.txt").exists()) {
io.Source.fromFile("censor.txt").getLines().map(line => {
val a = line.split(",")
assert(a.length == 2)
a(0) -> a(1)
}).toMap
}
else
Map("Shoot" -> "Pucky", "Darn" -> "Beans")
def censorText(text: String) =
(text /: censorWords)((text, censor) => text.replaceAll("(?i)" + censor._1, censor._2))
}
object TestCensor extends Censor{
val text = "Shoot Darn"
lazy val censoredText = censorText(text)
def print(){
println("Original: " + text)
println("Censored: " + censoredText)
}
}
TestCensor.print()
println()
/************ Day 3 ************/
/*
- Take the sizer application and add a message to count the number of links on the page.
- Make the sizer follow the links on a given page, and load them as well.
*/
import java.nio.charset.CodingErrorAction
import scala.io._
import scala.actors._
import Actor._
object PageLoader {
def load(url: String) = {
Source.fromURL(url)(Codec.default.onMalformedInput(CodingErrorAction.IGNORE)).mkString
}
// To simplify the problem, only links in the "complete" form are counted
val linkRegex = """(?i)<a\s[^>]*href="(http://[a-z./]+)"[^>]*>""".r
def extractLinks(source: String) = linkRegex.findAllIn(source).matchData.map(_.group(1)).toList
}
val urls = List(
"http://www.google.com",
"http://www.cnn.com"
)
def timeMethod(method: () => Unit) {
val start = System.nanoTime
method()
val end = System.nanoTime
println("Method took " + (end - start) / 1000000000.0 + " seconds.")
}
def getPageSizeSequentially() {
for (url <- urls) {
val source = PageLoader.load(url)
val links = PageLoader.extractLinks(source)
val totalSize = source.length + links.map(PageLoader.load(_).length).sum
println("Size for " + url + ": " + source.size +
", #Links: " + links.size +
", Total size: " + totalSize)
}
}
def getPageSizeConcurrently() {
val rootActor = self
for (url <- urls) {
actor {
val source = PageLoader.load(url)
val links = PageLoader.extractLinks(source)
val urlActor = self
links.foreach({
link =>
actor {
urlActor ! PageLoader.load(link).length
}
})
val totalSize = source.length + {
for (i <- 1 to links.length)
yield receive {
case size: Int =>
size
}
}.sum
rootActor !(url, source.length, links.size, totalSize)
}
}
for (i <- 1 to urls.size) {
receive {
case (url, size, linkCount, totalSize) =>
println("Size for " + url + ": " + size +
", #Links: " + linkCount +
", Total size: " + totalSize)
}
}
}
println("Sequential run:")
timeMethod(getPageSizeSequentially)
println("Concurrent run")
timeMethod(getPageSizeConcurrently)
#!/usr/local/bin/escript
%
% The next few lines are necessary for Day 3's OTP problem
%
-mode(compile) .
-behaviour(gen_server) .
-define(Log, loggerlog) .
-export([init/1, terminate/2, handle_cast/2, handle_call/3, handle_info/2, code_change/3]) .
%%%%%%%%%%%%%%%%%%%% Day 1 %%%%%%%%%%%%%%%%%%%%
%
% Write a function that uses recursion to return the number of words in a string.
%
is_letter(Char) ->
(Char >=$a) and (Char =< $z) or
(Char >= $A) and (Char =< $Z) or
(Char >= $0) and (Char =< $9) or
(Char == $-) .
count_words(String) -> count_words(String, false, 0) .
count_words([], _, Count) -> Count ;
count_words([Head|Tail], IsInWord, Count) ->
IsLetter = is_letter(Head),
case IsLetter and not IsInWord of
true ->
count_words(Tail, IsLetter, Count + 1);
false ->
count_words(Tail, IsLetter, Count)
end .
test_count_words() ->
Sentence = "AAM-4 is an example of fire-and-forget missles.",
io:put_chars(Sentence), io:nl(),
io:fwrite("#words = ~B~n", [count_words(Sentence)]) .
%
% Write a function that uses recursion to count to ten.
%
count_to(1) -> io:fwrite("~B ", [1]) ;
count_to(N) when is_integer(N) ->
count_to(N - 1),
io:fwrite("~B ", [N]).
test_count_to() ->
count_to(10), io:nl() .
%
% Write a function that uses matching to selectively print “success”
% or “error: message” given input of the form {error, Message} or success.
%
report(success) -> io:fwrite("success~n");
report({error, Message}) -> io:fwrite("error: ~s~n", [Message]).
test_report() ->
report(success),
report({error, "The hard disk has exploded."}) .
%%%%%%%%%%%%%%%%%%%% Day 2 %%%%%%%%%%%%%%%%%%%%
%
% 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.
%
get_value([{Key, Value}|_], Key) -> Value;
get_value([_|Tail], Key) -> get_value(Tail, Key);
get_value([], _) -> error(badarg) .
test_get_value() ->
Map = [{erlang, "a functional language"}, {ruby, "an OO language"}],
io:fwrite("~p: ~p~n", [erlang, get_value(Map, erlang)]),
io:fwrite("~p: ~p~n", [ruby, get_value(Map, ruby)]) .
%
% 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.
%
compute_total_price(ShoppingList) ->
[{Item, Quantity * Price} || {Item, Quantity, Price} <- ShoppingList] .
test_compute_total_price() ->
ShoppingList = [{pencil, 10, 0.5}, {eraser, 2, 1}, {pen, 1, 5}],
io:fwrite("~p~n", [compute_total_price(ShoppingList)]) .
%
% Write a program that reads a tic-tac-toe board presented as a list
% or a 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.
%
tictactoe_lines(
[ A,B,C,
D,E,F,
G,H,I ]
) ->
[ {A,B,C},{D,E,F},{G,H,I},
{A,D,G},{B,E,H},{C,F,I},
{A,E,I},{C,E,G} ] .
tictactoe_is_full(Board) ->
lists:all(fun(L) -> (L == x) or (L == o) end, Board) .
judge_tictactoe(Board) ->
Lines = tictactoe_lines(Board),
XWins = lists:member({x,x,x}, Lines),
OWins = lists:member({o,o,o}, Lines),
IsFull = tictactoe_is_full(Board),
if
XWins -> x;
OWins -> o;
IsFull -> cat;
true -> no_winner
end .
test_judge_tictactoe() ->
Board1 =
[null,null,null,
null,x,null,
null,o,null],
Board2 =
[x,o,o,
x,x,null,
x,null,o],
Board3 =
[o,x,x,
o,o,null,
o,null,x],
Board4 =
[o,x,x,
x,o,o,
x,o,x],
io:fwrite("~p ~p~n~p ~p~n~p ~p~n~p ~p~n",
[Board1, judge_tictactoe(Board1),
Board2, judge_tictactoe(Board2),
Board3, judge_tictactoe(Board3),
Board4, judge_tictactoe(Board4)]) .
%%%%%%%%%%%%%%%%%%%% Day 3 %%%%%%%%%%%%%%%%%%%%
%
% Monitor the translator_loop and restart it should it die.
%
translator_loop() ->
receive
"casa" ->
io:format("house~n"),
translator_loop();
"blanca" ->
io:format("white~n"),
translator_loop();
_ ->
io:format("I don't understand.~n"),
exit({translator, dont_understand})
end.
translator_doctor_loop() ->
process_flag(trap_exit, true),
receive
new_translator ->
io:fwrite("Creating and monitoring translate service process ...~n"),
register(translator, spawn_link(fun()-> translator_loop() end)),
translator_doctor_loop();
{'EXIT', From, Reason} ->
io:fwrite("The translation service ~p died with reason ~p. Restarting.~n", [From, Reason]),
self() ! new_translator,
translator_doctor_loop()
end .
test_monitor_translator() ->
DoctorPid = spawn(fun()-> translator_doctor_loop() end),
DoctorPid ! new_translator, timer:sleep(50),
translator ! "casa", translator ! "blanca", timer:sleep(50),
translator ! "blah", timer:sleep(50),
translator ! "casa", timer:sleep(50),
unregister(translator) .
%
% Make the Doctor process restart itself if it should die.
%
% No idea how this is possible.
%
% Make a monitor for the Doctor monitor. If either monitor dies, restart it.
%
doctor_loop() ->
receive
new_companion_doctor ->
spawn_link(fun()-> doctor_init() end),
doctor_loop();
{'EXIT', From, Reason} ->
io:fwrite("The doctor ~p died with reason ~p. Restarting.~n", [From, Reason]),
self() ! new_companion_doctor,
doctor_loop()
end .
doctor_init() ->
io:fwrite("Doctor ~p spawned.~n", [self()]),
process_flag(trap_exit, true),
case whereis(doctor1) of
undefined ->
register(doctor1, self()),
io:fwrite("Doctor ~p registered as doctor1.~n", [self()]);
_ ->
register(doctor2, self()),
io:fwrite("Doctor ~p registered as doctor2.~n", [self()])
end,
doctor_loop() .
test_companion_doctor() ->
spawn(fun() -> doctor_init() end) ! new_companion_doctor, timer:sleep(10),
exit(whereis(doctor1), kill), timer:sleep(10),
exit(whereis(doctor2), kill), timer:sleep(10),
exit(whereis(doctor1), kill), timer:sleep(10),
exit(whereis(doctor2), kill), timer:sleep(10) .
%
% Create a basic OTP server that logs messages to a file.
%
init([]) ->
disk_log:open([{name, ?Log}]),
{ok, []} .
terminate(normal, []) ->
disk_log:close(?Log) .
handle_cast({log, Term}, []) ->
disk_log:alog(?Log, Term),
{noreply, []} .
handle_call({log, Term}, _From, []) ->
{reply, disk_log:log(?Log, Term), []};
handle_call({chunk, Continuation}, _From, []) ->
{reply, disk_log:chunk(?Log, Continuation), []};
handle_call(terminate, _From, []) ->
{stop, normal, ok, []} .
handle_info(Msg, []) ->
io:format("Unexpected message: ~p~n",[Msg]),
{noreply, []} .
code_change(_, [], _) ->
{ok, []} .
%
logger_start_link() -> gen_server:start_link(?MODULE, [], []) .
logger_log(Pid, Term) ->
gen_server:call(Pid, {log, Term}) .
logger_alog(Pid, Term) ->
gen_server:cast(Pid, {log, Term}) .
logger_history(Pid) ->
% simplified here
Continuation = gen_server:call(Pid, {chunk, start}),
io:fwrite("~p~n", [Continuation]) .
logger_terminate(Pid) ->
gen_server:call(Pid, terminate) .
%
test_logger_otp() ->
{ok, Pid} = logger_start_link(),
logger_log(Pid, 1),
logger_log(Pid, 2),
logger_log(Pid, 3),
logger_alog(Pid, "a"),
logger_alog(Pid, "b"),
logger_alog(Pid, "c"),
timer:sleep(50),
logger_history(Pid),
logger_terminate(Pid) .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
main(_) ->
test_count_words(), io:nl(),
test_count_to(), io:nl(),
test_report(), io:nl(),
test_get_value(), io:nl(),
test_compute_total_price(), io:nl(),
test_judge_tictactoe(), io:nl(),
test_monitor_translator(), io:nl(),
test_companion_doctor(), io:nl(),
test_logger_otp(), io:nl(),
io:nl() .
#!/usr/bin/clj
;;; Day 1
;; Implement a function called (big st n) that returns true
;; if a string st is longer than n characters.
(defn big [str n]
(> (count str) n))
; Tests
(println (big "abc" 3))
(println (big "abcd" 3))
(println)
;; Write a function called (collection-type col) that returns :list,
;; :map, or :vector based on the type of collection col.
(defn collection-type [col]
(cond
(list? col) :list
(map? col) :map
(vector? col) :vector
(set? col) :set))
; Tests
(println (collection-type `()))
(println (collection-type []))
(println (collection-type #{}))
(println (collection-type {}))
(println)
;;; Day 2
;; Implement an unless with an else condition using macros.
(defmacro unless
[test body & [else-body]]
(list 'if test else-body body))
; Tests
(println (unless false "fail"))
(println (unless true "fail"))
(println (unless true "fail" "succeed"))
(println)
;; Write a type using defrecord that implements a protocol.
(defn sqr [x] (* x x))
(defprotocol Figure
(area [this]))
(defrecord Square [side-length]
Figure
(area [this]
(sqr (:side-length this))))
(defrecord Circle [radius]
Figure
(area [this]
(* Math/PI (sqr radius))))
; Tests
(println (area (Square. 5)))
(println (area (Circle. 5)))
(println)
;;; Day 3
;; Use refs to create a vector of accounts in memory. Create debit
;; and credit functions to change the balance of an account.
(def accounts
(into []
(take 10 (repeatedly #(ref (rand-int 100))))))
(defn credit [account amount]
(dosync
(alter account + amount)))
(defn debit [account amount]
(dosync
(alter account - amount)))
; Tests
(println (map deref accounts))
(doseq [account accounts]
(credit account 100))
(println (map deref accounts))
(doseq [account accounts]
(debit account 50))
(println (map deref accounts))
(println)
;; Sleeping barber problem
; Some logging facilities to allow synchronized I/O
; Can't think of a better way without using 3rd party libraries (agent doesn't work)
(def logger
(let [logger (java.util.logging.Logger/getLogger "barber")
handler (java.util.logging.ConsoleHandler.)]
(.setUseParentHandlers logger false)
(.setFormatter handler
(proxy [java.util.logging.Formatter][]
(format [record]
(str
"[Thread-" (.getThreadID record) "]\t"
(.getMessage record) \newline))))
(.addHandler logger handler)
logger
))
(defn log [& content]
(.info logger (apply str (interpose " " content))))
(defn log-arrive [arrive-time]
(log "[Customer] arrives at" arrive-time))
(defn log-enqueue [arrive-time curr-waiting-list]
(log "[Customer]" arrive-time "now in waiting list:" (into [] curr-waiting-list)))
(defn log-leave [arrive-time curr-waiting-list]
(log "[Customer]" arrive-time "left, waiting list is full:" (into [] curr-waiting-list)))
(defn log-start-cut [arrive-time]
(log "[Barber] starts cutting hair for" arrive-time))
(defn log-finish-cut [arrive-time]
(log "[Barber] finished cutting hair for" arrive-time))
(def waiting-list (ref clojure.lang.PersistentQueue/EMPTY))
(defn new-customer [arrive-time]
(log-arrive arrive-time)
; A temporary agent for logging (side-effect)
; This is why waiting-list is a ref rather than an atom
(let [log-agent (agent nil)]
(dosync
; Try to enqueue
(if (< (count @waiting-list) 3)
(do
(alter waiting-list conj arrive-time)
(send log-agent
(fn [_] (log-enqueue arrive-time @waiting-list))))
(send log-agent
(fn [_] (log-leave arrive-time @waiting-list)))))))
; Alter a ref of queue with pop, return the dequeued head
(defn pop-head [queue]
(dosync
(when-let [head (peek @queue)]
(alter queue pop)
head)))
(def haircut-count (agent 0))
(defn nudge-the-barber []
(send haircut-count
#(if-let [customer (pop-head waiting-list)]
(do
(log-start-cut customer)
(Thread/sleep 200)
(log-finish-cut customer)
(inc %1))
%1)))
(add-watch waiting-list :barber-watch
(fn [_ _ old-state new-state]
(when (not= old-state new-state)
(nudge-the-barber))))
(loop [time 0]
(let [interval (+ 10 (rand-int 21))
arrive-time (+ time interval)]
(when (<= arrive-time 10000)
(Thread/sleep interval)
; Customers come from different threads!
(future (new-customer arrive-time))
(recur arrive-time))))
(await haircut-count)
(println "Total haircuts:" @haircut-count)
(shutdown-agents)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment