如何使用结果类型在 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
}
}
在关于错误处理/异步编程的章节中,有一个关于表达式求值器的示例和为以后存储失败结果的代码 - 下面的代码,函数求值后的行。这部分确实会抛出更多错误,想请问您是否可以帮助澄清这些。按照我的建议,我将 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
}
}