Wie vergleiche ich zwei ganzzahlige Hashwerte?

  • Ich habe eine Funktion erstellt, die zwei Hashes vergleicht und einen Bericht zurückgibt, ob die Subtraktion ihrer Werte negativ ist oder nicht.

    Das Problem wird behoben Einen Kosten-Hash für ein Gebäude und einen aktuellen Ressourcen-Hash haben:

     cost = {:wood => 300, :stone => 200, :gold => 100}
    reqs = {:wood => 200, :stone => 220, :gold => 90}
     

    Dieser sollte einen Bericht zurückgeben hash wie:

     report = { :has_resources => false, :has_wood =>  true, :has_stone => false, :has_gold => true } 
     

    Jetzt im Kosten-Hash :gold, :stone oder :wood kann Null sein, dh nicht vorhanden.

    Mein erster Versuch ist definitiv nicht der Ruby-Weg und ich mag die Funktion nicht. Es funktioniert, aber ich möchte einen Weg finden, es besser zu schreiben:

     def has_resources?(cost)
      report = { :has_resources => true, :has_wood =>  true, :has_stone => true, :has_gold => true } 
      if not cost[:wood].nil? 
        if self.wood < cost[:wood]
          report[:has_wood] = false
          report[:has_resources] = false 
        end
      end
      if not cost[:stone].nil? 
        if self.stone < cost[:stone]
          report[:has_stone] = false
          report[:has_resources] = false
        end
      end   
      if not cost[:gold].nil?
        if self.gold < cost[:gold]
          report[:has_gold] = false
          report[:has_resources] = false
        end
      end
    
    end
     

    Wie soll ich schreib das neu? Ich mag die .nil? -Kontrollen hier nicht, aber ich muss sie einbeziehen, da der Operator < nicht für null Objekte funktioniert. Ich mag es auch nicht, so viele if s zu haben.

    18 January 2012
3 answers
  • Hier ist eine Version, die etwas mehr DRY ist. Es verwendet auch den Standardwert von ruby ​​für Hashes, und ich habe eine Dummy-Klasse eingefügt, um die Tests tatsächlich ausführen zu lassen.

    Dies könnte wahrscheinlich noch besser sein, aber einer Das, was Sie beim Schreiben von Ruby-Code (oder überhaupt eines anderen Codes überhaupt) wirklich erreichen sollten, besteht darin, sich nicht zu sehr zu wiederholen. Hier kommt DRY ins Spiel.

     class CostCalculation
      attr_accessor :wood, :stone, :gold
    
      def initialize(reqs)
        self.wood = reqs[:wood] || 0
        self.stone = reqs[:stone] || 0
        self.gold = reqs[:gold] || 0
      end 
    
      def has_resources?(cost)
        report = Hash.new(true)
        cost.default = 0 
    
        attributes.each do |key, value|
          report[:"has_#{key}"] = cost[key] <= value
        end 
    
        if report.values.include?(false)
          report[:has_resources] = false
        end 
    
        report
      end 
    
      def attributes
        { wood: self.wood, stone: self.stone, gold: self.gold }   
      end 
    end
     

    ... und hier sind auch einige Spezifikationen dafür:

     describe CostCalculation do
      it "has enough resources for something that's free" do
        report = CostCalculation.new({}).has_resources?({})
        report[:has_resources].should == true
      end 
    
      %w[wood stone gold].each do |resource|    
        it "does not have enough resources if #{resource} is missing" do
          cost = {resource.to_sym => 1}
          report = CostCalculation.new({wood: 0, stone: 0, gold: 0}).has_resources?(cost)
    
          report[:has_resources].should == false
          report[:"has_#{resource}"].should == false
        end 
      end 
    end
     
    18 January 2012
    Martin Frost
  • Hier ist meine Version: Getestet Arbeiten unter Ruby 1.8.7-p334

    Angenommen, die Eingaben sind:

     cost = {:wood => 300, :stone => 200, :gold => 100}
    reqs = {:wood => 200, :stone => 220, :gold => 90}
     

    und im Cost-Hash: Gold,: Stein oder: Holz kann Null sein.

     def has_resources?(cost,reqs)
        result = cost.merge(reqs) { |key, cst, rqs| cst.nil?||(cst - rqs) > 0 ? true : false }
        result[:has_resources] = !result.has_value?(false)
        return result
    end
     

    Gibt das Ergebnis zurück:

     { :has_resources => false, :wood =>  true, :stone => false, :gold => true } 
     

    Wenn Soll das Ergebnis wirklich "has_wood", "has_stone", "has_gold" als Schlüssel haben, müssen Sie möglicherweise den Code ändern, um den Schlüssel in result Hash umzubenennen. Aber ich denke, es ist nicht nötig.

    Falls die Leute fragen, warum sie die Methode zusammenführen? Hier ist die Dokumentation zu Ruby Hash Merge Wenn Sie zwei Hash-Werte mit demselben Schlüssel vergleichen, denke ich, dass die "Zusammenführen" -Methode der einfache Weg ist.

    Danke

    18 January 2012
    Haoest
  • Zuerst wiederholen Sie sich durch den copy'n'paste-Code , so dass er in eine Art Iteration umgewandelt werden sollte. Dann brechen Sie in kleine Stücke, die sich gut benennen lassen, und Sie haben etwas Kompaktes und Lesbares:

     MATERIALS = [ :wood, :stone, :gold ]
    
    def resource_availability(costs)
        MATERIALS.reject(&have_enough_for(costs)).
                  each_with_object(base_report, &not_enough) 
    end
    
    private
    
    def have_enough_for(costs)
        ->(material) { costs[material].nil? || costs[material] < self.send(material) }
    end
    
    def has_sym(material)
        ('has_' + material.to_s).to_sym
    end
    
    def base_report
        MATERIALS.each_with_object({ :has_resources => true }) { |m, h| h[has_sym(m)] = true }
    end
    
    def not_enough
        ->(material, report) do
            report[has_sym(material)] = false
            report[:has_resources   ] = false
        end
    end
     
    18 January 2012
    Sung KimDon P