Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
200 views
in Technique[技术] by (71.8m points)

oop - Handle command-line switch in Ruby without if...else block

In a blog post about unconditional programming Michael Feathers shows how limiting if statements can be used as a tool for reducing code complexity.

He uses a specific example to illustrate his point. Now, I've been thinking about other specific examples that could help me learn more about unconditional/ifless/forless programming.

For example, using OptionParser I made a cat clone that will upcase the stream if the --upcase switch is set:

#!/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

How would I handle that switch without an if..else block?

Also interested in links to other illustrative specific examples.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Try this,

#!/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])

This worked quite well, I am actually surprised how well that worked.

What has been changed?

  • The option is internally renamed to :transform
  • The internal default value is :itself
  • The command line switch sets the internal option to :upcase
  • Call the method with send

Not all if statements can be improved upon like this though. I would guess the idea of unconditional programming is to prefer a combination of meaningful default values, as I did above, and intention revealing functions whenever it seems reasonable but not at all costs.

Here are some examples of intention revealing functions,

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

Another related practice is forless programming.

If you want to practice unconditional and forless programming best look for examples that process arrays and strings and make use of the many "functional" methods in Ruby's enumerable module.

Here is an example of string justification without for and if,

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"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...