Variable used before being initialized in function

前端 未结 6 1572
孤城傲影
孤城傲影 2020-12-12 06:14

I\'m making a ro sham bo game. Functions in swift are different than what I have used before. I keep getting an error:

Variable used before being init

相关标签:
6条回答
  • 2020-12-12 06:55

    Try var returnval: Int = 0 (or another random number in case your if-else statements are exhaustive)

    0 讨论(0)
  • 2020-12-12 06:57

    EDITTED

    Problem Resolution

    You're getting the initializer error because you haven't initialized returnval with Int() prior to setting.

    Code Improvements

    Consider using a computed property to return the value of who won. I made the assumption in my code below, that the value 2 you were using inferred a tie situation based on your logic.

    Here I created an enumeration to ensure the returned value is only handled in these particular ways you'd expect the outcome to mean. You can still access the Int values via the hash value on the enum case. Use .rawValue to do so.

    It is important to try your best to avoid using Int values in this case because they can be other values.

    In the code I included below I wrote you a set of guard statements to fail and return a fatalError message when the values checked do not allow the winner state to be determined.

    Improved Code

    import Foundation
    import UIKit
    
    
    class Result: UIViewController {
    
      var rval: Int?
      var chosen: Int?
    
      enum Winner: Int {
        case one  = 0
        case two  = 1
        case tie  = 2
      }
    
      var winner: Winner? {
    
        guard (rval > 0 || rval < 4) else {
          fatalError("rval out of bounds: cannot determine winner")
        }
        guard (chosen > 0 || chosen < 3) else {
          fatalError("chosen out of bound: cannot determine winner")
        }
        guard (rval != nil && chosen != nil) else {
          fatalError("rval or chosen are nil: cannot determine winner")
        }
    
        switch (chosen!, rval!) {
        case (let chosen, let rval) where chosen == rval: return Winner.tie
        case (1, 3): return Winner.two
        case (1, 2): return Winner.one
        case (2, 1): return Winner.two
    
        default:
          return nil
        }
      }
    
    
      @IBOutlet weak var wl: UILabel!
    
      @IBAction func PlayAgain(sender: AnyObject) {
      }
      override func viewDidLoad() {
        print(chosen)
      }
    }
    

    As a side note be sure to +1 the answers you like!

    0 讨论(0)
  • 2020-12-12 06:59

    You can make this much more swifty.

    For instance... Why use Int to represent the different moves? Here I used an enum to represent the moves and the logic behind what wins...

    enum RoShamBo {
        case Rock
        case Paper
        case Scissors
    
        func beats(opposingPlay: RoShamBo) -> Bool {
            return self > opposingPlay
        }
    }
    
    // I thought it made sense to make it Comparable.
    // That way you can use the < or > operator to determine
    // the winning move.
    // I then used this in the function beats() above
    extension RoShamBo: Comparable {}
    
    func < (lhs: RoShamBo, rhs: RoShamBo) -> Bool {
        // this is required for Comparable
        // in this we return true if lhs loses to rhs
    
        // Scissors beat Paper
        return lhs == .Paper && rhs == .Scissors
        // Paper beats Rock
            || lhs == .Rock && rhs == .Paper
        // Rock beats Scissors
            || lhs == .Scissors && rhs == .Rock
    }
    

    Then all you need to do is wrap this up in some sort of Player and Game type thing...

    struct Player {
        let name: String
        let move: RoShamBo
    }
    
    struct Game {
        func winner(player1: Player, player2: Player) -> Player? {
            if player1.move.beats(opposingPlay: player2.move) {
                return player1
            }
    
            if player2.move.beats(opposingPlay: player1.move) {
                return player2
            }
    
            // tie
            return nil
        }
    }
    
    let p1 = Player(name: "Oliver", move: .Rock)
    let p2 = Player(name: "Geoff", move: .Scissors)
    
    let game = Game()
    
    let winner = game.winner(player1: p1, player2: p2)
    
    print(winner)
    
    //Output Optional(Player(name: "Oliver", move: RoShamBo.Rock))
    

    Not a single Int was used in the whole thing and you can see exactly what the winning move was and who won etc...

    Enums are really a lot more powerful than they are given credit for in Swift.

    0 讨论(0)
  • 2020-12-12 07:01

    The problematic statement is return (returnval) because Swift compiler thinks that there is a pass through the if-then-else chain that does not result in assignment of returnval.

    For example, if chosen is 3 and rval is 2, there would be no assignment.

    Perhaps other parts of your program makes it impossible for chosen to be 3 at the same time as rval is 2, but Swift has no idea of that, so it reports an error. In order to fix the compile error, add an initial value to returnval.

    If you are absolutely sure that your if-then-else chain enumerates all valid possibilities, set returnval to -1, and make an assertion about it being set to a non-negative value before returning:

    var returnval = -1
    ... // your conditionals go here
    assert(returnval >= 0, "Logic of determineWinner is broken")
    return returnval
    
    0 讨论(0)
  • 2020-12-12 07:01

    Every possible path in the flow of a Swift program must have a defined returned value. In your case, if the if/else if/else sections are all skipped, leaving returnval unassigned. Thus, no valid value is being returned. Try this:

    import Foundation
    import UIKit //space things out to be easier to read
    
    class Result: UIViewController {
    
        var rval: Int? //these should have better names
        var chosen: Int?
    
        func determineWinner() -> Int {
    
            var returnval = -1 //needs a default value
    
            if (chosen == rval){
                returnval = 2
            }
            else if (chosen == 1 && rval == 3){
                returnval = 1
            }
            else if (chosen == 1 && rval == 2){
                returnval = 0
            }
            else if (chosen == 2 && rval == 1){
                returnval = 1
            }
    
            return returnval //don't put return value in brackets
        }
    
        @IBOutlet weak var wl: UILabel!
    
        @IBAction func PlayAgain(sender: AnyObject) {
        }
    
        override func viewDidLoad() {
            print(chosen) 
        }
    }
    

    This is also a very good candidate for using pattern matching with switch statements. Here's what I think works beside, in conjunction withSean's suggestion.

    var determineWinner: Int? {
    
        guard let chosen = chosen, let rval = rval else {
            //handle error, either chosen or rval is nil
            return nil
        }
    
        switch ((chosen, rval)) {
        case let (x, y) where x == y: return 2
        case (1, 3): return 1
        case (1, 2): return 0
        case (2, 1): return 1
    
        default:
            print("handle default case here")
            return nil;
        }
    }
    
    0 讨论(0)
  • 2020-12-12 07:03

    Returning an Int?

    As others already said, the problem is that none of your conditions is met then returnval is not initialized.

    You can use a switch statement + guard + computed property. Like this

    var winner: Int? {
        guard let chosen = chosen, rval = rval else { return nil }
        switch (chosen, rval) {
        case (let chosen, let rval) where chosen == rval : return 2
        case (1, 3): return 1
        case (1, 2): return 0
        case (2, 1): return 1
        default: return nil
        }
    }
    

    Please note I slightly changed your logic. Infact in my code if chosen and rval are both nil the returned value is nil. While in your code 2 is returned. You should change it, maybe adding another guard on top of my code. Something like

    guard chosen != rval else { return 2 } 
    

    Returning Int + Fatal error

    If you know chose and rval will always be populated then

    var winner: Int {
        guard let chosen = chosen, rval = rval else { fatalError() }
        switch (chosen, rval) {
        case (let chosen, let rval) where chosen == rval : return 2
        case (1, 3): return 1
        case (1, 2): return 0
        case (2, 1): return 1
        default: fatalError()
        }
    } 
    
    0 讨论(0)
提交回复
热议问题