我对红宝石很新,但到目前为止非常享受它.有些事情给了我一些麻烦,以下也不例外.
我在这里要做的是通过对"Dir"进行子类化来创建一种"超级目录".我添加了一个名为'subdirs'的方法,用于列出目录对象的文件,如果文件本身是目录,则将它们推送到数组中.问题是,我的测试(File.directory?)的结果很奇怪 - 这是我的方法代码:
def subdirs subdirs = Array.new self.each do |x| puts "Evaluating file: #{x}" if File.directory?(x) puts "This file (#{x}) was considered a directory by File.directory?" subdirs.push(x) #yield(x) if block_given? end end return subdirs end
奇怪的是,即使我选择的目录中有很多目录("/ tmp") - 这个调用的结果只列出"." 和"......"
puts "Testing new Directory custom class: FileOps/DirClass" nd = Directory.new("/tmp") subs = nd.subdirs
结果如下:
Evaluating file: mapping-root Evaluating file: orbit-jvxml Evaluating file: custom-directory Evaluating file: keyring-9x4JhZ Evaluating file: orbit-root Evaluating file: . This file (.) was considered a directory by File.directory? Evaluating file: .gdmFDB11U Evaluating file: .X0-lock Evaluating file: hsperfdata_mishii Evaluating file: .X11-unix Evaluating file: .gdm_socket Evaluating file: .. This file (..) was considered a directory by File.directory? Evaluating file: .font-unix Evaluating file: .ICE-unix Evaluating file: ssh-eqOnXK2441 Evaluating file: vesystems-package Evaluating file: mapping-jvxml Evaluating file: hsperfdata_tomcat
Mark Westlin.. 17
你是从内部执行脚本/tmp
吗?我的猜测(我没有试过)是,File.directory?(x)
正在测试,看看是否有在当前目录下一个名为X的目录-所以,如果你从另一个目录中运行这个,你一定会找到.
和..
而不是其他目录.
尝试File.directory?(x)
改为File.directory?("#{path}/#{x}")
.
你是从内部执行脚本/tmp
吗?我的猜测(我没有试过)是,File.directory?(x)
正在测试,看看是否有在当前目录下一个名为X的目录-所以,如果你从另一个目录中运行这个,你一定会找到.
和..
而不是其他目录.
尝试File.directory?(x)
改为File.directory?("#{path}/#{x}")
.
马克韦斯特林已经回答了你当前的问题,但既然你提到你是Ruby的新手,这里有几个其他风格的建议:
def subdirs subdirs = Array.new
通常最好在可能的情况下使用文字语法,因此表达这一点的常用方法是subdirs = []
.
self.each do |x|
self
是Ruby中的隐式接收器,所以你可以把它关掉.(毕竟这不是Python.)然而,代码的主要目的是通信,所以如果你相信,这会更好地传达你的意图,留下它.
说到沟通:x
除非你在谈论笛卡尔坐标系中的点,否则它不是一个非常交流的名字.怎么样file
,或者如果你对Unix目标也是文件的概念感到不舒服,那就更中立了entry
?(实际上,最好的可能是path
,但我们很快就会看到这可能会让人感到困惑.)
我的第三个建议实际上是个人的偏好是与一般的Ruby风格:常见的Ruby风格决定了一个行块分隔与{
/ }
和多块分隔的do
/ end
,就像你一样.我不喜欢这样,因为它是一种任意的区别,没有任何意义.(还记得"沟通"的事情吗?)所以,我实际上做的事情是不同的:如果块本质上是命令性的,例如,改变一些全局状态,我使用关键字(因为块实际上是do
一些工作),如果块本质上是功能性的,只返回一个新对象,我更喜欢使用大括号(因为它们看起来很好数学).所以,在这种情况下,我会使用大括号.
if File.directory?(x)
正如Mark已经解释的那样,你需要做类似这样的File.directory?(File.join(path, entry))
事情,其中Dir#path
一个Dir
类的公共属性返回Dir.new
初始化的路径.
在这里你还看到,为什么我们没有使用path
块参数的名称.否则我们需要写File.directory?(File.join(self.path, path))
.
subdirs.push(x)
将元素附加到数组,或者确实在Ruby中附加任何东西的规范方法是<<
运算符.所以,这应该读subdirs << entry
.
Array#push
是别名Array#<<
,主要用于让您使用Array
堆栈(与之结合使用Array#pop
).
end end return subdirs
这是我的个人风格和常见的Ruby风格之间的另一个脱节:在Ruby中,如果没有显式return
,返回值只是最后一个表达式的值.这意味着,您可以省略return
关键字和常见的Ruby样式.但是,我更喜欢使用类似于块分隔符return
的方法:用于本质上是功能性的方法(因为它们实际上"返回"一个值)而不return
用于命令式方法(因为它们的实际返回值不是return
关键字后面的内容),但他们对环境有什么副作用).所以,就像你一样,我会return
在这里使用一个关键字,尽管有共同的风格.
习惯上,通过空行将方法体的其余部分的返回值分开.(顺便说一句,设置代码也一样.)
end
所以,这就是我们现在所处的位置:
def subdirs subdirs = [] each { |entry| if File.directory?(File.join(path, entry)) subdirs << entry end } return subdirs end
正如您所看到的,if
表达式实际上只用于跳过循环的一次迭代.使用next
关键字可以更好地传达这一点:
def subdirs subdirs = [] each { |entry| next unless File.directory?(File.join(path, entry)) subdirs << entry } return subdirs end
如您所见,我们设法从块结构中删除了一级嵌套,这总是一个好兆头.
这个成语被称为"保护条款",在函数式编程中非常流行,其中许多语言甚至都内置了保护结构,但它在地球上几乎任何其他语言都被大量使用,因为它极大地简化了控制流程:这个想法类似于城堡外面的警卫:不是让坏人进入城堡(方法,程序,功能,阻挡......),然后痛苦地试图追踪他们的一举一动,不断地害怕错过一些东西或丢失它们,你只需在你的城堡入口(你的方法,块,......的开头)张贴一个警卫,他们不会让他们开始(跳到程序的最后) ,从方法返回,跳过循环的一次迭代,...).在Ruby中,你可以使用raise
,return
,next
和break
这一点.在其他语言中,即使GOTO
是好的(这是极少数情况之一,GOTO
实际上可以清除控制流程).
但是,通过识别迭代器模式,我们可以进一步简化这一过程:您获取一个列表(目录条目),然后将该列表"压缩"到一个对象(subdirs
数组).对于一个类别理论家来说,这就是"catamorphism",一个硬核函数程序员" fold
",一个软核函数程序员" reduce
",一个Smalltalk程序员" inject:into:
"和一个Rubyist" Enumerable#inject
":
def subdirs return inject([]) { |subdirs, entry| next subdirs unless File.directory?(File.join(path, entry)) subdirs << entry } end
inject
使用前一次迭代的返回值来播种下一个迭代值,这就是为什么我们必须返回subdirs
,即使我们正在跳过一个迭代,也可以使用next subdirs
而不是plain next
(它将返回nil
,以便在下一次迭代时subdirs
将会nil
并且subdirs << entry
会提高a NoMethodError
.)
(注意我使用花括号作为块,即使块实际上修改了它的参数.我觉得这仍然是一个"功能"块.YMMV.)
我们能做的最后一件事就是要认识到我们正在做的只是过滤(或换句话说"选择")数组的元素.而Ruby已经内置了一个方法,它正是如此:Enumerable#select
.见证使用Ruby的全部功能生成的单行精彩:
def subdirs return select { |entry| File.directory?(File.join(path, entry)) } end
作为一般提示:学习奇迹Enumerable
.它是Ruby编程的主力,类似于IEnumerable
.NET,dict
Python中的函数式语言或PHP和Perl中的关联数组.