Showing posts with label seven-languages-in-seven-weeks. Show all posts
Showing posts with label seven-languages-in-seven-weeks. Show all posts

Tuesday, October 2, 2012

Seven Languages in Seven Weeks - Scala Day 2 Self Study

Hey there! I'm currently reading Seven Languages in Seven Weeks by Bruce A. Tate. As the title implies, this book explores seven different programming languages. Each chapter ends with a self study section. Here are the exercises of day 2 with the Scala programming language:

Use foldLeft to compute the total size of a list of strings:
Here are two variants, the first calls the foldLeft method (note the multiple parameter list, this is used for currying, i.e. partial function application) while the second is using the /: infix operator, which is in fact an overload of foldLeft:
  val list = List("foo", "bar", "hello", "world")
  list.foldLeft(0)((len, elem) => elem.size + len)//> res0: Int = 16
  (0 /: list) {(len, elem) => elem.size + len}    //> res1: Int = 16

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. Then, load the curse words and alternatives from a file:
The trait expects the curse words dictionary in the file words.txt:
Shoot:Pucky
Darn:Beans
The words are loaded line by line, each line is splitted at the : character and the values are stored in the substitions map. The replace method takes a text as input parameter, converts it as a list of words and applies the substitutions by looking up the map.
  trait Censor {
   val substitutions = new HashMap[String, String]
   
   Source.fromFile("/words.txt", "US-ASCII").getLines.foreach { line =>
    val elem = line.split(":", 2)
    substitutions += (elem(0) -> elem(1))
   }
   
   def replace(text: String): String = {
    text.split("\\s").map({word =>
     substitutions.getOrElse(word, word)
    }).reduceLeft((concat, word) => concat + " " + word)
   }
  }
Here's a simple example showing how the trait works:
  val censor = new Censor {}
  censor.replace("hello Darn world") //> res2: String = hello Beans world
Stay tuned, I'll soon be posting the 3rd study section about Scala.

Sunday, August 19, 2012

Seven Languages in Seven Weeks - Scala Day 1 Self Study

It's time for the next programming language in Seven Languages in Seven Weeks: Scala!
The self-study assignment consists in writing a program that will take a tic-tac-toe board and determine which player won (if any):
import scala.io.Source;

object TicTacToe extends App {

  val wins = List(
    List(0, 1, 2), List(3, 4, 5), List(6, 7, 8),
    List(0, 3, 6), List(1, 4, 7), List(2, 5, 8),
    List(0, 4, 8), List(2, 4, 6)
  )
  val input = Source.fromFile("tictactoe.txt").mkString
  val board = (for (chars <- "X|O".r findAllIn input) yield chars).toList

  if (isWin("X")) {
    if (isWin("O")) {
      println("Tie")
    } else {
      println("Player X wins")
    }
  } else {
 if (isWin("O")) {
      println("Player O wins")
 } else {
   println("No winner")
 }
  }

  def isWin (char: String): Boolean = {
    for (win <- wins) {
      var result = true
      for (x <- win) {
        result &= board(x) == char
      }
      if (result)
        return true
    }
 return false
  }
}

Monday, June 18, 2012

Seven Languages in Seven Weeks - Prolog Day 3 Self Study

Time to catch up with Seven Languages in Seven Weeks! This is the last chapter on Prolog and here are the tasks:

  • Modify the Sudoku solver to work on six-by-six puzzles (square are 3x2) and 9x9 puzzles
  • Make the Sudoku solver print prettier solutions

Note: since I am using SWI Prolog instead of gprolog as used in the book, there are a few differences:

Here's the solution for six-by-six puzzles:
:- use_module(library(clpfd)).
valid([]).
valid([H|T]) :- all_different(H), valid(T).

sudoku(Puzzle, Solution) :-
 Solution = Puzzle,
 Puzzle = [
  S11, S12, S13, S14, S15, S16,
  S21, S22, S23, S24, S25, S26,
  S31, S32, S33, S34, S35, S36,
  S41, S42, S43, S44, S45, S46,
  S51, S52, S53, S54, S55, S56,
  S61, S62, S63, S64, S65, S66
 ],
 Puzzle ins 1..6,
 Row1 = [S11, S12, S13, S14, S15, S16],
 Row2 = [S21, S22, S23, S24, S25, S26],
 Row3 = [S31, S32, S33, S34, S35, S36],
 Row4 = [S41, S42, S43, S44, S45, S46],
 Row5 = [S51, S52, S53, S54, S55, S56],
 Row6 = [S61, S62, S63, S64, S65, S66],
 Col1 = [S11, S21, S31, S41, S51, S61],
 Col2 = [S12, S22, S32, S42, S52, S62],
 Col3 = [S13, S23, S33, S43, S53, S63],
 Col4 = [S14, S24, S34, S44, S54, S64],
 Col5 = [S15, S25, S35, S45, S55, S65],
 Col6 = [S16, S26, S36, S46, S56, S66],
 Rect1 = [S11, S12, S13, S21, S22, S23],
 Rect2 = [S14, S15, S16, S24, S25, S26],
 Rect3 = [S31, S32, S33, S41, S42, S43],
 Rect4 = [S34, S35, S36, S44, S45, S46],
 Rect5 = [S51, S52, S53, S61, S62, S63],
 Rect6 = [S54, S55, S56, S64, S65, S66],
 valid([Row1, Row2, Row3, Row4, Row5, Row6,
  Col1, Col2, Col3, Col4, Col5, Col6,
  Rect1, Rect2, Rect3, Rect4, Rect5, Rect6]).

pretty_print(Puzzle) :-
 writef("\
 +-----+ +-----+\n\
 |%d %d %d| |%d %d %d|\n\
 |%d %d %d| |%d %d %d|\n\
 +-----+ +-----+\n\n\
 +-----+ +-----+\n\
 |%d %d %d| |%d %d %d|\n\
 |%d %d %d| |%d %d %d|\n\
 +-----+ +-----+\n\n\
 +-----+ +-----+\n\
 |%d %d %d| |%d %d %d|\n\
 |%d %d %d| |%d %d %d|\n\
 +-----+ +-----+", Puzzle).

Thursday, April 26, 2012

Seven Languages in Seven Weeks - Prolog Day 2 Self-Study

More fun with prolog! This self-study section consists in implementing some list operations.

Reverse the elements of a list
rev([], []).
rev([Head|Tail], List) :- append(X, [Head], List), rev(Tail, X).

Find the smallest element of a list.
min([X], X).
min([Head1|[Head2|Tail]], X) :- Head1 =< Head2, min([Head1|Tail], X).
min([Head1|[Head2|Tail]], X) :- Head1 > Head2, min([Head2|Tail], X).

Sort the elements of a list.
insert(X, [], [X]).
insert(X, [H|T], [X,H|T]) :- X =< H.
insert(X, [H|T], [H|Rest]) :- X > H, insert(X, T, Rest).

isort([], Acc, Acc).
isort([H|T], Acc, Sorted) :- insert(H, Acc, Acc2), isort(T, Acc2, Sorted).

Wednesday, April 25, 2012

Seven Languages in Seven Weeks - Prolog Day 1 Self-Study

For this book's section about prolog, I'll be using SWI-Prolog, as I'm currently getting an error installing the gnu-prolog macport. Here's the link to the SWI-Prolog online reference.

Make a simple knowledge base. Represent some of your favorite books and authors.
% books.pl
book(sevenLanguagesInSevenWeeks).
book(cleanCode).
book(cleanCoder).
book(programmingInScala).
author(odersky).
author(tate).
author(martin).
wrote(odersky, programmingInScala).
wrote(tate, sevenLanguagesInSevenWeeks).
wrote(martin, cleanCode).
wrote(martin, cleanCoder).

Find all books in your knowledge base written by one author.
?- ['books'].
% books compiled 0.00 sec, 13 clauses
true.

?- wrote(martin, X).
X = cleanCode ;
X = cleanCoder.

Make a knowledge base representing musicians and instruments. Also represent musicians and their genre of music.
% music.pl
instrument(flea, bass).
instrument(hendrix, guitar).
instrument(hancock, piano).
instrument(cash, guitar).
genre(flea, funk).
genre(hendrix, blues).
genre(hancock, jazz).
genre(cash, country).

Find all musicians who play the guitar.
?- ['music'].
% music compiled 0.00 sec, 10 clauses
true.

?- instrument(X, guitar).
X = hendrix ;
X = cash.

Wednesday, March 28, 2012

Seven Languages in Seven Weeks - Io Day 3 Self-Study

It's been some time now, still I'm keeping up reading Seven Languages in Seven Weeks. Here's the last self-study section about the Io programming language.

Enhance the XML program to add spaces to show the indentation structure.

Builder := Object clone
Builder numIndents := 0
Builder doIndents := method (for (i, 1, numIndents, write(" ")))
Builder openTag := method(name, doIndents; writeln("<", name, ">"))
Builder closeTag := method(name, doIndents; writeln("</", name, ">"))
Builder forward := method (
  openTag(call message name);
  numIndents = numIndents + 1;
  call message arguments foreach (
    arg,
    content := self doMessage(arg);
    if (content type == "Sequence", doIndents; writeln(content))
  );
  numIndents = numIndents - 1;
  closeTag(call message name)
)

Builder ul(
  li("Javascript"),
  li("Lua"),
  li("Javascript")
)

Create a list syntax that uses brackets

squareBrackets := method(call message arguments)

Enhance the XML program to handle attributes: if the first argument is a map (use the curly brackets syntax), add attributes to the XML program. For example: book({"author": "Tate"}...) would print <book author="Tate">.

OperatorTable addAssignOperator(":", "atPutNumber")
Map atPutNumber := method(
  self atPut(
    call evalArgAt(0) asMutable removePrefix("\"") removeSuffix("\""),
    call evalArgAt(1)
  )
)
curlyBrackets := method(
  map := Map clone;
  call message arguments foreach(arg, map doMessage(arg));
  map
)

Builder := Object clone
Builder numIndents := 0
Builder doIndents := method (for (i, 1, numIndents, write(" ")))
Builder doAttributes := method(map, map keys foreach(key, write(" ", key, "=\"", map at(key) ,"\"")))
Builder forward := method (
  doIndents; write("<", call message name);
  if(call message arguments size == 0, writeln(">"));
  numIndents = numIndents + 1;
  call message arguments foreach (i, arg,
    content := self doMessage(arg);
    if (i == 0, 
      if(content type == "Map", doAttributes(content));
      writeln(">")
    )
    if (content type == "Sequence", doIndents; writeln(content));
  );
  numIndents = numIndents - 1;
  doIndents; writeln("</", call message name, ">")
)

Monday, March 12, 2012

Seven Languages in Seven Weeks - Io Day 2 Self-Study

Here's the next self-study section about the Io programming language. These are several exercises to get some practice.

A Fibonacci sequence starts with two 1s. Each subsequent number is the sum of the two numbers that came before; 1, 1, 2, 3, 5, 8, 13, 21, and so on. Write a program to get the nth Fibonacci number. fib(1) is 1, and fib(4) is 3. As a bonus, solve the problem with recursion and with loops

The recursive approach (very concise):
fib := method(n,  if (n <= 2, 1, fib(n-1) + fib(n-2)))
And with a loop:
fib := method(n,
  p := 1;
  q := 1;
  sum := 1;
  for (i, 3, n,
    sum := p + q;
    p := q;
    q := sum
  );
  sum
)
How would you change / to return 0 if the denominator is zero?
Number div := Number getSlot("/")
Number / := method(n, if (n == 0, 0, self div(n))
Write a program to add up all of the numbers in a two-dimensional array.
a := list(list(1, 2, 3, 4), list(5, 6, 7, 8))
s := 0
a foreach(sublist, s := s + sublist sum)
Or more concise:
a flatten sum
Add a slot called myAverage to a list that computes the average of all the numbers in a list. What happens if there are no numbers in a list? (Bonus: Raise an Io exception if any item in the list is not a number.)
List myAverage := method(self sum / self size)
And with exception handling:
List myAverage := method(
  sum := 0;
  self foreach(e,
    (e isKindOf(Number)) ifFalse(Exception raise("not a number"));
    sum := sum + e
  );
   sum / self size
)
Write a prototype for a two-dimensional list. The dim(x, y) method should allocate a list of y lists that are x elements long. set(x, y, value) should set the value, and get(x, y) should return that value
List2d := Object clone

List2d dim := method(x, y,
  self rows := List clone;
  for (i, 1, x,
    cols := List clone;
    for (j, 1, y,
      cols append(nil)
    );
    self rows append(cols)
  );
  self
)

List2d set := method(x, y, value, 
  self rows at(x) atPut(y, value);
  self
)

List2d get := method(x, y, 
  self rows at(x) at(y)
)
Bonus: write a transpose method so that (new_matrix get(y, x)) == matrix get(x, y) on the original list.
List2d transpose := method(
  transposed := List2d clone;
  transposed dim(self rows at(0) size, self rows size);
  self rows foreach(i, row, 
    row foreach(j, elem,
      transposed set(j, i, elem)
    );
  );
  transposed;
)
Write the matrix to a file, and read a matrix from a file
List2d toFile := method(filename,
  file := File clone open(filename);
  self rows foreach(col, 
    col foreach (elem,
      file write(elem asString, " ")
    );
    file write("\n")
  );
  file flush close;
  file
)

List2d fromFile := method(filename,
  self rows := List clone;
  file := File clone open(filename);
  file readLines foreach(line,
    cols := line split;
    self rows append(cols)
  );
  self
)
Write a program that gives you ten tries to guess a random number from 1--100. If you would like, give a hint of "hotter" or "colder" after the first guess.
secretNum := Random value(100) floor() + 1
in := File standardInput
for (i, 1, 10,
  write("Guess number (try #", i, "): ");
  guess := in readLine asNumber;
  if (guess == secretNum, write("Win!"); break);
  if (i > 1, 
    if ((secretNum - guess) abs < (secretNum - previousGuess) abs,
      write("hotter\n"),
      write("cooler\n")
    )
  );
  previousGuess := guess;
)

Thursday, March 8, 2012

Seven Languages in Seven Weeks - Io Day 1 Self-Study

Here's the first self-study section about the Io programming language. I didn't know Io before, I still find it a bit awkward, but I'm beginning to like it.

Evaluate 1 + 1 and then 1 + "one". Is Io strongly typed or weakly typed? Support you answer with code.
Io> 1 + 1
==> 2
Io> 1 + "one"

  Exception: argument 0 to method '+' must be a Number, not a 'Sequence'
  ---------
  message '+' in 'Command Line' on line 1
Io is strongly typed, operands of different types will not be implicitly converted:
Io> 1 + "2"

  Exception: argument 0 to method '+' must be a Number, not a 'Sequence'
  ---------
  message '+' in 'Command Line' on line 1
Is 0 true or false? What about the empty string? Is nil true or false? Support your answer with code.
Io> 0 and true
==> true
Io> "" and true
==> true
Io> nil and true
==> false
How can you tell what slots a prototype supports?

Invoking an object in the prompt will print the supported slots:
Io> Car
==>  Car_0x7fb039c9a1f0:
  type             = "Car"
  vroom            = method(...)
The slotNames message returns the list of slots:
Io> Car slotNames
==> list(type, vroom)
To get the slots defined by the parent, call the proto message:
Io> Car proto
==>  Vehicle_0x7fb039c6e270:
  description      = "Something that takes you far away"
  type             = "Vehicle"
What is the difference between = (equals), := (colon equals), and ::= (colon colon equals)? When would you use each one?
=Assigns value to slot if it exists, otherwise raises exception
:=Creates slot, assigns value
::=Creates slot, creates setter, assigns value
Run an Io program from a file
$ echo '"Hello, world!" println' > helloworld.io
$ io helloworld.io 
Hello, world!
Execute the code in a slot given its name

Unfortunately, I haven't been able to solve this one yet. I guess the intent is given a slot holding some code as a string, one should figure out a statement to execute this code.
Io> code := "\"hello\" println"
==> "hello" println

Friday, March 2, 2012

Seven Languages in Seven Weeks - Ruby Day 3 Self Study

Hi again. Here's the self-study section for day 3 from the book Seven Languages in Seven Weeks, the last one about ruby.

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 a given heading.
module ActsAsCsv
  
  def self.included (base)
    base.extend ClassMethods
  end
  
  module ClassMethods
    
    def acts_as_csv
      include InstanceMethods
    end
    
    module InstanceMethods
      
      class CsvRow
        def initialize (headers, row)
          @headers = headers
          @row = row
        end
        
        def method_missing (name, *args)
          index = @headers.index(name.to_s)
          @row[index] unless index.nil?
        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 << CsvRow.new(headers, row.chomp.split(', '))
        end
      end
      
      attr_accessor :headers, :csv_contents
      
      def initialize
        read
      end
      
      def each (&block)
        @csv_contents.each(&block)
      end
    end
  end
end

Wednesday, February 29, 2012

Seven Languages in Seven Weeks - Ruby Day 2 Self-Study

These are my answers to the 2nd self-study section of Seven Languages in Seven Weeks, enjoy.

Find out how to access files with and without code blocks. What is the benefit of the code block?
file = File.open("hello.txt")
line = file.readline
until line.nil? 
  puts line
  line = file.readline
end
file = File.open("hello.txt")
file.each { |line| puts line }
Latter variant is far more concise. Also, using a code block one avoids dealing with the file object's state.

How would you translate a hash to an array? Can you translate arrays to hashes?
Taking a hash, one can convert it into an array of key-value pairs:
hash.each_pair { |key, value| array.push([key, value]) }
Taking an array, one can map each value to its index (which becomes the key):
array.each_with_index { |item, index| hash[index] = item }

Can you iterate through hash?
Yes, using each_pair (see also example above).

You can use Ruby arrays as stacks. What other common data structures do arrays support?
Ruby arrays provide the push and pop stack operations. To use an array as a queue, use push to enqueue and shift to dequeue items. List operations are supported using methods like first, insert, etc.

Print the contents of an array of sixteen numbers, four numbers at the time, using just each.
i = 1
array.each do |x|
  print x, ' '
  puts if i % 4 == 0
  i += 1
end

Now, do the same with each_slice in Enumerable.
array.each_slice(4) { |slice| p slice }

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 of hashes. 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 (name, children={}) 
    @node_name = name
    @children = []
    children.each_pair {|key, val| @children << Tree.new(key, val) }
  end
  
  def visit_all (&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end
  
  def visit (&block)
    block.call self
  end
end
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 simple in Ruby.) If you want, include line numbers.
def grep (pattern, filename)
  f = File.open(filename)
  f.each {|line| puts "#{f.lineno}: #{line}" if line =~ /#{pattern}/}
end

Monday, February 27, 2012

Seven Languages in Seven Weeks - Ruby Day 1 Self-Study

I started reading Seven Languages in Seven Weeks. I'll be posting some of my answers to the self-study section at the end of each chapter, mainly for self-documentation.

Some links:

Print the string "Hello, world."
>> puts "Hello, World"
Hello, World
=> nil

For the string "Hello, Ruby", find the index of the word "Ruby"
>> "Hello, Ruby".index("Ruby")
=> 7

Print your name ten times
>> 10.times { puts "ant0inet" }
ant0inet
ant0inet
ant0inet
ant0inet
ant0inet
ant0inet
ant0inet
ant0inet
ant0inet
ant0inet
=> 10

Run a Ruby program from a file.
$ cat > hello_world.rb << EOF
> #!/usr/bin/env ruby
> puts "Hello, world."
> EOF
$ ruby hello_world.rb 
Hello, world.

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.
#!/usr/bin/env ruby

random_num = rand(10) + 1
puts "Enter your guess:"
guess = gets.to_i
until guess == random_num
  if guess < random_num
    puts "too low"
  else
    puts "too high"
  end
  guess = gets.to_i
end 
puts "correct"