Seven Languages in Seven Weeks: Day 6

Last day of Io and what do we have? A small snippet that adds [1, 2, 3] style array literals to the language. Kind of neat that you can do it... also the kind of thing that can almost certainly get you in trouble.

squareBrackets := method(
    lst := List clone
    call evalArgs foreach(arg,
        lst append(arg)
    )
    return lst
)

... and here's a Builder that lets you create XML documents by dynamically translating messages into XML elements.

Builder := Object clone
Builder forward := method(
    node := Element new(call message name)
    call message arguments foreach(arg,
        content := self doMessage(arg)
        if(content type == "Sequence",
            node addChild(TextNode new(content)),
            node addChild(content)
        )
    )
    return node
)

Node := Object clone
Node indent := method(depth,
    return "\t" repeated(depth)
)

TextNode := Node clone
TextNode new := method(text,
    node := TextNode clone
    node text := text
    return node
)

TextNode render := method(depth,
    writeln(self indent(depth), self text)
)

Element := Node clone
Element new := method(name,
    other := self clone
    other attrs := Map clone
    other name := name
    other children := List clone
    return other
)

Element addChild := method(child,
    self children append(child)
)

Element attr := method(k, v,
    self attrs atPut(k, v)
    return self
)

Element fmtAttrs := method(
    if(self attrs isEmpty,
        return "",
        return " " with(self attrs map(k, v,
            k with("=\"", v, "\"")
        ) join(" "))
    )
)

Element render := method(depth,
    writeln(indent(depth), "<", self name, self fmtAttrs, ">")
    self children foreach(child,
        child render(depth + 1)
    )
    writeln(indent(depth), "</", self name, ">")
)

... and a Builder document ends up looking like this:

Builder ul(
    li("some text") attr("class", "first"),
    li("some more text"),
    li("even more text") attr("class", "last")
) attr("class", "fancy") attr("id", "menu") render(0)

Seven Languages in Seven Weeks: Day 5

Have you ever wanted some bastard to redefine division on you? I haven't either, but if it ever comes up, you can totally do it in Io:

Number trueDiv := Number getSlot("/")
Number / = method(n,
    if(n == 0,
        return 0,
        return self trueDiv(n)
    )
)

... and here's something that approaches useful: A two-dimensional matrix class that's backed by a one-dimentional list. It includes getter, setter and iterator methods. Yay!

Matrix := Object
Matrix dim := method(x, y,
    self width := x
    self height := y
    self buffer := list()
    self buffer setSize(x * y)
)

Matrix checkBounds := method (x, y,
    if(x >= width or y >= height,
        Exception raise("index out of bounds")
    )
)

Matrix get := method(x, y,
    checkBounds(x, y)
    return self buffer at(x + y * width)
)

Matrix set := method(x, y, value,
    checkBounds(x, y)
    self buffer atPut(x + y * width, value)
)

Matrix forEach := method(f,
    for(y, 0, height - 1,
        for(x, 0, width - 1,
            f call(x, y, self get(x, y))
        )
    )
)

Seven Languages in Seven Weeks: Day 4

Io is the second language covered in Seven Languages in Seven Weeks and it is substantially further off the beaten path than Ruby was. The first day's worth of exercises didn't really have anything code snippet worthy, so here's recursive Fibonnaci to prove that I got the VM running ;-)

Fib := Object clone
Fib fib := method(n,
    if(n < 2, return 1)
    return self fib(n - 1) + self fib(n - 2)
)
Io> Fib fib(6)
==> 13

Seven Languages in Seven Weeks: Day 3

The emphasis for the final day of Ruby was on metaprogramming using open classes and mixins. So without further ado, here's a CSV reader that provides a mixin that lets you access the values of a CSV file using dynamic properties (Whee!):

module ActsAsCsv
    class CsvRow
        def initialize(csv, fields)
            @fields = fields
            @csv = csv
            @map = Hash[@csv.headers.zip(fields)]
        end

        def method_missing(key)
            @map[key.to_s]
        end
    end

    def self.included(base)
        base.extend ClassMethods
    end

    module ClassMethods
        def acts_as_csv
            include InstanceMethods
        end
    end

    module InstanceMethods
        include Enumerable

        attr_accessor :headers, :csv_contents

        def initialize
            read
        end

        def each
            @csv_contents.each do |row|
                yield row
            end
        end

        def parse(s)
            s.chomp.split(', ')
        end

        def read
            @csv_contents = []
            filename = self.class.to_s.downcase + ".csv"
            file = File.new(filename)
            @headers = parse(file.gets)

            file.each do |row|
                fields = parse(row)
                @csv_contents << CsvRow.new(self, fields)
            end
        end
    end
end

Given a CSV file like this:

msg, response
Hello!, Hi!

... we could read it like this:

class Example
    include ActsAsCsv
    acts_as_csv
end

Example.new.each do |row|
    puts row.msg, row.response
end

Seven Languages in Seven Weeks: Day 2

A grep work-a-like which despite being much shorter then the previous exercise actually has the possibility of being useful:

pattern = Regexp.new(ARGV[0])
path = ARGV[1]

File.open(path, "r").each_with_index do |line, i|
    puts "#{path}:#{i + 1}\t#{line}" if line.index(pattern)
end