Monday, September 17, 2012

Ice skating in the desert

It doesn't matter how well you figure-skate on the ice hockey rink.

I have seen this error appear a number of times in my life. Sometimes I'm watching others make it; sometimes I'm waking up to my own blunders.

Covey had a similar phrase: It doesn't matter how fast you climb the wrong ladder.

My Data Communications lecturer at uni (love ya, Terry) used to tell the best anecdotes. He had one about a group of coders who'd been corrected by a senior programmer. They were chiding his solution for being slower than theirs. The problem was, theirs just didn't work. Terry instilled in me then the lesson: It doesn't matter how fast you can calculate the wrong answer.

A few years ago I was teaching ESL at a university in China. I was getting increasingly frustrated at my lessons - the kids just weren't getting on board! I woke up one morning with the revelation: I was playing basketball on the soccer field. D'Oh! So I changed up my game to address their actual needs and the lessons started flowing a lot better. I dropped my preconceptions about what I thought they should want at that stage of their education and instead looked at (and listened to!) what they really needed.

Sometimes we need to be reminded that it doesn't matter how much we know or how much we've done in a particular field or endeavour if that game isn't the one we're being asked to play right now.

Sunday, September 16, 2012

Vim is Like a Big School

Vim is like a big school. When the first-graders come they are shown their playground and classrooms, the washrooms and the canteen. They are happy and content running around in their little world. When they accidentally stumble into the 6th graders' hall, they o_O and run away in terror. It’s only when they are walking alongside a grown-up that they happily follow along, walking right through the scary hall without realising it. Soon those same kids are running through all the halls without fear.

Don’t be afraid to explore your Vim grounds. Sure, you may stumble into uncharted territory and see something really scary - but it’s mostly harmless and there are only a few rooms with auto-closing doors… And the basement is a little tricky to get out of… And you might trip and stumble or run into something sharp and painful. You might even end up running away, screaming. Wear your brown pants and a buffer you can afford to lose and you’ll be just fine.

If you need a grown-up’s hand to hold, knock on the #vim office door - there are plenty of cheerful guides in there who are happy to help.

Two of the scariest rooms to try:
  • Enter the basement from normal mode with gQ
    • The basement is not like any other room in Vim… You can’t leave with :q but must instead scream the school’s Latin name: :vi!
  • Wait in the principal’s office with q:
    • You might also find yourself being sent there for swearing (ctrl-f) on the : command line.

Sunday, September 9, 2012

Genetic Algorithms in VimL (Part I)

Burak Kanber is into machine learning. I was entertained by his Hello World genetic algorithm example and, in alignment with his implementation language agnosticism, I thought I'd write a version in VimL:

This depends on vim-rng for the random number stuff.

let s:Chromosome = {}

function! s:Chromosome.New(...)
  let chromosome = copy(self)
  let chromosome.code = ''
  let chromosome.cost = 9999
  let chromosome.pivot = 0

  if a:0
    let chromosome.code = a:1
    let chromosome.pivot = (strchars(chromosome.code) / 2) - 1

  return chromosome

function! s:Chromosome.random(length)
  let self.code = RandomString(a:length)
  let self.pivot = (a:length / 2) - 1
  return self

function! s:Chromosome.mutate(chance)
  if (RandomNumber(100) / 100.0) < a:chance
    let index = RandomNumber(1, strchars(self.code)) - 1
    let upOrDown = RandomNumber(100) <= 50 ? -1 : 1
    let exploded = split(self.code, '\zs')
    let change = nr2char(char2nr(exploded[index]) + upOrDown)
    if index == 0
      let self.code = change . join(exploded[index+1:], '')
      let self.code = join(exploded[0:index-1], '') . change . join(exploded[index+1:], '')
  return self

function! s:Chromosome.mate(chromosome)
  let child1 = strpart(self.code, 0, self.pivot) . strpart(a:chromosome.code, self.pivot)
  let child2 = strpart(a:chromosome.code, 0, self.pivot) . strpart(self.code, self.pivot)
  return [s:Chromosome.New(child1), s:Chromosome.New(child2)]

function! s:Chromosome.calcCost(compareTo)
  let total = 0
  let i = 0
  while i < strchars(self.code)
    let diff = char2nr(self.code[i]) - char2nr(a:compareTo[i])
    let total += diff * diff
    let i += 1
  let self.cost = total
  return self

function! s:Chromosome.to_s()
  return self.code . ' (' . string(self.cost) . ')'

let s:Population = {}

function! s:Population.New(goal, size)
  let population = copy(self)
  let population.members = []
  let population.goal = a:goal
  let population.generationNumber = 0
  let population.solved = 0

  let size = a:size
  let length = strchars(population.goal)
  while size > 0
    let chromosome = s:Chromosome.New()
    call chromosome.random(length)
    call add(population.members, chromosome)
    let size -= 1

  return population

function! s:Population.display()
  % delete
  call setline(1, "Generation: " . self.generationNumber)
  call setline(2, map(copy(self.members), 'v:val.to_s()'))
  return self

function! s:Population.costly(a, b)
  return float2nr(a:a.cost - a:b.cost)

function! s:Population.sort()
  call sort(self.members, self.costly, self)

function! s:Population.generation()
  call map(self.members, 'v:val.calcCost(self.goal)')

  call self.sort()
  call self.display()

  let children = self.members[0].mate(self.members[1])
  let self.members = extend(self.members[0:-3], children)

  let i = 0
  while i < len(self.members)
    call self.members[i].mutate(0.5)
    call self.members[i].calcCost(self.goal)
    if self.members[i].code == self.goal
      call self.sort()
      call self.display()
      let self.solved = 1
    let i += 1

  let self.generationNumber += 1

  return self

let population = s:Population.New('Hello, world!', 20)
while population.solved != 1
  call population.generation()

To see it run save it in a file and type   :so %   from within vim.

Why, dear bairui, you ask? Well, at this stage... I don't know. It just looked like fun. However, a couple of wild thoughts occurred to me: finding the ideal (good enough; as in 'correct' enough) combination of various vim options to achieve a desired look and behaviour. Take for example the various C indenting styles - what mad combination of &cinoptions, &cinkeys and &cinwords would you need to achieve Frankenstein's Indentation Style? What about getting &formatlistpat right for your preferred markup style? Sure, these might be totally hair-brained ideas -- but they might give you an idea for something less hairy and actually useful. Either way, I plan to keep playing with Burak's tutorial as he progresses through it. Thanks, Burak! :-)