Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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

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

1 Answer

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"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...