【问题标题】:Swift compilation error - Expression was too complex to be solved in reasonable timeSwift 编译错误 - 表达式太复杂,无法在合理的时间内解决
【发布时间】:2016-09-15 07:32:07
【问题描述】:

我正在制作一个非常简单的计算器,但我遇到了一个非常奇怪的编译时错误。我的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

【问题讨论】:

  • 你们是如何实现Operation的?
  • 我已经定义了Operation。它是一个带有关联值的枚举,例如case BinaryOperation((Double, Double) -&gt; Double)。使用运算符 */ 时,这很好,但使用运算符 -+ 时出现编译错误
  • 我实际上要求您发布您的代码,展示您如何实现Operation enum。
  • @ozgur 对不起。刚刚用Operation 枚举更新了帖子
  • 你看过these search results吗?

标签: ios xcode swift compiler-errors functional-programming


【解决方案1】:

它与类型推断有关。有关更一般性的讨论,请参阅 this answer

在您的特定情况下,闭包中进行的类型推断导致编译器出现问题。我相信这就是为什么当您在闭包表达式中提供特定类型注释时,编译器能够解决问题。

我建议将您的闭包存储在外部常量中:

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 中重用/添加额外操作的方法的原因。使用您的解决方案,看起来您最终会出现代码重复。加法和减法是二元运算,因此(我认为)拥有一种涵盖该运算而不是两种的函数类型会更整洁。
  • 我理解你的理由。但是,您必须以一种或另一种方式帮助编译器处理类型,否则您将不断收到类似的错误。你必须选择你的毒药:)
【解决方案2】:

这是一个看起来不那么冗长的解决方法:

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

我在斯坦福大学的 Paul Hegarty 关于使用 Swift 进行 IOS 开发的在线课程中偶然发现了这个问题(正如我所期望的那样): https://itunesu.itunes.apple.com/WebObjects/LZDirectory.woa/ra/directory/courses/1104579961/feed.

这可能是 Swift 编译器中的一个错误。

【讨论】:

  • 谢谢,“as”表示法有助于相对简洁、本地化的解决方法。做同样的课程,希望它最终会奏效,我只添加到需要它的课程中,.BinaryOperation({ $0 + $1 } as (Double,Double) -> Double)
【解决方案3】:

虽然有点冗长,但这很有效,同时让我不必在操作字典之外添加任何额外的代码:

// 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 推理功能向后退了几步,因此编译器不必做太多工作。

【讨论】:

    【解决方案4】:
     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
     ]
    

    ...类型推断似乎太多了,我无法通过编译来摆脱错误。有趣的是,如果我只删除几个枚举“操作”类型的说明(不是全部),它仍然有效。所以是的,一个错误。

    【讨论】:

      猜你喜欢
      • 2016-02-21
      • 1970-01-01
      • 2018-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多