hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Thin Strand of Search-and-Replace Rails #

by why in inspect

If you’re playing with Rails and you want to better understand Ruby blocks, as well as method chaining, Matthew Bohnsack has a great one-liner, which boils to:

  Article.find_all.each { |a| a.body.gsub! \
    /src="\/(.*.(jpg|gif|png))/, 'src=\"http://bohnsack.com\1';
    a.save }

More like three or four lines, but still. He breaks it up, explains each part, with short paragraph-long lessons. I like how code like the above has the feeling of a sed recipe, but isn’t utterly cryptic.

said on 17 Aug 2005 at 05:06

For me, the lack of parentheses in the gsub! call makes this harder to parse.

I prefer to drop parentheses when:
  • there’s no or only one argument
  • I want to pretend that it’s not a method call (when using some DSL ).
I also dislike things like
def somemeth bla, blerg, foo = 1, *bar

Anyway this is not a particularly enticing example of method chaining, is it?

said on 17 Aug 2005 at 05:54
That looks like some ancient AR code ;) I rather like (newlines inserted to fit in the box):

Article.find(:all).each {|a| 
  a.update_attribute(:body, a.body.gsub())
}
Or if you were doing some operation you weren’t sure would be a success:

Article.find(:all).select {|a| 
  a unless a.update_attribute(:foo, 'bar')
}
Which would hand you any articles where the update failed.
said on 17 Aug 2005 at 06:04

Woops, the ‘a unless’ part is not necessary. I should not reply before I’ve had my coffee :(

said on 17 Aug 2005 at 08:44

Thanks for the linkage and the suggestions – update_attribute seems the way to go.

I was just geeked that I got it to work, not really knowing what I was doing. The statement I created was mostly a result of bouncing on the tab key in irb, picking methods that looked good, looking at their inspect output, and then repeating a few times until everything worked.

This seems an important part of the reason why Ruby and the Rails framework are so powerful. Lots of stuff works like you intuitively expect it to.

said on 17 Aug 2005 at 14:10

If someone wants to explain yet another nifty piece of Ruby code: Quicksort by Tony Hoare at www.approximity.com/rubybuch2/node8_main.html . (I still cannot completely make out the data flow in this script, esp. the nested sorting issue, e.g. how & why [-1, -3.4, ...] would become [-3.4, -1, ...])

said on 17 Aug 2005 at 14:34

def quicksort( xs ) return xs if xs.size <= 1 m = xs0 # Split-Element quicksort(xs.select { |i| i < m } ) + xs.select { |i| i == m } + quicksort(xs.select { |i| i > m } ) end

line by line:


  return xs if xs.size <= 1

If the size is 1 or 0, we just return the array; it’s already sorted


  m = xs[0]

Here we choose an arbitrary “pivot” element. This can be any element of the array. In this case we choose the first.


  quicksort(xs.select { |i| i < m } ) +
     xs.select { |i| i == m } +
     quicksort(xs.select{ |i| i > m } )

This is a little more complicated. First, let’s figure out what those selects do. The first one selects all elements less than the pivot. The second selects all elements equal to the pivot. The final one selects all elements greater than the pivot. Let’s modify this and use some temp variables.


  less = xs.select { |i| i < m }
  equal = xs.select { |i| i == m }
  more = xs.select { |i| i > m }

  quicksort( less ) + 
    equal + 
    quicksort( more ) )

In other words, we’re returning a list of all the elements less than the pivot, sorted, then all those equal, then all those greater, sorted. This is classic divide-and-conquer recursion. Quicksort is applied to ever smaller lists until we use it on lists of one or zero elements, and we hit the terminating condition.

OK, I’m not sure if that helped at all :)

said on 17 Aug 2005 at 14:36

Oops, messed up the formatting on that first bit.


def quicksort( xs )
   return xs if xs.size <= 1
   m = xs[0] # Split-Element
   quicksort(xs.select { |i| i < m } ) +
     xs.select { |i| i == m } +
     quicksort(xs.select { |i| i > m } )
end

said on 17 Aug 2005 at 14:52

Wow, thanks Matt for this ultra quick response! Great going!

said on 18 Aug 2005 at 01:23

Hehe, it’s funny how this definition of quicksort is a direct translation of how one would write it in functional languages like Haskell. It’s one of the strengths of Ruby that it supports all programming styles (structured, object-oriented, functional, you name it) without ever having to resort to ugly hacks :) .

said on 18 Aug 2005 at 05:19

To get a handle on the data flow you also can insert: puts i < m etc. or just after def quicksort(xs): puts xs.size; print xs; print ”\n”.

said on 19 Aug 2005 at 01:00

The internal sorting of the array of numbers contained in the less and more temp variables is accomplished by the nested quicksort(xs.select…)functions. Each of these functions has the return-if condition but sets its own m=xs(0) pivot element! (The functions can be called nested because they are enclosed in def quicksort(xs)...end)

Comments are closed for this entry.