Python Slices vs Ruby blocks
Product-driven, polyglot software engineer. I've spent over two decades working at every stage of the software development life-cycle, in a variety of roles and using a plethora of languages and platforms. My software interests include Ruby, Elixir, relational databases, Behavior-Driven Development, requirements management and agile methods.
As well as an avid reader of anything on software development, quantum mechanics and Iain M.Banks novels, I love writing blog posts and speaking at conferences, having done so at several national and international software-related conferences. I have published two books: "Managing Software Requirements the Agile Way", came out of a need to improve some of the entrenched practices in managing requirements in the agile world. "The Professional ScrumMaster Guide" provides guidance on getting certified and applying Scrum pragmatically.
A couple of my Python colleagues tried to impress me today with Python's named slices feature. The way it works is like that:
s = list('helloworld!')
=> ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!']
WORLD = slice(5, 10)
s[WORLD]
=> ['w', 'o', 'r', 'l', 'd']
So you can have your own customised slicing mechanism that you can apply to any list. Which is kind of cool. That prompted me to demonstrate how we can do the same thing with Ruby. Luckily, in Ruby world we have blocks, procs and lambdas, the ultimate play-dough that allows us to stretch and flex in every direction.
s = ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!']
=> ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!']
world = proc {|x | x.slice(5, 10)}
world[s]
=> ["w", "o", "r", "l", "d", "!"]
so we can do things like:
first = proc { |x| x.slice(0) }
first[s]
=> "h"
or even things that we can't do with Python's named slicing, since it doesn't allow us to pass the receiver as an argument to the block (x in the example below)
last = proc {|x| x.slice x.length-1, x.length}
last[%w(dog rabbit fox cat)]
=> ["cat"]
or
median = proc {|x| x.slice(x.length / 2) }
median[%w(dog rabbit cat)]
=> "rabbit"
and of course we're not just restricted to slicing arrays,
domain_extractor = proc { |x| x.gsub(/.+@([^.]+).+/, '\1') }
domain_extractor["fred@mydomain.co.uk"]
=> "mydomain"
and since a block is just an anonymous Proc object, we can use it with any method that accepts Proc parameters
email_list = ["fred@mydomain.com", "john@gmail.com", "mary@yahoo.co.uk"]
=> ["fred@mydomain.com", "john@gmail.com", "mary@yahoo.co.uk"]
email_list.map(&domain_extractor)
=> ["mydomain", "gmail", "yahoo"]
Blocks and Procs (a.k.a lambdas) are ideal for some quick, reusable functionality or for when defining a full-blown method would be too much, but also for more serious uses such as callbacks and deferred execution. IMHO, they are a fundamental part of what makes Ruby such a flexible and powerful language. Learn them, use them, enjoy them :)


