Swift中基于NSExpression实现带IF逻辑的数据库字符串公式计算
解决Swift中NSExpression支持分支逻辑公式的方案
核心问题分析
你之前尝试的TERNARY表达式无效,是因为错误地将子公式用引号括起来——原生TERNARY()需要接收表达式而非字符串,引号会让NSExpression把它当成字符串字面量,无法执行计算。
可行方案
方案1:正确使用原生TERNARY表达式(推荐)
直接在数据库存储的公式中嵌套表达式,不用引号,同时改用带索引的占位符(如$1、$2)避免变量替换歧义。
代码示例
// 数据库存储的分支公式(支持多分支嵌套) let dbFormula = "TERNARY($1 == 1, (($2*$3)-$4)/$5, TERNARY($1 == 2, (($2*$3)+$4)*$5, 0))" // 输入值数组(按占位符索引对应:$1对应第一个值,$2对应第二个,以此类推) let inputValues = [2.0, 5.0, 3.0, 2.0, 6.0] // 将值绑定到字典,用于NSExpression解析 var variables = [String: Double]() for (index, value) in inputValues.enumerated() { variables["$\(index+1)"] = value } // 计算表达式 do { let expression = NSExpression(format: dbFormula) guard let result = expression.expressionValue(with: variables, context: nil) as? Double else { throw NSError(domain: "FormulaEval", code: 1, userInfo: [NSLocalizedDescriptionKey: "表达式计算失败"]) } print(result) // 输出 ((5*3)+2)*6 = 17*6 = 102 } catch { print(error.localizedDescription) }
方案2:自定义分支函数(适配复杂CASE场景)
如果多分支嵌套TERNARY可读性差,可以自定义CASE()函数,在代码中解析后替换为对应表达式再计算。
代码示例
extension NSExpression { static func evaluateCaseFormula(_ formula: String, with values: [Double]) throws -> Double { var processedFormula = formula // 替换带索引的占位符 for (index, value) in values.enumerated() { processedFormula = processedFormula.replacingOccurrences(of: "$\(index+1)", with: "\(value)") } // 解析CASE表达式:CASE(变量, 分支1值:分支1公式, 分支2值:分支2公式, default:默认公式) let caseRegex = try NSRegularExpression(pattern: "CASE\\((.*?),\\s*(.*?)\\)") guard let match = caseRegex.firstMatch(in: processedFormula, range: NSRange(processedFormula.startIndex..., in: processedFormula)) else { throw NSError(domain: "FormulaEval", code: 2, userInfo: [NSLocalizedDescriptionKey: "无效的CASE表达式"]) } guard let varRange = Range(match.range(at: 1), in: processedFormula), let branchesRange = Range(match.range(at: 2), in: processedFormula) else { throw NSError(domain: "FormulaEval", code: 3, userInfo: [NSLocalizedDescriptionKey: "CASE表达式格式错误"]) } let targetValue = try NSExpression(format: String(processedFormula[varRange])).expressionValue(with: nil, context: nil) as? Double ?? 0 let branchesStr = String(processedFormula[branchesRange]) let branches = branchesStr.components(separatedBy: ",") var selectedExprStr = "0" // 默认值 for branch in branches { let parts = branch.components(separatedBy: ":").map { $0.trimmingCharacters(in: .whitespaces) } guard parts.count == 2 else { continue } if parts[0] == "default" { selectedExprStr = parts[1] break } let branchValue = Double(parts[0]) ?? 0 if branchValue == targetValue { selectedExprStr = parts[1] break } } // 替换CASE为选中的表达式并计算 processedFormula.replaceSubrange(Range(match.range, in: processedFormula)!, with: selectedExprStr) let finalExpr = NSExpression(format: processedFormula) guard let result = try finalExpr.expressionValue(with: nil, context: nil) as? Double else { throw NSError(domain: "FormulaEval", code: 4, userInfo: [NSLocalizedDescriptionKey: "最终表达式计算失败"]) } return result } } // 用法示例 let caseFormula = "CASE($1, 1: (($2*$3)-$4)/$5, 2: (($2*$3)+$4)*$5, default: 0)" let inputValues = [1.0,5.0,3.0,2.0,6.0] do { let result = try NSExpression.evaluateCaseFormula(caseFormula, with: inputValues) print(result) // 输出 (15-2)/6 ≈2.1667 } catch { print(error.localizedDescription) }
方案3:替换为更灵活的表达式解析库
如果NSExpression的限制太多,可以使用Swift轻量开源表达式解析库,这类库原生支持分支、函数等复杂语法,直接解析数据库存储的公式即可,无需额外预处理。
注意事项
- 避免使用单个
$作为占位符,改用带索引的$1、$2,防止多变量替换时出现顺序错误。 - 存储公式时无需加引号,直接写原生表达式语法。
- 复杂嵌套分支优先使用方案2或3,保证可读性和可维护性。
内容的提问来源于stack exchange,提问作者Nicholas Farmer
相关产品推荐
相关产品推荐

