Swift - сопоставления по шаблону с соответствующими значениями с коммутативностью
Swift 5.0, Xcode 10.2.1
У меня есть Expression
перечисление в Swift.
enum Expression {
indirect case add(Expression, Expression)
indirect case subtract(Expression, Expression)
indirect case multiply(Expression, Expression)
indirect case divide(Expression, Expression)
indirect case power(Expression, Expression)
indirect case log(Expression, Expression)
indirect case root(Expression, Expression)
case x
case n(Int)
}
extension Expression: Equatable {
static func == (lhs: Expression, rhs: Expression) -> Bool {
switch (lhs, rhs) {
case let (.add(a, b), .add(c, d)) where a == c && b == d,
let (.subtract(a, b), .subtract(c, d)) where a == c && b == d,
let (.multiply(a, b), .multiply(c, d)) where a == c && b == d,
let (.divide(a, b), .divide(c, d)) where a == c && b == d,
let (.power(a, b), .power(c, d)) where a == c && b == d,
let (.log(a, b), .log(c, d)) where a == c && b == d,
let (.root(a, b), .root(c, d)) where a == c && b == d:
return true
case let (.n(a), .n(b)) where a == b:
return true
case (.x, .x):
return true
default:
return false
}
}
}
Первая попытка
Я выполняю множество сопоставлений с образцом для моего типа Expression
.Коммутативность сложения и умножения сделана для очень длинных выражений сопоставления с образцом.Я хотел найти способ упростить и сократить это, поэтому я решил создать перечисление ExpressionPattern
и определить перегрузку оператора сопоставления с образцом (~=
).
enum ExpressionPattern {
case commutativeMultiply(Expression, Expression)
case commutativeAdd(Expression, Expression)
}
func ~= (lhs: ExpressionPattern, rhs: Expression) -> Bool {
switch lhs {
case let .commutativeMultiply(a, b):
switch rhs {
case .multiply(a, b), .multiply(b, a):
return true
default:
return false
}
case let .commutativeAdd(a, b):
switch rhs {
case .add(a, b), .add(b, a):
return true
default:
return false
}
default:
return false
}
}
Я хочу бытьвозможность заменить операторы сопоставления с образцом, такие как:
case let .add(.n(3), a), let .add(a, .n(3)) where a > 10: //matches (a + 3), (3 + a)
//...
На:
case let .commutativeAdd(.n(3), a) where a > 10: //matches (a + 3), (3 + a)
//...
Когда я впервые попытался сделать это, я получил сообщение об ошибке «Связывание с образцом не можетпоявляются в выражении ".
Примечание: Я считаю, что это совпадение работает, если я использую точные, окончательные значения без привязки значений, но я использую эту функцию во многих местах моего проекта.
Попытка использования:
let expression: Expression = Expression.add(.divide(.n(1), .n(2)), .subtract(.n(3), .n(4)))
switch expression {
case let .commutativeAdd(.subtract(a, b), .divide(c, d)): //Error: Pattern variable binding cannot appear in an expression.
print("This matches: ((\(a) - \(b)) + (\(c) ÷ \(d))) and ((\(c) ÷ \(d) + (\(a) - \(b)))")
default:
break
}
Вторая попытка
Для второй попытки я изменил определение функции Expression
* ==
и попыталсяпереопределить реализацию по умолчанию ~=
.
extension Expression: Equatable {
static func == (lhs: Expression, rhs: Expression) -> Bool {
switch (lhs, rhs) {
case let (.add(a, b), .add(c, d)) where (a == c && b == d) || (a == d && b == c),
let (.subtract(a, b), .subtract(c, d)) where a == c && b == d,
let (.multiply(a, b), .multiply(c, d)) where (a == c && b == d) || (a == d && b == c),
let (.divide(a, b), .divide(c, d)) where a == c && b == d,
let (.power(a, b), .power(c, d)) where a == c && b == d,
let (.log(a, b), .log(c, d)) where a == c && b == d,
let (.root(a, b), .root(c, d)) where a == c && b == d:
return true
case let (.n(a), .n(b)) where a == b:
return true
case (.x, .x):
return true
default:
return false
}
}
static func ~= (lhs: Expression, rhs: Expression) -> Bool {
return lhs == rhs
}
}
Попытка использования:
let expression: Expression = Expression.add(.divide(.n(1), .n(2)), .subtract(.n(3), .n(4)))
print(expression == .add(.subtract(.n(3), .n(4)), .divide(.n(1), .n(2)))) //Prints "true"
switch expression {
case .add(.subtract(.n(3), .n(4)), .divide(.n(1), .n(2))):
print("Matched")
default:
print("Not matched")
}
//Prints "Not matched"
Примечание: Это должно было бы идеально напечатать "Matched".
Вопрос
Как мне найти способ использовать полноценныйсопоставление с образцом с учетом коммутативности определенных случаев.
Примечание: Это должно быть в состоянии использовать для сопоставления вложенных случаев с коммутативностью (то есть .add(.multiply(..., ...), ...)
).Это особенно важно, поскольку при сопоставлении чего-либо с сложением и умножением требуется гораздо больше случаев.