Ruby quickies and useful idioms

There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too.

Array folding is a functional idiom in which each item is evaluated in
some way and its result accumulated, returning the final value of the
accumulator. It's simple in Ruby:

class Array
  def foldl(accum, &block)
    each {|value| accum = yield(accum, value)}
    return accum
  end
  def foldr(accum, &block)
    reverse.foldl(accum, &block)
  end
  alias fold :foldl
end

The canonical example is summing values:

r = (1..100).to_a
p r.foldl(0) {|a,v| a += v}

=> 5050

p r.foldr(5050) {|a,v| a -= v}

=> 0

A more interesting and useful application involves boolean logic. For
example, the following snippet ensures all arguments passed to
FooSet#new are of type Foo:

class Foo; end
class FooSet
  def initialize(*values)
    raise TypeError unless
      values.fold(true) {|a,v| a and v.is_a? Foo}
    # ...
  end
end

Another quickie I'm fond of assists in what Josh Bloch calls
``defensive copying'' in his book ``Effective Java.'' The idea is
that you should always return copies of mutable instance variables if
you aren't prepared to have users of your class modify those
variables.

For instance, if I have a class which contains an array, and I want to
make that array accessible from outside but not modifiable,
attr_reader won't be sufficient -- I need to call Array#dup and return
that. Once again, it's trivial in Ruby to define a helper to do just
that:

class Class
  def attr_defender(*symbols)
    symbols.each do |symbol|
      class_eval "def #{symbol}; @#{symbol}.dup; end"
    end
  end
end

Here's how I might use it:

class Container
  def initialize(*values)
    @values = values
    @length = values.length
  end
  attr_defender :values
  attr_reader :length
end

Container#values returns a new object each time:

c = Container.new('foo', 'bar', 'baz')
p c.values.__id__

=> 402800548

p c.values.__id__

=> 402800512

So now I can be lazy and do whatever I want with the copy.

Please share your quickies!

Sam

Hi Sam,

'foldl' is, I believe, the same as Enumeration#inject. Try checking
out the docs for that, but:

  (1..100).inject(0) { |acc,x| acc + x }

That "defensive attribute reader" was interesting; thanks.

For more of these, check out

   www.rubygarden.org/ruby?StandardClassExtentions

Some of those, and others besides, are coded up in the 'extensions'
project on RubyForge, for which I'll gratefully receive suggestions.

Cheers,
Gavin

···

On Monday, July 12, 2004, 8:33:51 AM, Sam wrote:

There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too. [.....]

Sam Stephenson <sstephenson@gmail.com> writes:

There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too.

[...]

Please share your quickies!

These threads make the util.rb files from my long-abandoned projects
feel useful again.

Here's my take on Lisp's PROG1:

def before_returning (value, &action)
   action.call
   return value
end

And an example:

class IdSupply
  def initialize
    @id = 0
  end

  def next
    before_returning @id do
      @id += 1
    end
  end
end

Here's a convenient abstraction of some uses of Array#each:

def each_call (id, things)
  things.each do |thing|
    self.send id, thing
  end
end

And an example:

things = [1, 2, 3, 4, 5, 6, 7, 8]
unwanted = [3, 6, 2, 1, 5]
things.each_call :delete, unwanted

Here's my take on prototype-based programming:

def add_method (id, &closure)
  (class << self; self; end).send(:define_method, id, &closure)
end

And an example:

foo = [1, 2, 3]
foo.add_method :frob do |x|
  self * self.length
end

# foo.frob 1 => 6

Here's some shuffling stuff:

class Array
  def shuffle!
    each_index do |j|
      i = rand(size - j)
      self[j], self[j+i] = self[j+i], self[j]
    end
    self
  end
  
  def shuffle
    self.clone.shuffle!
  end
end

And that's it.

  mikael

I've found this useful on several occasions

class Array
  include EnumerableOperator
  def all_pairs
    a = size-1
    for i,j in product(0..a, 0..a, proc {|i,j| i < j})
      yield at(i), at(j)
    end
  end
end

It depends on http://zem.novylen.net/ruby/fproduct.rb

Called all_pairs to distinguish it from the [1,2] [2,3] [3,4]... meaning
of each_pair

martin

···

Sam Stephenson <sstephenson@gmail.com> wrote:

There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too.

There's an easier way to do it:

  values.all? {|v| v.is_a? Foo}

Regards,

  Michael

···

On Mon, Jul 12, 2004 at 07:33:51AM +0900, Sam Stephenson wrote:

There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too.

Array folding is a functional idiom in which each item is evaluated in
some way and its result accumulated, returning the final value of the
accumulator. It's simple in Ruby:

> class Array
> def foldl(accum, &block)
> each {|value| accum = yield(accum, value)}
> return accum
> end
> def foldr(accum, &block)
> reverse.foldl(accum, &block)
> end
> alias fold :foldl
> end

The canonical example is summing values:

> r = (1..100).to_a
> p r.foldl(0) {|a,v| a += v}
=> 5050
> p r.foldr(5050) {|a,v| a -= v}
=> 0

A more interesting and useful application involves boolean logic. For
example, the following snippet ensures all arguments passed to
FooSet#new are of type Foo:

> class Foo; end
> class FooSet
> def initialize(*values)
> raise TypeError unless
> values.fold(true) {|a,v| a and v.is_a? Foo}
> # ...
> end
> end

I often find myself needing to instantiate a populated Hash, based on
the contents of two arrays, where the first contains what will become
the keys of the Hash and the second the values.

I use the following for this:

irb(main):001:0* foo = %w[ a b c ]
=> ["a", "b", "c"]
irb(main):002:0> bar = %w[ d e f ]
=> ["d", "e", "f"]
irb(main):003:0> baz = Hash[ *( foo.zip( bar ).flatten ) ]
=> {"a"=>"d", "b"=>"e", "c"=>"f"}

Nice, but I'd like it even more if ruby had a more direct and legible
way of achieving the same goal.

Ian

···

On Mon 12 Jul 2004 at 07:33:51 +0900, Sam Stephenson wrote:

There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too.

--
Ian Macdonald | Obstacles are what you see when you take
System Administrator | your eyes off your goal.
ian@caliban.org |
http://www.caliban.org |
                            >

Please share your quickies!

A complement to Array each_with_index that I find myself using quite often
when juggling with Arrays. Makes the code more legible.

class Array
  def collect_with_index
    r = Array.new(length)
    each_with_index do | o, i | r[i] = yield(o,i) end r
  end
end

greetings,

Brian

···

--
Brian Schröder
http://www.brian-schroeder.de/

Mikael Brockman <phubuh@phubuh.org> writes:

Here's my take on prototype-based programming:

> def add_method (id, &closure)
> (class << self; self; end).send(:define_method, id, &closure)
> end

And an example:

> foo = [1, 2, 3]
> foo.add_method :frob do |x|
> self * self.length
> end
>
> # foo.frob 1 => 6

I just took that verbatim from a utility file of mine, forgetting what
its actual purpose is.

The difference between Object#add_method and class << foo is that the
former takes a closure, so it can use variables from its lexical scope.

  mikael

the best shuffle I can think of is the Grossian shuffle (quoting
Flroain Gross (actually written with a different character than ss):

class Array
def shuffle
  sort_by {rand}
end
end

You may even found extensions.rubyforge.org interesting

···

il Mon, 12 Jul 2004 07:55:58 +0900, Mikael Brockman <phubuh@phubuh.org> ha scritto::

Here's some shuffling stuff:

class Array
  def shuffle!
    each_index do |j|
      i = rand(size - j)
      self[j], self[j+i] = self[j+i], self[j]
    end
    self
  end
  
  def shuffle
    self.clone.shuffle!
  end
end

And that's it.

'foldl' is, I believe, the same as Enumeration#inject. Try checking
out the docs for that, but:

  (1..100).inject(0) { |acc,x| acc + x }

Wow, I didn't know about Enumerable#inject. Semantically, it's not
quite the same as foldl: for instance, it doesn't make sense to foldl
(or foldr) a Hash, since you're not guaranteed any order. Practically
speaking, however, it's much nicer, since I can use it with Range.

As an aside: It would be nice to have a subclass of Enumerable that
guarantees item order; Array, Range, et al could mix it in.

For more of these, check out

   www.rubygarden.org/ruby?StandardClassExtentions

Some of those, and others besides, are coded up in the 'extensions'
project on RubyForge, for which I'll gratefully receive suggestions.

Cool, thanks!

Sam

···

On Mon, 12 Jul 2004 07:47:12 +0900, Gavin Sinclair <gsinclair@soyabean.com.au> wrote:

Ian Caliban wrote:

I often find myself needing to instantiate a populated Hash, based on
the contents of two arrays, where the first contains what will become
the keys of the Hash and the second the values.

Just curious: what sort of scenario necessitates that use?

If there were a nice easy way to do that in Ruby, what would you like it
to be called? i.e.

  a = [:x, :y, :z]
  b = [1, 2, 3]
  hash = # Fill me in!
  assert_equal({:x => 1, :y => 2, :z => 3}, hash)

Cheers,
Gavin

Trying to be too succinct can get you into trouble:

irb(main):001:0> a = [1, 2, 3]
=> [1, 2, 3]
irb(main):002:0> b = [[1], [2, 3], [4, 5]]
=> [[1], [2, 3], 3]
irb(main):003:0> Hash[ *( a.zip( b ).flatten ) ]
=> {1=>1, 2=>2, 3=>3, 4=>5}

Paul

···

On Tue, Jul 13, 2004 at 03:11:41PM +0900, Ian Macdonald wrote:

irb(main):001:0* foo = %w[ a b c ]
=> ["a", "b", "c"]
irb(main):002:0> bar = %w[ d e f ]
=> ["d", "e", "f"]
irb(main):003:0> baz = Hash[ *( foo.zip( bar ).flatten ) ]
=> {"a"=>"d", "b"=>"e", "c"=>"f"}

It's too early in the morning for me to decode this. Is this the same as my Array#each_unique_pair method documented here:

http://phrogz.net/RubyLibs/rdoc/classes/Array.html#M000061

If so, the implementation which doesn't require anything else is simply:

class Array
         def each_unique_pair
                 self.each_with_index{ |a,i|
                         self[(i+1)..-1].each{ |b| yield a,b }
                 }
         end
end

···

On Jul 12, 2004, at 1:47 AM, Martin DeMello wrote:

I've found this useful on several occasions

class Array
  include EnumerableOperator
  def all_pairs
    a = size-1
    for i,j in product(0..a, 0..a, proc {|i,j| i < j})
      yield at(i), at(j)
    end
  end
end

Hi --

···

On Wed, 14 Jul 2004, Brian Schroeder wrote:

> Please share your quickies!
>
>

A complement to Array each_with_index that I find myself using quite often
when juggling with Arrays. Makes the code more legible.

class Array
  def collect_with_index
    r = Array.new(length)
    each_with_index do | o, i | r[i] = yield(o,i) end r
  end
end

This is a perennial favorite :slight_smile: I still pull in my home-made
version fairly often.

David

--
David A. Black
dblack@wobblini.net

gabriele renzi <surrender_it@rc1.vip.ukl.yahoo.com> writes:

···

il Mon, 12 Jul 2004 07:55:58 +0900, Mikael Brockman > <phubuh@phubuh.org> ha scritto::

Here's some shuffling stuff:

class Array
  def shuffle!
    each_index do |j|
      i = rand(size - j)
      self[j], self[j+i] = self[j+i], self[j]
    end
    self
  end
  
  def shuffle
    self.clone.shuffle!
  end
end

And that's it.

the best shuffle I can think of is the Grossian shuffle (quoting
Flroain Gross (actually written with a different character than ss):

class Array
def shuffle
  sort_by {rand}
end
end

You may even found extensions.rubyforge.org interesting

It's very nifty indeed. I heard it was biased in some obscure way,
though.

  mikael

post an rcr about it or write your own module including Enumerable :slight_smile:

···

il Mon, 12 Jul 2004 08:15:13 +0900, Sam Stephenson <sstephenson@gmail.com> ha scritto::

As an aside: It would be nice to have a subclass of Enumerable that
guarantees item order; Array, Range, et al could mix it in.

My favourite 'practical' feature is that it tests for a seed value, and
defaults to foldl1 if none is given.

martin

···

Sam Stephenson <sstephenson@gmail.com> wrote:

Wow, I didn't know about Enumerable#inject. Semantically, it's not
quite the same as foldl: for instance, it doesn't make sense to foldl
(or foldr) a Hash, since you're not guaranteed any order. Practically
speaking, however, it's much nicer, since I can use it with Range.

"Sam Stephenson" <sstephenson@gmail.com> schrieb im Newsbeitrag
news:11fb0551040711161466c5309f@mail.gmail.com...

> 'foldl' is, I believe, the same as Enumeration#inject. Try checking
> out the docs for that, but:
>
> (1..100).inject(0) { |acc,x| acc + x }

Wow, I didn't know about Enumerable#inject. Semantically, it's not
quite the same as foldl: for instance, it doesn't make sense to foldl
(or foldr) a Hash, since you're not guaranteed any order.

That's not true. Since + is commutative it doesn't matter in which order
you add elements up. And there's a whole lot other algorithms that don't
depend on the order in which they are processed.

Even if you need an order you can do (stupid example):

sorted_keys = hash.sort.inject() {|keys, (key, val)| print key, " -> ",
val, "\n"; keys << key }

Practically
speaking, however, it's much nicer, since I can use it with Range.

As an aside: It would be nice to have a subclass of Enumerable that
guarantees item order; Array, Range, et al could mix it in.

Which order? Insertion order, natural order of the elements or an
arbitrary order?

Regards

    robert

···

On Mon, 12 Jul 2004 07:47:12 +0900, Gavin Sinclair > <gsinclair@soyabean.com.au> wrote:

A flatten_once would really be a useful method to have around.

martin

···

Paul Brannan <pbrannan@atdesk.com> wrote:

On Tue, Jul 13, 2004 at 03:11:41PM +0900, Ian Macdonald wrote:
> irb(main):001:0* foo = %w[ a b c ]
> => ["a", "b", "c"]
> irb(main):002:0> bar = %w[ d e f ]
> => ["d", "e", "f"]
> irb(main):003:0> baz = Hash[ *( foo.zip( bar ).flatten ) ]
> => {"a"=>"d", "b"=>"e", "c"=>"f"}

Trying to be too succinct can get you into trouble:

irb(main):001:0> a = [1, 2, 3]
=> [1, 2, 3]
irb(main):002:0> b = [[1], [2, 3], [4, 5]]
=> [[1], [2, 3], 3]
irb(main):003:0> Hash[ *( a.zip( b ).flatten ) ]
=> {1=>1, 2=>2, 3=>3, 4=>5}

Heh - so it is :slight_smile: Mine was basically the translation from the list
comprehension.

martin

···

Gavin Kistner <gavin@refinery.com> wrote:

On Jul 12, 2004, at 1:47 AM, Martin DeMello wrote:
> I've found this useful on several occasions
>
> class Array
> include EnumerableOperator
> def all_pairs
> a = size-1
> for i,j in product(0..a, 0..a, proc {|i,j| i < j})
> yield at(i), at(j)
> end
> end
> end

It's too early in the morning for me to decode this. Is this the same
as my Array#each_unique_pair method documented here:

Class: Array

If so, the implementation which doesn't require anything else is simply:

class Array
         def each_unique_pair
                 self.each_with_index{ |a,i|
                         self[(i+1)..-1].each{ |b| yield a,b }
                 }
         end
end