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(p) @files << finf
elsif p.directory?
dinf = DirInfo.new(p)
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?
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(p) @files << finf
elsif p.directory?
dinf = DirInfo.new(p)
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?
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 Thu, 18 Oct 2007 02:40:09 +0900, Luis <lcrespom@gmail.com> wrote:
def scan @path.each_entry do |p|
next if p.to_s == "." || p.to_s == ".."
p = @path + p
if p.file?
finf = FileInfo.new(p) @files << finf
elsif p.directory?
dinf = DirInfo.new(p)
dinf.scan # ==> Recursive search of subdirs
end
end
yield @path, @files
end
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
···
On 17.10.2007 19:36, Luis wrote:
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(p) @files << finf
elsif p.directory?
dinf = DirInfo.new(p)
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?
def scan(&block)
# do stuff
scan &block # <== recursive call with block
yield
end
I think that'll work.
···
On Oct 17, 12:36 pm, Luis <lcres...@gmail.com> wrote:
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(p) @files << finf
elsif p.directory?
dinf = DirInfo.new(p)
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?
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.
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
···
2007/10/18, Luis <lcrespom@gmail.com>:
> 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.
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.
···
On Oct 18, 11:22 am, "Robert Klemme" <shortcut...@googlemail.com> wrote:
2007/10/18, Luis <lcres...@gmail.com>:
> > 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.
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