在没有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 =电子邮件