Search code examples
rubyoptimizationternary

How can I speed up using a ternary operator?


I am attempting to answer a challenge question about coordinates within 100ms response time. I am given the following values through STDIN and am told to respond with a direction ("NE", "W", etc).

@light_x, @light_y, @initial_tx, @initial_ty = gets.split(" ").collect {|x| x.to_i}

I am trying to figure out why this attempt times out:

print @light_y > @initial_ty ? "S" : "N" unless @light_y == @initial_ty
print @light_x > @initial_tx ? "E" : "W" unless @light_x == @initial_tx

while this attempt does not:

if @light_x > @initial_tx && @light_y > @initial_ty
    puts "SE"
elsif @light_x < @initial_tx && @light_y > @initial_ty
    puts "SW"
elsif @light_x > @initial_tx && @light_y < @initial_ty
    puts "NE"
elsif @light_x < @initial_tx && @light_y < @initial_ty
    puts "NW"
elsif @light_x < @initial_tx && @light_y == @initial_ty
    puts "W"
elsif @light_x > @initial_tx && @light_y == @initial_ty
    puts "E" 
elsif @light_y > @initial_ty && @light_x == @initial_tx
    puts "S" 
elsif @light_y < @initial_ty && @light_x == @initial_tx
    puts "N"
else
end

Is there any way to speed up my ternary, or am I going about this the wrong way?


Solution

  • I think the primary issue isn't speed, it's clarity of the logic. I'd rewrite these:

    print @light_y > @initial_ty ? "S" : "N" unless @light_y == @initial_ty
    print @light_x > @initial_tx ? "E" : "W" unless @light_x == @initial_tx  
    

    to:

    if @light_y > @initial_ty
      print 'S'
    elsif @light_y < @initial_ty
      print 'N'
    end
    
    if @light_x > @initial_tx
      print 'E'
    elsif @light_x < @initial_tx
      print 'W'
    end
    

    A ternary statement is only appropriate when there are two possible conditions, typically the result of a true/false condition or test result. You've got three conditions: <, > and == and you only want to output for the first two, resulting in logic that doesn't flow and is confusing.

    Similarly, I think you can reduce this:

    if @light_x > @initial_tx && @light_y > @initial_ty
      puts "SE"
    elsif @light_x < @initial_tx && @light_y > @initial_ty
      puts "SW"
    elsif @light_x > @initial_tx && @light_y < @initial_ty
      puts "NE"
    elsif @light_x < @initial_tx && @light_y < @initial_ty
      puts "NW"
    elsif @light_x < @initial_tx && @light_y == @initial_ty
      puts "W"
    elsif @light_x > @initial_tx && @light_y == @initial_ty
      puts "E" 
    elsif @light_y > @initial_ty && @light_x == @initial_tx
      puts "S" 
    elsif @light_y < @initial_ty && @light_x == @initial_tx
      puts "N"
    else
    end
    

    to:

    y_direction = if @light_y > @initial_ty
                    'S'
                  elsif @light_y < @initial_ty
                    'N'
                  else
                    ''
                  end
    
    x_direction = if @light_x > @initial_tx
                    'E'
                  elsif @light_x < @initial_tx
                    'W'
                  else
                    ''
                  end
    
    puts y_direction + x_direction              
    

    That's all untested, but it looks about right.

    The changes to the code should run faster because the code is reduced to fewer tests. It's also easier to understand, which your future self will appreciate.

    Finally:

    gets.split(" ")
    

    can be written as:

    gets.split
    

    since the default behavior is to split on whitespace.