[英]Can I force NSExpression and expressionValue to assume Doubles instead of Ints somehow?
I'm trying to do math from a string. 我正在尝试用字符串做数学。
When I turn a string into a math problem with NSExpression, and then get the result with expressionValue, Swift assumes I want an Integer. 当我使用NSExpression将字符串转换为数学问题,然后使用expressionValue获取结果时,Swift假设我想要一个整数。 Consider these two Playground examples:
考虑这两个Playground示例:
let currentCalculation = "10 / 6"
let currentExpression = NSExpression(format: currentCalculation)
print(currentExpression) // 10 / 6
if let result = currentExpression.expressionValue(with: nil, context: nil) as? Double {
print(result) // 1
}
let anotherCalculation = "10.0 / 6.0"
let anotherExpression = NSExpression(format: anotherCalculation)
print(anotherExpression) // 10 / 6
if let result = anotherExpression.expressionValue(with: nil, context: nil) as? Double {
print(result) // 1.666666667
}
What should I be doing so that I always get a Double as a result? 我应该怎么做才能让我总是得到一个Double? I don't want to have to parse the string ahead of time.
我不想提前解析字符串。
Pretty interesting that the second example turns "anotherExpression" into Integers, yet still returns a Double as a result. 非常有趣的是,第二个示例将“anotherExpression”转换为Integers,但仍然返回Double作为结果。
You might be better off using a 3rd party expression parser/evaluator, such as DDMathParser . 您可能最好使用第三方表达式解析器/评估程序,例如DDMathParser 。
NSExpression
is quite limited, and has no options to force floating point evaluation. NSExpression
非常有限,没有强制浮点评估的选项。
If you want to (or have to) stick to NSExpression
: Here is a possible solution to (recursively) replace all constant values in an expression by their floating point value: 如果你想(或必须)坚持
NSExpression
:这是一个可能的解决方案(递归地)用它们的浮点值替换表达式中的所有常量值:
extension NSExpression {
func toFloatingPoint() -> NSExpression {
switch expressionType {
case .constantValue:
if let value = constantValue as? NSNumber {
return NSExpression(forConstantValue: NSNumber(value: value.doubleValue))
}
case .function:
let newArgs = arguments.map { $0.map { $0.toFloatingPoint() } }
return NSExpression(forFunction: operand, selectorName: function, arguments: newArgs)
case .conditional:
return NSExpression(forConditional: predicate, trueExpression: self.true.toFloatingPoint(), falseExpression: self.false.toFloatingPoint())
case .unionSet:
return NSExpression(forUnionSet: left.toFloatingPoint(), with: right.toFloatingPoint())
case .intersectSet:
return NSExpression(forIntersectSet: left.toFloatingPoint(), with: right.toFloatingPoint())
case .minusSet:
return NSExpression(forMinusSet: left.toFloatingPoint(), with: right.toFloatingPoint())
case .subquery:
if let subQuery = collection as? NSExpression {
return NSExpression(forSubquery: subQuery.toFloatingPoint(), usingIteratorVariable: variable, predicate: predicate)
}
case .aggregate:
if let subExpressions = collection as? [NSExpression] {
return NSExpression(forAggregate: subExpressions.map { $0.toFloatingPoint() })
}
case .anyKey:
fatalError("anyKey not yet implemented")
case .block:
fatalError("block not yet implemented")
case .evaluatedObject, .variable, .keyPath:
break // Nothing to do here
}
return self
}
}
Example: 例:
let expression = NSExpression(format: "10/6+3/4")
if let result = expression.toFloatingPoint().expressionValue(with: nil, context: nil) as? Double {
print("result:", result) // 2.41666666666667
}
This works with "simple" expressions using arithmetic operators and functions and some "advanced" expression types (unions, intersections, ...). 这适用于使用算术运算符和函数的“简单”表达式以及一些“高级”表达式类型(联合,交集,......)。 The remaining conversions can be added if necessary.
如有必要,可以添加剩余的转化次数。
Here's a variant of Martin R's great answer that has two important changes: 这是Martin R的一个很好的答案,有两个重要的变化:
count({1,2,3,4,5}) / count({1,2})
where the arguments to division aren't constant values. count({1,2,3,4,5}) / count({1,2})
这样的表达式,其中除法的参数不是常量值。 Code: 码:
import Foundation
extension NSExpression {
func toFloatingPointDivision() -> NSExpression {
switch expressionType {
case .function where function == "divide:by:":
guard let args = arguments else { break }
let newArgs = args.map({ arg -> NSExpression in
if arg.expressionType == .constantValue {
if let value = arg.constantValue as? Double {
return NSExpression(forConstantValue: value)
} else {
return arg
}
} else {
return NSExpression(block: { (object, arguments, context) in
// NB: The type of `+[NSExpression expressionForBlock:arguments]` is incorrect.
// It claims the arguments is an array of NSExpressions, but it's not, it's
// actually an array of the evaluated values. We can work around this by going
// through NSArray.
guard let arg = (arguments as NSArray).firstObject else { return NSNull() }
return (arg as? Double) ?? arg
}, arguments: [arg.toFloatingPointDivision()])
}
})
return NSExpression(forFunction: operand, selectorName: function, arguments: newArgs)
case .function:
guard let args = arguments else { break }
let newArgs = args.map({ $0.toFloatingPointDivision() })
return NSExpression(forFunction: operand, selectorName: function, arguments: newArgs)
case .conditional:
return NSExpression(forConditional: predicate,
trueExpression: self.true.toFloatingPointDivision(),
falseExpression: self.false.toFloatingPointDivision())
case .unionSet:
return NSExpression(forUnionSet: left.toFloatingPointDivision(), with: right.toFloatingPointDivision())
case .intersectSet:
return NSExpression(forIntersectSet: left.toFloatingPointDivision(), with: right.toFloatingPointDivision())
case .minusSet:
return NSExpression(forMinusSet: left.toFloatingPointDivision(), with: right.toFloatingPointDivision())
case .subquery:
if let subQuery = collection as? NSExpression {
return NSExpression(forSubquery: subQuery.toFloatingPointDivision(), usingIteratorVariable: variable, predicate: predicate)
}
case .aggregate:
if let subExpressions = collection as? [NSExpression] {
return NSExpression(forAggregate: subExpressions.map({ $0.toFloatingPointDivision() }))
}
case .block:
guard let args = arguments else { break }
let newArgs = args.map({ $0.toFloatingPointDivision() })
return NSExpression(block: expressionBlock, arguments: newArgs)
case .constantValue, .anyKey:
break // Nothing to do here
case .evaluatedObject, .variable, .keyPath:
// FIXME: These should probably be wrapped in blocks like the one
// used in the `.function` case.
break
}
return self
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.