在没有if ... else块的情况下处理Ruby中的ARGV

时间:2016-12-26 18:56:04

标签: ruby oop

在一篇关于unconditional programming的博文中,Michael Feathers展示了如何将限制if语句用作降低代码复杂性的工具。

他用一个具体的例子来说明他的观点。现在,我一直在考虑其他可以帮助我更多地了解无条件/ if少/ for编程的具体示例。

例如,在此cat克隆中,有一个if..else块:

#!/usr/bin/env ruby

if ARGV.length > 0
  ARGV.each do |f|
    puts File.read(f)
  end
else
  puts STDIN.read
end

事实证明ruby有ARGF,这使得这个程序更加简单:

#!/usr/bin/env ruby

puts ARGF.read

我想知道ARGF是否存在,上面的例子怎么可能被重构,所以没有if..else块?

也对其他说明性具体示例的链接感兴趣。

3 个答案:

答案 0 :(得分:1)

从技术上讲,你可以,

inputs = { ARGV => ARGV.map { |f| File.open(f) }, [] => [STDIN] }[ARGV]
inputs.map(&:read).map(&method(:puts))

虽然那是code golf而且太聪明了。

不过,它是如何运作的?

  • 它使用哈希来存储两个备选方案。
  • ARGV映射到打开文件数组
  • []映射到具有STDIN的数组,如果该条目为空则有效覆盖ARGV条目
  • 访问哈希中的ARGV,如果为空则返回[STDIN]
  • 阅读所有打开的输入并打印它们

不要写那段代码。

正如my answer to your other question中所提到的,无条件编程并不是不惜一切代价避免if表达式,而是追求可读性和意图揭示代码。有时这只意味着使用if表达式。

答案 1 :(得分:-1)

你不能总是摆脱有条件的(可能有疯狂的课程数量),迈克尔·费瑟斯并不是在鼓吹这一点。相反,它有点抵制过度使用条件。我们所见过的噩梦代码都是嵌套if / elsif / else的无穷无尽的链条,他也是如此。

  

此外,人们常常在条件限制内嵌入条件。我曾经见过的一些最糟糕的代码是嵌套条件的巨大噩梦,其中散布着奇怪的工作。我认为控制结构的真正问题在于它们经常与工作混合在一起。我确信我们可以通过某种方式将此视为违反单一责任的一种形式。

您可以通过首先从ARGV创建IO对象数组来简化代码,而不是盲目地尝试消除这种情况,如果该列表为空,则使用STDIN。

io = ARGV.map { |f| File.new(f) };
io = [STDIN] if !io.length;

然后,您的代码可以使用io执行所需的操作。

虽然它具有严格相同数量的条件,但它消除了if / else块并因此消除了分支:代码是线性的。更重要的是,由于它将收集数据与使用它分开,您可以将其放入函数中并重复使用它,从而进一步降低复杂性。一旦它成为一个函数,我们就可以利用早期的回报。

# 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 }

答案 2 :(得分:-2)

首先,虽然原则很好,但你必须考虑其他更重要的事情,比如可读性和执行速度。

也就是说,你可以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/articles/3-ways-to-monkey-patch-without-making-a-mess/

由于read方法不是String的一部分,因此如果您打算使用模块,则不需要使用alias和super的解决方案,这里是如何做到的

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-of-fixnum-with-string-failed?utm_source=rubyweekly&utm_medium=email

相关问题