在没有if ... else块的情况下处理Ruby中的命令行开关

时间:2016-12-26 19:20:21

标签: ruby oop if-statement

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

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

例如,使用OptionParser我创建了一个cat克隆,如果设置了--upcase开关,该克隆会使流整齐:

#!/usr/bin/env ruby

require 'optparse'

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: cat [options] [file ...]"

  opts.on("-u", "--upcase", "Upcase stream") do
    options[:upcase] = true
  end
end.parse!

if options[:upcase]
  puts ARGF.read.upcase
else
  puts ARGF.read
end

如果没有if..else阻止,我将如何处理该切换?

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

2 个答案:

答案 0 :(得分:3)

试试这个,

#!/usr/bin/env ruby
require 'optparse'

options = { :transform => :itself }
OptionParser.new do |opts|
  opts.banner = "Usage: cat [options] [file ...]"
  opts.on("-u", "--upcase", "Upcase stream") do
    options[:transform] = :upcase
  end
  # add more options for downcase, reverse, etc ...
end.parse!

puts ARGF.read.send(options[:transform])

这很有效,我真的很惊讶它的效果如何。

发生了什么变化?

  • 该选项在内部重命名为:transform
  • 内部默认值为:itself
  • 命令行开关将内部选项设置为:upcase
  • 使用send
  • 调用方法

尽管如此,并非所有if语句都可以改进。我猜想无条件编程的想法是更喜欢有意义的默认值的组合,就像我上面所做的那样,并且只要看起来合理但不是不惜一切代价,意图揭示功能。

以下是意图揭示功能的一些例子,

  • max
  • min
  • Hash#fetch
  • Enumerable#detect
  • Enumerable#select
  • Enumerable#chunk
  • Enumerable#drop_while
  • Enumerable#slice_when
  • Enumerable#take_while
  • 等...

另一种相关做法是for少编程。

如果你想练习无条件和for编程,最好寻找处理数组和字符串的例子,并利用许多" functional" Ruby的可枚举模块中的方法。

以下是没有forif

的字符串对齐示例
str = 'This is an example to be aligned to both margins'    
words = str.split
width, remainder = (50 - words.map(&:length).inject(:+)).divmod(words.length - 1)
words.take(words.length - 1).each { |each| width.times { each << 32 }}
words.take(words.length - 1).shuffle.take(remainder).each { |each| each << 32 }
p words.join
# => "This  is an example to  be aligned to both margins"

答案 1 :(得分:1)

消除条件是降低复杂性的工具,而不是最终目标。 I explained that better in my other answer。在这种情况下,条件必须存在,因为是否设置了options[:upcase]是逻辑的一部分。但你至少可以消除重复。

因为else子句与if子句相同但只是添加了一个步骤,所以你可以通过无条件地执行公共位来删除重复并使代码成为线性,然后有条件地执行额外的操作。

data = ARGF.read;
data.upcase! if options[:upcase];