Are there more elegant ways to prevent negative numbers in Ruby?

后端 未结 5 2079
一向
一向 2021-01-17 23:53

Given that I\'d like to do the following calculation:

total = subtotal - discount

Because discount might be greater than

相关标签:
5条回答
  • 2021-01-18 00:15

    Just to clarify more, we need to add classes to be extended in core_ext.rb . file :
    1) Create core_ext.rb file under config\initializers folder in your project.
    2) Paste below as mentioned by @songyy in his answer:

    class Numeric                                                                  
      def non_negative                                                             
        self > 0 ? self : 0                                                                      
      end                                                                          
    end    
    

    Reference:
    https://guides.rubyonrails.org/plugins.html#extending-core-classes

    0 讨论(0)
  • 2021-01-18 00:18

    Thinking we can extend the Numeric class?

    class Numeric                                                                  
      def non_negative                                                             
        self > 0 ? self : 0                                                                      
      end                                                                          
    end                                                                            
    
    class Calculator
      def initialize(subtotal: subtotal, discount: discount)
        @subtotal = subtotal
        @discount = discount
      end
    
      def total
        (@subtotal - @discount).non_negative
      end
    end
    
    0 讨论(0)
  • 2021-01-18 00:25

    A plain if statement might be easier to understand:

    def total
      if discount > subtotal
        0
      else
        subtotal - discount
      end
    end
    
    0 讨论(0)
  • 2021-01-18 00:30

    Some performance numbers:

                            user     system      total        real
    [i, 0.0].max        0.806408   0.001779   0.808187 (  0.810676)
    0.0 if i < 0.0      0.643962   0.001077   0.645039 (  0.646368)
    0.0 if i.negative?  0.625610   0.001680   0.627290 (  0.629439)
    

    Code:

    require 'benchmark'
    
    n = 10_000_000
    Benchmark.bm do |benchmark|
      benchmark.report('[value, 0.0].max'.ljust(18)) do
        n.times do |i|
          a = [-1*i, 0.0].max
        end
      end
    
      benchmark.report('0.0 if value < 0.0'.ljust(18)) do
        n.times do |i|
           a = 0.0 if -1*i < 0.0 
        end
      end
    
      benchmark.report('0.0 if value.negative?'.ljust(18)) do
        n.times do |i|
          a = 0.0 if (-1*i).negative?
        end
      end
    end
    
    0 讨论(0)
  • 2021-01-18 00:32

    I think your solution is essentially correct, and probably the most readable besides a small refactor. I might change it slightly like so:

      def total
        final_total = subtotal - discount
        [final_total, 0].max
      end
    

    The ruby expression [final_total, 0].max is essentially the traditional solution in mathematics for the same function: max {final_total, 0}. The difference is just notation and context. Once you see this max expression once or twice you can read it as follows: "final_total, but at least zero".

    Perhaps if you use this expression more than once you can add another at_least_zero method or something like in Shiko's solution.

    0 讨论(0)
提交回复
热议问题