Variable used before being initialized in function

2020-02-07 11:12发布

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 initialized in function

What am I doing wrong?

import Foundation
import UIKit
class Result: UIViewController {
    var rval: Int?
    var chosen: Int?
    func determineWinner() -> Int {
        var returnval: Int
        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)
    }

    @IBOutlet weak var wl: UILabel!

    @IBAction func PlayAgain(sender: AnyObject) {
    }
    override func viewDidLoad() {
        print(chosen)
    }
}

标签: ios swift
6条回答
地球回转人心会变
2楼-- · 2020-02-07 11:22

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()
    }
} 
查看更多
够拽才男人
3楼-- · 2020-02-07 11:24

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!

查看更多
Fickle 薄情
4楼-- · 2020-02-07 11:27

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

查看更多
趁早两清
5楼-- · 2020-02-07 11:37

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
查看更多
别忘想泡老子
6楼-- · 2020-02-07 11:39

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;
    }
}
查看更多
一纸荒年 Trace。
7楼-- · 2020-02-07 11:43

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.

查看更多
登录 后发表回答