Swift编译错误 - 表达式过于复杂以至于无法在合理的时间内解决。

9
我正在制作一个非常简单的计算器,但是我遇到了一个非常奇怪的编译时错误。在我的CalculatorBrain类中,我遇到了以下错误:

表达式过于复杂,无法在合理的时间内解决;请考虑将表达式分解为不同的子表达式

这里是生成错误的代码:
private var operations: Dictionary<String, Operation> = [
    "π" : .Constant(M_PI),
    "±" : .UnaryOperation({ -$0 }),
    "×" : .BinaryOperation({ $0 * $1 }),
    "÷" : .BinaryOperation({ $0 / $1 }),
    "+" : .BinaryOperation({ $0 + $1 }),
    "−" : .BinaryOperation({ $0 - $1 }),
    "=" : .Equals
]

奇怪的是,如果我移除以下内容:
"±" : .UnaryOperation({ -$0 })
"+" : .BinaryOperation({ $0 + $1 })
"−" : .BinaryOperation({ $0 - $1 })

代码编译通过,否则会抛出错误。
另一个奇怪的事情是,如果我将它们改成:
"±" : .UnaryOperation({ (op1: Double) -> Double in return -op1 })
"+" : .BinaryOperation({ (op1: Double, op2: Double) -> Double in return op1 + op2 })
"−" : .BinaryOperation({ (op1: Double, op2: Double) -> Double in return op1 - op2 })

代码编译通过,没有抛出错误。
我有点疑惑,为什么使用运算符 */ 时可以正常工作,而使用 -+ 却不行。
如果您想知道如何实现 Operation ,这里是它的代码:
private enum Operation {
    case Constant(Double)
    case UnaryOperation((Double) -> Double)
    case BinaryOperation((Double, Double) -> Double)
    case Equals
}

我使用的是Xcode版本为7.3.1上的Swift 2.2。


1
你是如何实现 Operation 的? - Ozgur Vatansever
我已经定义了Operation。它是一个带有关联值的枚举,例如case BinaryOperation((Double, Double) -> Double)。问题在于,当使用运算符*/时,一切都很好,但是当使用运算符-+时,我会得到编译错误。 - breaktop
我实际上要求您发布您的代码,展示您如何实现“Operation”枚举。 - Ozgur Vatansever
@ozgur 啊,抱歉。刚刚用Operation枚举更新了帖子。 - breaktop
1
你是否查阅了这些搜索结果 - rmaddy
4个回答

6
这与类型推断有关。请参见该回答以获取更一般的讨论。
在您的特定情况下,闭包中进行的类型推断导致编译器出现问题。我相信这就是为什么当您在闭包表达式中提供具体的类型注释时,编译器能够解析事物的原因。
我建议将您的闭包存储在外部常量中:
let addition: (Double, Double) -> Double = { $0 + $1 }
let subtraction: (Double, Double) -> Double = { $0 - $1 }
// etc...

然后在你的operations字典中使用这些常量:

private var operations: Dictionary<String, Operation> = [
    "+" : .BinaryOperation(addition),
    "−" : .BinaryOperation(subtraction)
    /// etc...
]

这将为编译器提供解决所有问题所需的内容,并且更加清晰(我认为)。

编辑:发布后,我意识到有一种更简洁的方法来编写闭包:

let addition: (Double, Double) -> Double = (+)
let subtraction: (Double, Double) -> Double = (-)

我认为这更加清晰。

其他一些选项可以满足编译器的要求,并减少代码重复,其中包括创建二进制操作数组:

let binaryOps: [((Double, Double) -> Double)] = [(+), (-), (/), (*)]

然后通过索引访问它们:

private var operations: Dictionary<String, Operation> = [
    "+" : .BinaryOperation(binaryOps[0]),
    "−" : .BinaryOperation(binaryOps[1])
    /// etc...
]

或者创建一个typealias

typealias BinaryOp = (Double, Double) -> Double

let addition: BinaryOp = (+)
let subtraction: BinaryOp = (-)

这些减少了一些冗长,但无论如何,我认为你需要在某个地方使用具体的类型注释来满足编译器的要求。


我试图避免代码重复,这就是为什么我采用了我的方法,可以在“Operation”中重用/添加其他操作。使用您的解决方案,看起来会出现代码重复的情况。加法和减法都是二元操作,因此最好(我认为)有一个函数类型涵盖该操作,而不是两个。 - breaktop
我理解你的推理。但不管怎样,你都必须帮助编译器确定类型,否则你会继续得到类似的错误。你必须自行决定 :) - Aaron Rasmussen

1
这是一个看起来不太冗长的解决方法,如下:

typedef d2d = (Double,Double)->Double
enum Operation {
    case Constant(Double)
    case Binary(d2d)
    case Unary((Double)->Double)
    case Equals
}

let b:[String:Operation] = [
    "+": .Binary({$0+$1} as d2d),
    "*":  .Binary({$0*$1} as d2d),
    "/":  .Binary({$0/$1} as d2d),
    "-":  .Binary({$0-$1} as d2d),
    "mod": .Binary( {$0%$1} as d2d),
]

func performOperation(symbol:String, op1:Double, op2:Double)->Double?{
    if let operation = b[symbol] {
        switch operation {
        case .Binary(let op ) :
            return(op(op1,op2))
        default:
            break
        }
    }
    return(nil)
}



performOperation("*", op1:3, op2:7)  // 21

我偶然遇到了这个问题(就像breaktop一样),是从斯坦福大学Paul Hegarty的在线课程中学习IOS开发使用Swift时遇到的:https://itunesu.itunes.apple.com/WebObjects/LZDirectory.woa/ra/directory/courses/1104579961/feed

很可能这是Swift编译器中的一个bug。


谢谢,"as"符号表示法有助于相对简洁、局部化的解决方法。我正在做同样的课程,希望最终能够奏效,我只是添加到需要它的地方, .BinaryOperation({ $0 + $1 } as (Double,Double) -> Double) - dbreaux

0

虽然有点啰嗦,但这个方法可以让我避免在操作字典之外添加任何额外的代码:

// operations list
private let operations : Dictionary<String, Operation> = [
    "π" : .Constant(M_PI),
    "e" : .Constant(M_E),
    "√" : .UnaryOperation(sqrt),
    "cos" : .UnaryOperation(cos),
    "×" : .BinaryOperation({ (op1, op2) -> Double in return op1 * op2 }),
    "÷" : .BinaryOperation({ (op1, op2) -> Double in return op1 / op2 }),
    "+" : .BinaryOperation({ (op1, op2) -> Double in return op1 + op2 }),
    "−" : .BinaryOperation({ (op1, op2) -> Double in return op1 - op2 }),
    "=" : .Equals
]

这只是将超级棒的Swift推理功能向后移动几步,以便编译器不必做太多工作。


0
 private let mathOperationTable : Dictionary<String,Operation> =
 [    "π" : Operation.Constant(Double.pi),
      "e" : Operation.Constant(M_E),
      "√" : Operation.UnaryOperation(sqrt),
      "±" : Operation.UnaryOperation({-$0}),
      "+" : Operation.BinaryOperation(+),
      "−" : Operation.BinaryOperation(-),
      "×" : Operation.BinaryOperation(*),
      "÷" : Operation.BinaryOperation(/),
      "=" : Operation.Equals
 ]

这是我的版本。我可以通过重新构建/运行来摆脱编辑器中的错误,这似乎比编辑器中可用的编译更加新鲜和准确。如果我删除所有的“操作”-

 private let mathOperationTable : Dictionary<String,Operation> =
 [    "π" : .Constant(Double.pi),
      "e" : .Constant(M_E),
      "√" : .UnaryOperation(sqrt),
      "±" : .UnaryOperation({-$0}),
      "+" : .BinaryOperation(+),
      "−" : .BinaryOperation(-),
      "×" : .BinaryOperation(*),
      "÷" : .BinaryOperation(/),
      "=" : .Equals
 ]

...看起来有太多的类型推断,我无法通过编译错误。有趣的是,如果我仅删除一些枚举“操作”类型的说明(而不是全部),它仍然可以工作。所以,是一个 bug。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接