Io, Day 3
Final day with Io. It’s been quite a ride, and even if I don’t think I grasp everything that Io is, I consider playing with Io a little bit on my own later because of it’s potential, especially in DSL.
Enhance the XML program to add spaces to show the indentation structure
Builder := Object clone Builder depth := 0 Builder forward := method( prefix := " " repeated(self depth) writeln(prefix, "<", call message name, ">") self depth = self depth + 1 call message arguments foreach(arg, content := self doMessage(arg) if(content type == "Sequence", writeln(prefix, " ", content)) ) self depth := self depth - 1 writeln(prefix, "</", call message name, ">") ) Builder html(head(title("Programing languages")), body(ul(li("IO"), li("Lua"), li("JavaScript"))))
Create a list syntax that uses brackets
curlyBrackets := method( l := List clone call message arguments foreach(arg, l append(doMessage(arg)) ) return l )
Io, Day 3
Let’s get down to some coding.
Write a method to find the nth Fibonacci number, both iteratively and recursively
Fibonacci := Object clone Fibonacci iter := method(n, l := list(1, 1) for(i, 2, n, 1, l append(l at(i - 2) + l at(i - 1)) ) l at(n - 1) ) Fibonacci rec := method(n, if(n < 3, 1, rec(n - 1) + rec(n - 2) ) ) x := Fibonacci iter(12) x println y := Fibonacci rec(12) y println
How would you change the operator / to return 0 is the denominator is zero
For this one, I was a bit stuck with only the book and the language reference, so I had to Google it. And that’s where you have all the beauty and annoyance of the internet: one of the first hit is a solution to that exact question on StackOverflow (it was to be expected.) I was expecting this exercise to take me to the OperatorTable for which there is an example in the book, but actually the operator already exists so it does not have to be added, but instead re-defined for the Number type.
Write a program to add up all the numbers in a two-dimensional array
a := list(list(1, 2, 3)) a append(list(4, 5, 6, 7, 8, 9)) a append(list(10, 11, 12)) a append(list(13, 14, 15, 16, 17, 18, 19, 20)) total := 0 for(i, 0, a size - 1, b := a at(i) for(j, 0, b size - 1, n := b at(j) total = (total + n) ) ) total println
Add an myAverage slot 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 exception if any item in the list is not a Number.
a := list(1, 2, 3, 4, 5) b := list(1, "hello") List myAverage := method( total := 0 for(i, 0, self size -1, n := self at(i) if (n type != "Number", Exception raise("An item in the list is not a Number"), total = total + n ) ) total / self size ) a myAverage println b myAverage println
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. The set(x, y, value) method should set a value and the get(x, y) method should return that value.
Matrix := Object clone Matrix list := nil Matrix dim := method(x, y, self list := List clone for(i, 0, x - 1, l := List clone for(j, 0, y - 1, l append(nil)) self list append(l) ) ) Matrix set := method(x, y, value, self list at(x) atPut(y, value) ) Matrix get := method(x, y, self list at(x) at(y) ) matrix := Matrix clone matrix dim(4, 2) matrix set(3, 1, "Hello") matrix set(1, 1, "World") matrix get(3, 1) println matrix get(1, 1) println
Bonnus: write a transpose method so that new_matrix get(y, x) == matrix get(x, y) on the original list
Matrix transpose := method( x := self list at(0) size y := self list size m := Matrix clone m dim(x, y) for(i, 0, x - 1, for(j, 0, y - 1, m set(i, j, self get(j, i)) ) ) m ) new_matrix := matrix transpose (matrix get(3, 1) == new_matrix get(1, 3)) println
Write the matrix to a file, read the matrix from a file.
For this one, decided to aggressively look in the List API to make the code simpler. I discovered quite a lot of useful functions! I also implemented the asString method so that it is easier to debug (the println method on the Object type is implemented to print the result of asString.)
Here is the full code for the whole Matrix object, improved as much as I could:
Matrix := Object clone Matrix list := nil Matrix dim := method(x, y, self list := List clone setSize(x) for(i, 0, x - 1, self list atPut(i, List clone setSize(y)) ) ) Matrix set := method(x, y, value, self list at(x) atPut(y, value) ) Matrix get := method(x, y, self list at(x) at(y) ) Matrix transpose := method( x := self list at(0) size y := self list size m := Matrix clone m dim(x, y) for(i, 0, x - 1, for(j, 0, y - 1, m set(i, j, self get(j, i)) ) ) return m ) Matrix asString := method( s := "" self list foreach(e, s := s asMutable appendSeq(e join(",")) asMutable appendSeq("\n") ) ) Matrix saveTo := method(file, f := File with(file) remove openForUpdating self list foreach(i, e, f write(e join(",")) write("\n") ) f close ) Matrix loadFrom := method(file, l := List clone f := File with(file) openForReading f readLines foreach(e, l append(e splitNoEmpties(",")) ) f close m := Matrix clone m dim(l size, l at(0) size) l foreach(i, e, e foreach(j, f, m set(i, j, f) ) ) m ) /* Tests */ matrix saveTo("matrix.txt") new_matrix saveTo("new_matrix.txt") matrix := Matrix loadFrom("matrix.txt") new_matrix := Matrix loadFrom("new_matrix.txt") matrix println new_matrix println
Write a program that gives you 10 tries to guess a random number from 1 to 100. Give hints of “Hotter” or “Colder” if you like.
//Not really random, but will suffice for now n := Date now second * 1000 % 100 input := File standardInput(); for(i, 1, 10, guess := input readLine asNumber if (guess < n, "Hotter" println) if (guess > n, "Colder" println) if (guess == n, "Well done!" println break ) )
I really enjoyed myself with these! Looking forward to Day 3…
Io, Day 1
First day on Io, which I never heard about…
Io is a dynamic prototype-based programming language.
Apart from JavaScript, I never heard any other prototype-based language. And even in JavaScript, I don’t know exactly what this implies, so my expectation of learning new things while studying Io is quite high, which is good!
By the way, Io is the name of a nymph in Greek mythology and one of Jupiter’s satellite. Believe it or not, but I knew that before encountering the language, and still wonder if there is any relation whatsoever, but didn’t find anything about the language name’s origin…
(to me it was the perfect excuse to put a picture of a sexy girl on my blog and get more traffic!)
Anyway, first task, get Io to run on my box, namely a Windows 7 x64 machine. After downloading the sources and trying to build them according to the readme.txt, lots of errors…
Finally, I found that the best solution is to download the binaries which work fine on my machine. That’ll do for now.
I also needed some reference to lookup to when in doubt, and that’s all available here.
So, on day one, no real assignments, or at least nothing that can’t be found in 30 seconds on the official website, so I’m not going to replicate this here.
Ruby, Day 3
Last day on Ruby!
Retrospectively, I didn’t knew Ruby before (and I still don’t know much about it, admittedly), but it’s been fun.
I’m not a big fan after what I saw. I still find it a bit messy, but that’s maybe because I’m not used to it enough. The “you can rewrite anything and change basic behavior” is very nice, but I seriously wonder how many times it has bitten back developers. If someone rewrites one of the basic methods in a big codebase, I can only imagine the consequences, if you are luck enough to see them right away…
However, I enjoyed scripting with Ruby very much, and it was a good brainteaser to try to find the effective way of doing those things in a language I completely ignored so far.
Anyway, here is my last homework!
Do
So, the goal here is to modify a class that reads CSV files and add an each method that will perform a block on a CsvRow object, object that is to be written. This object should have its method_missing overwritten so that you can access columns as if they were methods:
module ActsAsCsv def self.included(base) base.extend ClassMethods end module ClassMethods def acts_as_csv include InstanceMethods end end module InstanceMethods def read @csv_contents = [] filename = self.class.to_s.downcase + '.csv' 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.each { |line| block.call CsvRow.new(@headers, line) } end end end class RubyCsv # no inheritance! You can mix it in include ActsAsCsv acts_as_csv end class CsvRow attr_accessor :headings, :values def initialize(headings, values) @headings = headings @values = Hash[headings.zip(values).map { |e| [e[0], e[1]] }] end def method_missing(name, *args) col = name.to_s @values[col] end end csv = RubyCsv.new csv.each { |row| puts row.two }
Ruby, Day 2
Find
#File IO without block #f = File.new("testfile", "r") #f.close #File IO with block, implicit call to f.close #File.open("testfile", "r") do |aFile| end #Hash to array h = { 1 => 2, "cat" => "tom" } a = h.to_a #Array to hash a = (1..10).to_a h = Hash[a.map { |i| [i, i * 2] } ] #Iterate through a hash h.each { |i| puts "#{i[0]} : #{i[1]}" } #Use array as stack a.push 19 a.pop #Arrays can be used as: stacks, sets, queues, dequeues, and fifos
Do
Print the content of an array of sixteen numbers, four numbers at a time, using just each. Now, do the same with each_slice in Enumerable:
#Using each (interesting fact: there is a closure on a here) a.each { |i| if i % 4 == 0 then puts "#{a[i-4..i-1]}" end } #Using each_slice a.each_slice(4) { |s| puts "#{s}" }
Rewrite the Tree class so it can be initialized with a nested structure with hashes and arrays:
#Initial code from the book class Tree attr_accessor :children, :node_name def initialize(name, children=[]) @children = children @node_name = name end def visit_all(&block) visit &block @children.each { |c| c.visit_all &block } end def visit(&block) block.call self end end t = Tree.new("Ruby", [Tree.new("Gem")]) t.visit_all { |t| puts "#{t.node_name}" }
Starting from that, this is what we want to be able to write:
Tree.new({ 'grandpa' => { 'dad' => { 'child 1' => {}, 'child 2' => {} }, 'uncle' => { 'child 3' => {}, 'child 4' => {} } } })
And here is my solution to it:
class Tree attr_accessor :children, :node_name def initialize(structure) structure.each do |key, value| @node_name = key @children = value.to_a.map { |e| Tree.new({ e[0] => e[1] }) } end end def visit_all(&block) visit &block @children.each { |c| c.visit_all &block } end def visit(&block) block.call self end end t = Tree.new({ 'grandpa' => { 'dad' => { 'child 1' => {}, 'child 2' => {} }, 'uncle' => { 'child 3' => {}, 'child 4' => {} } } }) t.visit_all { |t| puts "#{t.node_name}" }
Write a simple grep that will print the lines of a file having any occurrences of a phrase anywhere in that line. Use Regular Expressions and include line numbers:
regex = Regexp.new('sit amet') ln = 0 IO.foreach("TestGrep.txt") do |line| ln = ln + 1 puts "#{ln}: #{line}" if regex.match(line) end
