在没有if … else块的情况下处理Ruby中的ARGV
在一篇关于无条件编程的博客文章中,Michael Feathers展示了如何限制if
语句作为降低代码复杂性的工具。
他用一个具体的例子来说明他的观点。 现在,我一直在考虑其他具体的例子,可以帮助我更多地了解无条件/ if
更少/更少的编程。
例如,在这个cat clone中有一个if..else
块:
#!/usr/bin/env ruby if ARGV.length > 0 ARGV.each do |f| puts File.read(f) end else puts STDIN.read end
事实certificateruby有ARGF
,这使得这个程序更加简单:
#!/usr/bin/env ruby puts ARGF.read
我想知道ARGF
是否不存在如何重构上面的例子所以没有if..else
块?
也对其他说明性具体示例的链接感兴趣。
你不能总是摆脱有条件的(可能有疯狂的课程数量),迈克尔·费瑟斯并不是在鼓吹这种情况。 相反,它有点抵制过度使用条件。 我们都看到了噩梦代码,这是嵌套if / elsif / else的无穷无尽的链条,所以他也是如此。
此外,人们常常在条件限制内嵌入条件。 我见过的一些最糟糕的代码是嵌套条件的巨大噩梦,其中散布着奇怪的工作。 我认为控制结构的真正问题在于它们经常与工作混合在一起。 我确信我们可以通过某种方式将此视为违反单一责任的一种forms。
您可以通过首先从ARGV创建IO对象数组来简化代码,而不是盲目地尝试消除这种情况,如果该列表为空,则使用STDIN。
io = ARGV.map { |f| File.new(f) }; io = [STDIN] if !io.length;
然后你的代码可以用io
做它喜欢的事情。
虽然它具有严格相同数量的条件,但它消除了if / else块并因此消除了分支:代码是线性的。 更重要的是,由于它将收集数据与使用它分开,您可以将其放入函数中并重复使用它,从而进一步降低复杂性。 一旦它处于function中,我们就可以利用早期的回报。
# I don't have a really good name for this, but it's a # common enough idiom. Perl provides the same feature as <> def arg_files return ARGV.map { |f| File.new(f) } if ARGV.length; return [STDIN]; end
现在它在一个函数中,你捕获所有文件或stdin的代码变得非常简单。
arg_files.each { |f| puts f.read }
从技术上讲,你可以,
inputs = { ARGV => ARGV.map { |f| File.open(f) }, [] => [STDIN] }[ARGV] inputs.map(&:read).map(&method(:puts))
虽然这是代码高尔夫,而且太聪明了。
不过,它是如何运作的?
- 它使用哈希来存储两个备选方案。
- 将
ARGV
映射到打开文件的数组 - 将
[]
映射到带有STDIN
的数组,如果它是空的,则有效地覆盖ARGV
条目 - 在散列中访问
ARGV
,如果它为空则返回[STDIN]
- 读取所有打开的输入并打印它们
不要写那段代码。
正如我在回答你的另一个问题中所提到的那样,无条件编程并不是要不惜一切代价避免表达,而是要追求可读性和意图揭示代码。 有时这只意味着使用if
表达式。
首先,虽然原则很好,但您必须考虑其他更重要的事情,例如可读性和执行速度。
也就是说,你可以monkeypatch String类来添加一个read方法,并将STDIN和参数放在一个数组中,并从头开始读取,直到数组的末尾减1,所以如果有参数则停在STDIN之前,然后继续-1(结束)如果没有参数。
class String def read File.read self if File.exist? self end end puts [*ARGV, STDIN][0..ARGV.length-1].map{|a| a.read}
在有人注意到我仍然使用if来检查文件是否存在之前,你应该在你的例子中使用两个if来检查它,如果你没有,请使用rescue来正确地通知用户。
编辑:如果您将使用该补丁,请阅读这些链接中可能存在的问题http://blog.jayfields.com/2008/04/alternatives-for-redefining-methods.html http://www.justinweiss.com/文章/ 3的方式对猴子补丁,而无需制作-A-一塌糊涂/
由于read方法不是String的一部分,所以使用alias和super的解决方案不是必需的,如果你打算使用Module,这里是如何做到的
module ReadString def read File.read self if File.exist? self end end class String include ReadString end
编辑:只是阅读有关猴子补丁的安全方法,有关您的文档,请参阅https://solidfoundationwebdev.com/blog/posts/writing-clean-monkey-patches-fixing-kaminari-1-0-0-argumenterror-comparison-的-Fixnum对象-与字符串失败的?utm_source = rubyweekly&utm_medium =电子邮件