Filip Bartuzi
Filip Bartuzi

Reputation: 5931

Differences between || and inline if statement?

Is there any difference (performance? semantic? readability?) between

def test
  x ? x : y
end

and

def test
  x || y
end?

Also want to ask about best practices in this situation.

Upvotes: 1

Views: 261

Answers (4)

Jay Mitchell
Jay Mitchell

Reputation: 1240

Readability

I prefer x || y.

Semantically

Summary: Exactly the same.

Here is my test:

ternary = ->(x, y) { x ? x : y }
ord = ->(x, y) { x || y }
puts "Ternary nil, nil: #{ternary.call(nil, nil)}"
puts "Or'd    nil, nil: #{ord.call(nil, nil)}"
puts "Ternary nil, 1:   #{ternary.call(nil, 1)}"
puts "Or'd    nil, 1:   #{ord.call(nil, 1)}"
puts "Ternary 1,   nil: #{ternary.call(1, nil)}"
puts "Or'd    1,   nil: #{ord.call(1, nil)}"
puts "Ternary 1,   2:   #{ternary.call(1, 2)}"
puts "Or'd    1,   2:   #{ord.call(1, 2)}"

The result is:

Ternary nil, nil: 
Or'd    nil, nil: 
Ternary nil, 1:   1
Or'd    nil, 1:   1
Ternary 1,   nil: 1
Or'd    1,   nil: 1
Ternary 1,   2:   1
Or'd    1,   2:   1

Performance

Summary: The speed differences are so negligible that if it matters, you probably shouldn't choose Ruby.

Here is my test:

require 'benchmark'

n = 10_000_000
Benchmark.bm do |b|
  b.report("T n,n") { n.times do; x=nil; y=nil; x ? x : y; end }
  b.report("O n,n") { n.times do; x=nil; y=nil; x || y; end }
  puts
  b.report("T n,1") { n.times do; x=nil; y=1; x ? x : y; end }
  b.report("O n,1") { n.times do; x=nil; y=1; x || y; end }
  puts
  b.report("T 1,n") { n.times do; x=1; y=nil; x ? x : y; end }
  b.report("O 1,n") { n.times do; x=1; y=nil; x || y; end }
  puts
  b.report("T 1,2") { n.times do; x=1; y=2; x ? x : y; end }
  b.report("O 1,2") { n.times do; x=1; y=2; x || y; end }
end

The result is:

       user     system      total        real
T n,n  0.720000   0.000000   0.720000 (  0.715549)
O n,n  0.780000   0.000000   0.780000 (  0.781091)

T n,1  0.580000   0.000000   0.580000 (  0.579195)
O n,1  0.630000   0.000000   0.630000 (  0.636524)

T 1,n  0.580000   0.000000   0.580000 (  0.572820)
O 1,n  0.600000   0.000000   0.600000 (  0.599424)

T 1,2  0.580000   0.000000   0.580000 (  0.588349)
O 1,2  0.590000   0.000000   0.590000 (  0.587952)

Upvotes: 1

pmwood
pmwood

Reputation: 771

Your second example is more idiomatic to Ruby.

def test
  x || y
end

It uses short circuit evaluation to return the first "truthy" value in a comparison. The performance gain between this and the ternary operator is probably negligent.

From experience, I'll say that I don't see the ternary operator very often in Ruby. One scenario where the above x || y form won't work is when you want to select a non-empty string. In Ruby "" is truthy, so "" || "abc" will be true. In this case, the ternary operator (or a regular if statement) would work very well:

str.empty? ? "abc" : str

References:

Upvotes: 4

Oscar Barrett
Oscar Barrett

Reputation: 3265

Semantically, there is no difference. Both check the truthiness of x and fallback to y if x is false. The first is more readable, and is more powerful in it's usage (for example, if you want z to be returned when x is true).

In terms of performance, I ran a benchmark with 100 million iterations.

                       user     system      total        real
if-else                5.480000   0.010000   5.490000 (  5.485275)
or                     5.580000   0.010000   5.590000 (  5.597094)

The results are pretty close, there's barely any difference.

require 'benchmark'
x = false
y = 1
iterations = 100_000_000

Benchmark.bm(20) do |bm|
  bm.report('if-else') do
    iterations.times { z = x ? x : y }
  end

  bm.report('or') do
    iterations.times { z = x || y }
  end
end

Upvotes: 0

Doon
Doon

Reputation: 20232

I prefer the x || y version, but it depends on what x and y are. I find it more readable as you don't have to repeat the x.

as for faster a quick and dirty benchmark shows there is not much (if any differnce)

require 'benchmark'
n=1_000_000
Benchmark.bm do |b|
  b.report("||")  { n.times do ;  x=true; y=2; x || y; end }
  b.report("? :") { n.times do ;  x=true; y=2; x ? x :y ; end }
end

run 1

[~] ruby foo.rb
       user     system      total        real
||  0.080000   0.000000   0.080000 (  0.076901)
? :  0.080000   0.000000   0.080000 (  0.076868)

run 2

[~] ruby foo.rb
       user     system      total        real
||  0.070000   0.000000   0.070000 (  0.076943)
? :  0.080000   0.000000   0.080000 (  0.077277)

run 3

[~] ruby foo.rb
       user     system      total        real
||  0.070000   0.000000   0.070000 (  0.075403)
? :  0.080000   0.000000   0.080000 (  0.076831)


[~] ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

Upvotes: 3

Related Questions