Sunday, December 16, 2012

call() for a Good Time

Simple functions in Vim are declared like this:

function! A(a, b, c)
  echo a:a a:b a:c
endfunction

call A(1, 2, 3)

There’s probably nothing surprising there except for the a:a syntax, which is how Vim insists on accessing the function’s arguments (mnemonic: a: for argument).
Just as simple is calling function A() from another function, B(), passing its arguments directly along to A():

function! B(a, b, c)
  return A(a:a, a:b, a:c)
endfunction

call B(1, 2, 3)

Nothing surprising there at all. But we’ve just laid the groundwork for the main attraction tonight. In VimL, you can call a function using the library function call(func, arglist) where arglist is a list. If you’re calling a function that takes multiple arguments, collect them in an actual list like this:

function! C(a, b, c)
  return call("A", [a:a, a:b, a:c])
endfunction

call C(1, 2, 3)

If you already have the elements in a list, no need to wrap it in an explicit list:

function! D(a)
  return call("A", a:a)
endfunction

call D([1, 2, 3])

Let’s step it up a notch. What if you want to be able to accept the args as either separate arguments or as a list? Vim has your back with variadic functions cloaked in a syntax similar to C’s:

Variadics in the key of V:
  • a:0 is a count of the variadic arguments
  • a:000 is all of the variadic arguments in a single list
  • a:1 to a:20 are positional accessors to the variadic arguments


So now it doesn’t matter how we receive the arguments — standalone or in a list — we can keep Vim happy and call A() appropriately.

function! E(...)
  if a:0 == 1
    return call("A", a:1)
  else
    return call("A", a:000)
  endif
endfunction

call E(1, 2, 3)
call E([1, 2, 3])

Ok. That’s not too bad; it’s perhaps a little awkward. We’re calling A() directly here, but it shouldn’t be a surprise to see that we can call C() in the same way too:

function! F(...)
  if a:0 == 1
    return call("C", a:1)
  else
    return call("C", a:000)
  endif
endfunction

call F(1, 2, 3)
call F([1, 2, 3])

Pretty straightforward. What about calling D() instead which expects a single list argument? Hmm… if Vim wants a list, give him a list:

function! G(...)
  if a:0 == 1
    return call("D", [a:1])
  else
    return call("D", [a:000])
  endif
endfunction

call G(1, 2, 3)
call G([1, 2, 3])

It’s worth stopping briefly here to consider what call() is doing to that arglist: It’s splatting it (extracting the arguments and passing them as separate members to the called function). Nice. Wouldn’t it be nice if we could splat lists ourselves? Well, be envious of Ruby coders no more because we can splat lists in VimL!

Splat!
To splat a list into separate variables (a, b and c here):
let [a, b, c] = somelist
Read :help :let-unpack for the juicy extras.

I like the splatting approach because it gives us variable names to play with inside our function:

function! H(...)
  if a:0 == 1
    let [a, b, c] = a:1
  else
    let [a, b, c] = a:000
  endif
  return D([a, b, c])
endfunction

call H(1, 2, 3)
call H([1, 2, 3])

Of course, it works just as well for calling functions with explicit multiple arguments, like C():

function! I(...)
  if a:0 == 1
    let [a, b, c] = a:1
  else
    let [a, b, c] = a:000
  endif
  return C(a, b, c)
endfunction

call I(1, 2, 3)
call I([1, 2, 3])

You’ll notice that the splat semantics are identical between H() and I() and only the call of D() and C() change, respectively. This is very neat, I think.
So far we’ve been calling through to functions that call A() directly. Happily, we can call through to one of these dynamic functions (like E(), but any would work as well) and have it Just Work too:

function! J(...)
  if a:0 == 1
    let [a, b, c] = a:1
  else
    let [a, b, c] = a:000
  endif
  return E(a, b, c)
endfunction

call J(1, 2, 3)
call J([1, 2, 3])

So, that’s it. Vim has variadic functions and splats. And splats are my recommended pattern for handling deep call chains between variadic functions.
There’s one last, cute, little thing about splats: you can collect a certain number of explicit arguments as you require, and then have any remaining arguments dumped into a list for you. The rest variable here will be a list containing [4, 5, 6] from the subsequent calls:

function! K(...)
  if a:0 == 1
    let [a, b, c; rest] = a:1
  else
    let [a, b, c; rest] = a:000
  endif
  echo "rest: " . string(rest)
  return E(a, b, c)
endfunction

call K([1, 2, 3, 4, 5, 6])
call K(1, 2, 3, 4, 5, 6)

And I thought this was going to be a short post when I started. I almost didn’t bother posting it because of that reason.

Saturday, December 1, 2012

Rapid Programming Language Prototypes with Ruby & Racc, Commentary

I just watched a tolerable ruby conference video by Tom Lee on Rapid Programming Language Prototypes with Ruby & Racc.

What he showed he showed fairly well. His decision to "introduce compiler theory" was, he admitted, last-minute and the hesitation in its delivery bore testimony to that. The demonstration of the compiler pipeline using his intended tools (ruby and racc) was done quite well with a natural progression through the dependent concepts along the way. By the end of the talk he has a functional compiler construction tool chain going from EBNF-ish grammar through to generated (and using gcc, compiled) C code.

I was surprised that nobody in the audience asked the question I was burning to ask from half way through the live-coding session: Why not use Treetop? (or the more generic: why not use a peg parser generator or a parser generator that does more of the heavy lifting for you?)

The whole point of Tom's presentation is: use ruby+racc because it saves you from all the headaches of setting up the equivalent tool chain in C/C++. And it does, he's right. But it feels to me that Treetop does even more of that hard work for you, allowing you to more quickly get to the fun part of actually building your new language. I'm angling for simplicity here.

I could be wrong, though, so let me ask it here (as Confreaks seems to not allow comments): Why not treetop (or an equally 'simple' parser generator) for something like this? (and answers along the lines of EBNF > PEG are not really what I'm after, but if you have a concrete example of that I'd like to hear it too.)

On a completely separate note: Tom, you need to add some flying love to your Vim habits. :-)

Thursday, November 8, 2012

Vim Motions

One of the more frequent admonishments delivered on #vim to the whining novice or the curious journeyman is to master the many motions within the editor. Previously, a bewildering list of punctuation and jumbled letters was unceremoniously dumped on the complainant with the misguided expectation that they'd then take themselves off and get right to the task of memorising the eighty odd glyphs. We mistook their silence for compliance but I rather suspect it was more bewilderment or repulsion or sheer paralysis. In an attempt to friendly that mess up, I have started an infographic series intended to cover the twelve major categories, probably spread over six separate infographics.

The Vim Motions Infographic Series (in 9 parts):

1. Line & Buffer
2. Column
3. Word
4. Find
5. Search
6. Large Objects
7. Marks, Matches & Folds
8. Text Objects (not motions, but mesh nicely at this point)
9. Creating your own Text Objects

I plan to have a different expression on the chibi's face in each of the pages. I'll move the crying one from the Large Object page (as shown below) to page 1 and then progressively improve her mood through the remaining pages: something like -- crying, disappointment, resignation, hope, amazement, happiness, confidence, smugness and something devilish. As an update on that, I have inked five of the chibis now. I look forward to having them all up in their own infographics.

I decided to have the background colour change to suit the mood of the chibi, starting from black in image number one to represent depression and despair. I will roughly follow the same colour spread I used on the How Do I Feel graphic.

I have no experience in putting together a multi-page piece like this. Feedback certainly welcome. I was vaguely thinking of having it a bit like a magazine or comic book spread, but I don't know how to do that or whether it's the right or even a good approach.


Legend:
Green indicates cursor origin before issuing the motion.
Red indicates cursor destination at the end of the motion.
Orange shows the area covered by the motion. This would be the same area highlighted in Vim if a visual operator was used with these motions.

1. Line & Buffer Motions


2. Column Motions


 
6. Large Object Motions




The Many Faces of % in Vim

Pity the poor Vimmer for he has so many a face to put to percent:

Help Topic Description
N% go to {count} percentage in the file
% match corresponding [({})] (enhanced with matchit.vim plugin)
g% enhanced match with matchit.vim plugin — cycle backwards through matches
:% as a range, equal to :1,$ (whole file)
:_% used as an argument to an :ex command as the name of the current file
"% as a register, the name of the current file
expr-% in VimL as modulo operator
expand(), printf() and bufname() in VimL use % in printf-like format specifiers
'grepformat', 'errorformat', 'shellredir', 'printheader' and 'statusline' various options use % as a printf-like format specifier
Regular Expression Atoms:
Match locations:
\%# cursor position
\%' position of a mark
\%l specific line
\%c specific column
\%v specific virtual column
\%( non-backref capturing group
\%[ sequence of optionally matched atoms
Numeric character specifier in matches:
\%d decimal
\%o octal
\%x hex (2 digits)
\%u hex (4 digits)
\%U hex (8 digits)
Absolute file or string boundaries:
\%^ start of file (or start of string)
\%$ end of file (or end of string)
\%V match inside visual area

Thursday, October 11, 2012

A Little Drop of Prudence

I like my Vim with a little drop of prudence

I frequently create temporary macros or maps for ad hoc edits when I find myself having to do the same job more than a few times over. This is a good thing to spend some time reflecting on in your editing. If you’re in the heat of the moment and don’t want to break concentration or waste time on R&D right now, make a note to come back when you have time to look at your current editing inefficiency. I recommend setting up a practise file (something I mention in learnvim) which you can quickly jump to using a global bookmark.

Setting up a Practise File
  1. :e ~/vim-practise.txt
  2. mP
Note This sets a global mark that can be jumped to from anywhere within vim using the normal mode ' command. (:help 'A)
Jumping to your Practise File
  • 'P

So, there you are editing away on another dreary Tuesday and in a moment of lucidity you realise you’ve just mashed the same key pattern a dozen times over — you’ve just discovered an inefficiency! Awesome.

Quick check: “Do I have time to investigate and optimise this now?”

No: :-( Sucks to be you. Quick! To the Practise File! Make a quick note about this so that you can come back to it on your morning tea break.

Yes: :-) You soldier! Yank a sample snippet of the problem at hand and then… Quick! To the Practise File! Paste in your snippet and start experimenting with ways to optimise the necessary changes. Is there a map or macro you can make to wrap these steps up into a fast and simple solution? Take your macro/map back to the real work you were doing before this R&D diversion and finish the rest of those lame edits with the genuine vim you should be applying to life.

“But couldn’t I have just experimented in my original file?”

Sure… but then you lose the problem. You’re left with just a finished solution. A useless page of sterile, problemless text. That might please your boss and clients, but it’s just no good for your continued development as a vimmer. You’ll grow more by squirreling away interesting little nasties like this that you find in the wild so that you can revisit them during quieter moments as part of your Deliberate Practise regimen.

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
  endif

  return chromosome
endfunction

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

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:], '')
    else
      let self.code = join(exploded[0:index-1], '') . change . join(exploded[index+1:], '')
    endif
  endif
  return self
endfunction

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)]
endfunction

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
  endwhile
  let self.cost = total
  return self
endfunction

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


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
  endwhile

  return population
endfunction

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

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

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

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
      break
    endif
    let i += 1
  endwhile

  let self.generationNumber += 1

  return self
endfunction

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

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! :-)