How to combine blocks and recursiveness

Hello,

I am trying to code a method that accepts a block… easy. The problem
is that this method is recursive, so I don’t know how to pass the
block through the recursive calls.

The specific code is about recurising through a directory tree while
calling the provided code block once per each directory, passing the
list of files in that directory. This is the code:

class DirInfo

constructor, etc. removed for clarity

def scan
@path.each_entry do |p|
next if p.to_s == “.” || p.to_s == “…”
p = @path + p
if p.file?
finf = FileInfo.new§
@files << finf
elsif p.directory?
dinf = DirInfo.new§
dinf.scan # ==> Recursive search of subdirs
end
end
yield @path, @files
end

end

So if I do:

di = DirInfo.new("/devtools/ruby")
di.scan do |path, files|
puts “Directory #{path} has #{files.length} files”
end

I expect to get my block called once per each subdirectory, passing
the subdir path and list of files in that subdir. The problem is that
the fisrt time the call scan is made from within scan, no block is
given, so I get a “no block given” exception and the program stops.

I know there are other ways to scan directories, but please take it
just as an example. Still the question is, how to combine
recursiveness with blocks?

Thanks in advance,

Luis.

You can get a hold of the block as an argument:

def scan(&block)

dinf.scan(&block)

block.call(@path, @files)
end

Jason

On Thu, 18 Oct 2007 02:40:09 +0900, Luis [email protected] wrote:

  end
end
yield @path, @files

end

Another poster mentioned the &block thing, which is probably preferable.

Note that you could also simply do:

dinf.scan { |p,f| yield p,f }

The advantage to this is that (for shallow recursion), there is less
overhead than creating a Proc object for the block. The big
disadvantage
is that you add an extra layer of yield-ing for each level recursion.

-mental

On 17.10.2007 19:36, Luis wrote:

class DirInfo
elsif p.directory?
So if I do:

I know there are other ways to scan directories, but please take it
just as an example. Still the question is, how to combine
recursiveness with blocks?

The typical (and IIRC most efficient) idiom is to pass on the block
parameter:

def foo(&b)
foo(&b)
end

Another remark: it seems you are reinventing Find.find(). Why do you do
that?

Kind regards

robert

The typical (and IIRC most efficient) idiom is to pass on the block
parameter:

def foo(&b)
foo(&b)
end

That looks nice and simple.
Thanks!

Another remark: it seems you are reinventing Find.find(). Why do you do
that?

As I said, the dir scanning was just an example of recursiveness.
Still, I do need to scan a directory in a way that I get an array of
files per each block call, and not one file at a time, because I want
to compare the contents of a directory with other directory, in order
to identify changed files, new files, deleted files, etc.

Regards,

Luis.

On Oct 17, 12:36 pm, Luis [email protected] wrote:

class DirInfo
elsif p.directory?

I know there are other ways to scan directories, but please take it
just as an example. Still the question is, how to combine
recursiveness with blocks?

Thanks in advance,

Luis.

You can make the block argument explicit:

def scan(&block)

do stuff

scan &block # <== recursive call with block
yield
end

I think that’ll work.

2007/10/18, Luis [email protected]:

to identify changed files, new files, deleted files, etc.
You can still build that with Find.find() which saves you the traversal
code:

#!ruby

require ‘find’
require ‘pp’

ARGV.each do |dir|
list = Hash.new {|h,k| h[k] = []}

Find.find dir do |f|
d, b = File.split f
next if /^..?$/ =~ b
list[d] << b
end

pp list
end

Cheers

robert

On Oct 18, 11:22 am, “Robert K.” [email protected]
wrote:

to identify changed files, new files, deleted files, etc.

robert

Cool! The code is much more advanced compared with mine… yes, I am a
Ruby beginner.
Still, if I understood your code well, it creates a whole directory
tree in memory, which, if applied to “/” (or “c:”) it can really take
a huge amount of RAM.

I want to process the list of files in each directory, one directory
at a time, and then forget about these files once they are processed.
However I guess I can modify your code above a bit, remove the hash,
check that “d” is the same as the previous pass, and if not yield the
list, etc.

Regards,

Luis.

Am 18 Oct 2007 um 18:22 hat Robert K. geschrieben:

#!ruby

require ‘find’
require ‘pp’

ARGV.each do |dir|
list = Hash.new {|h,k| h[k] = []}

Find.find dir do |f|

For Windows it might be nicer to add
f= f.gsub(/\/,’/’)
so you don’t have a mixture of ‘’ and ‘/’ as separators.

d, b = File.split f
next if /^\.\.?$/ =~ b

Find:find does not return ‘.’ or ‘…’ like Dir:entries does for example
as Find.find is implicitely recursive.
So this line can be omitted.

list[d] << b

end

pp list
end

Have fun!
Dirk

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs