在一篇关于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
块?
也对其他说明性具体示例的链接感兴趣。
答案 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