如何使用结果类型在 Swift 中存储 success/failure 状态

How to use Result type to store success/failure states in Swift

在关于错误处理/异步编程的章节中,有一个关于表达式求值器的示例和为以后存储失败结果的代码 - 下面的代码,函数求值后的行。这部分确实会抛出更多错误,想请问您是否可以帮助澄清这些。按照我的建议,我将 lex() 方法 return 类型更改为 Result<[Token], Lexer.Error>。而return成败。 .success(tokens) 或 .failure(.invalidCharacter(nextCharacter)),但后来不起作用。还试图注释掉与解析器相关的所有行。如果你能指点一下。

在最后几行中,我根据大家的意见修改了代码,感谢您的帮助。更改已评论。

   enum Token: CustomStringConvertible {
    case number (Int)
    case plus
    
    var description: String {
        switch self {
        case .number (let n):
            return "Number: \(n)"
        case .plus:
            return "Symbol: +"
        }
    }
}

class Lexer {
    enum Error: Swift.Error {
        case invalidCharacter (Character)
    }
    
    let input: String
    var position: String.Index
    
    init(input: String) {
        self.input = input
        self.position = input.startIndex
    }
    
    func peek() -> Character? {
        guard position < input.endIndex else {
            return nil
        }
        return input[position]
    }
    
    func advance() {
        assert(position < input.endIndex, "Cannot advance past endIndex!")
        position = input.index(after: position)
    }
    
    func getNumber() -> Int {
        var value = 0
        
        while let nextCharacter = peek() {
            switch nextCharacter {
            case "0" ... "9":
                let digitValue = Int(String(nextCharacter))!
                value = 10*value + digitValue
                advance()
            default:
                return value
            }
        }
        return value
    }
    
    func lex() throws -> [Token] { // changed to [Token]
        var tokens = [Token]()
        
        while let nextCharacter = peek() {
            switch nextCharacter {
            case "0" ... "9":
                let value = getNumber()
                tokens.append(.number(value))
            case "+":
                tokens.append(.plus)
                advance()
            case " ":
                advance()
            default:
                throw Lexer.Error.invalidCharacter(nextCharacter)
                // .failure(.invalidCharacter(nextCharacter)) did not work here
            }
        }
        return tokens // changed to tokens
    }
}

func evaluate(_ input: String) {
    print("Evaluating: \(input)")
    let lexer = Lexer(input: input)
    do {
        let tokens = try lexer.lex()
        print("Lexer output: \(tokens)")
    
       // let parser = Parser(tokens: tokens)
       // let result = try parser.parse()
       // print("Parser output: \(result)")
    } catch Lexer.Error.invalidCharacter(let character) {
        print("Input contained an invalid character: \(character)")
    // } catch Parser.Error.unexpectedEndOfInput {
        // print("Unexpected end of input during parsing")
    // } catch Parser.Error.invalidToken(let token) {
       //  print("Invalid token during parsing: \(token)")
    } catch {
        print("An error occured: \(error)")
    }
}
/*
class Parser {
    enum Error: Swift.Error {
        case unexpectedEndOfInput
        case invalidToken(Token)
    }
   
    let tokens: [Token] // [Token]
    var position = 0
    
    init(tokens: [Token]) {
        self.tokens = tokens // tokens
    }
    
    func getNextToken() -> Token? {
        guard position < tokens.count else {
            return nil
        }
        
        let token = tokens[position]
        position += 1
        return token
    }
    
    func getNumber() throws -> Int {
        guard let token = getNextToken() else {
            throw Parser.Error.unexpectedEndOfInput
        }
        
        switch token {
        case .number(let value):
            return value
        case .plus:
            throw Parser.Error.invalidToken(token)
        }
    }
    
    func parse() throws -> Int {
        var value = try getNumber()
        
        while let token = getNextToken() {
            switch token {
            case .plus:
                let nextNumber = try getNumber()
                value += nextNumber
            case .number:
                throw Parser.Error.invalidToken(token)
            }
        }
        return value
    }
}
*/

// evaluate("10 + 1")

/*
let lexer = Lexer(input: "1 + 2")
let tokensResult = Result { try lexer.lex() }
print(tokensResult)
*/

/*
let lexer = Lexer(input: "1 + 2 + 3")
let tokensResult = Result { try lexer.lex() } // Result<Success, Failure>' cannot be constructed because it has no accessible initializers

enum Result<Success, Failure> where Failure : Error {
    case .success(Success) // Extraneous '.' in enum 'case' declaration
    case .failure(Failure) // Extraneous '.' in enum 'case' declaration
}

switch tokensResult {
case let .success(tokens):
    print("Found \(tokens.count) tokens \(tokens)")
case let .failure(error):
    print("Could not lex \(lexer.input): \(error)")
}

let numbersResult: Result<[Int],Error> = tokensResult.map {
      tokens.compactMap { token in // Cannot find 'tokens' in scope
          switch token {
          case let .number(digit): return digit
          default: return nil
          }
      }
}

func extractNumbers(from result: Result<[Int],Error>) throws -> [Int] {
        return try result.get() // Value of type 'Result<[Int], Error>' has no member 'get'
    }
*/

let lexer = Lexer(input: "1 + 7")
let tokensResult = Result { try lexer.lex() }
var tokens = [Token]() // added

// deleted redeclaration of Result

switch tokensResult {
case let .success(tokens):
    print("Found \(tokens.count) tokens \(tokens)")
case let .failure(error):
    print("Could not lex \(lexer.input): \(error)")
}

// before error: Contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored, fixed by: insert _ in

let numbersResult: Result<[Int],Error> = tokensResult.map { _ in
      tokens.compactMap { token in
          switch token {
          case let .number(digit): return digit
          default: return nil
          }
      }
}

func extractNumbers(from result: Result<[Int],Error>) throws -> [Int] {
        return try result.get()
    }

我认为您需要更多地阅读 Result 类型以及如何使用它,因为上面有一些非常奇怪的代码。

这是我清理过的代码版本,没有重新定义 Result 和正确的处理方式 .success

let lexer = Lexer(input: "1 + 2 + 3")
let tokensResult = try lexer.lex()

var tokens =  [Token]()
switch tokensResult {
case let .success(result):
    print("Found \(tokens.count) tokens \(tokens)")
    tokens = result
case let .failure(error):
    print("Could not lex \(lexer.input): \(error)")
}

let numbersResult = tokens.compactMap { token in
    switch token {
    case let .number(digit): return digit
    default: return nil
    }
}