The Nightstar Zoo

Nightstar IRC Network - irc.nightstar.net
It is currently Sun Apr 23, 2017 12:31 am

All times are UTC - 6 hours [ DST ]




Post new topic Reply to topic  [ 4 posts ] 
Author Message
PostPosted: Fri Dec 01, 2006 8:04 am 
Offline
Safari Exhibit
Safari Exhibit
User avatar

Joined: Mon Feb 07, 2005 3:48 am
Posts: 151
Location: Durban, South Africa
I have recently written a block of code to perform some limited validation on a series of DocBook XML files. Specifically, this does things like check for uniqueness of "id" attributes (which the tools I use only does within files, not between them), check for broken references, etc.

The chunk of code that prompts this post is the following:

Code:
# Get xml files to validate:
files = Dir.new('.').to_a.collect { |f|
    retval = nil  # Default: we don't want this file
    retval = f if f =~ /\.xml$/  # Actually, we do if it's xml
    excluded_files.each { |ef|
        retval = nil if f == ef
    }
    retval
}.compact


This got me thinking, there has to be an easier way. A little digging and consulting with the office Ruby expert led me to find_all, reject and partition. Thus, I could craft a cunning expression that returns either f or nil. While these will work in this case, consider a something like following, for which I can find no more elegant alternative:

Code:
# Perform some filtering and manipulation
def fixfile(file)
    return nil if not f =~ /\.xml$/
    return f if good_files.include? f
    return some_manipulation(f) if bad_files.include? f
end

files = Dir.new('.').to_a.collect { |f| fixfile(f) }.compact


The obvious solution would be to return early from a block, but this is not possible (return in a block is a LocalJumpError). The other obvious (but slightly less elegant) solution would be to pass a function instead of a block. but I don't think there is a way to do this.

Am I fighting the language rather than working with it here? Does Ruby have an idiom for this? Have I completely lost what little bits of my mind remain?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Dec 17, 2006 9:52 pm 
People often mistake collect for an iterator. In fact, it's a mapping: it takes an array a, and a function (block) f, and returns a new array b, such that:

b = [f(a[0]), f(a[1]), ..., f(a[n])]

A synonym for collect is 'map', reflecting the fact that ruby is influenced by both Lisp and Smalltalk.

Thinking of your approach as in inadequacy of blocks with respect to 'return' is wrongheaded. Break/continue/jump operations only make sense for iterators.

What you need is a combination of reject and collect:

files.reject {|f| f.invalid? }.collect { |f| f.transform }

If you only want to do one pass (if performance is critical), define a method collect_if on array. Using 'each', of course.


Top
  
 
 Post subject:
PostPosted: Mon Dec 18, 2006 2:35 am 
Offline
Safari Exhibit
Safari Exhibit
User avatar

Joined: Mon Feb 07, 2005 3:48 am
Posts: 151
Location: Durban, South Africa
Hmm, somehow the idea of adding a bunch of methods to Array to deal with fairly specific lists feels somewhat dirty. I'd rather subclass it and add appropriate methods to that, but most of the time it'd be a once-off filter.

Back to the map/filter thing. Perhaps I'm missing a deeper understanding of the idiom here, but I see a block as a lambda -- it should differ from a function only in not being bound to a name. Thus, early return would make sense. I want to bea able to say "I have finished processing for now, and this is my result" even if I haven't reached then end of the block yet.

I think this comes back to my general gripe about blocks having very different semantics from functions and functions not being first-class entities. Perhaps I'd be better off in Lisp...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 20, 2006 12:11 am 
Personally, I'd just use ?: to collapse the block contents to a single expression, as that's a bit more lambda-ish. It's how if works in Lisp/Scheme, anyway. (But then, as anyone who's read anything I've posted here can tell you, I don't know what I'm talking about. :| )


Top
  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 6 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group