Рекурсивные перечисления

Перечисления отлично справляются с моделированием данных, когда есть всего несколько вариантов, которые нужно рассмотреть, как например простые арифметические операции, которые мы используем с двумя простыми целыми числами. Эти операции помогают комбинировать простые арифметические выражения, которые состоят из чисел, как например 4 + 4.

Одна важная характеристика арифметических выражений то, что они могут быть вложенными. Например, выражение (5 + 4) * 2 имеет число справа от умножения и выражение слева от умножения. Так как данные у нас вложенные, значит перечислению для хранения так же нужно поддерживать вложенность, что значит, что перечисления должны быть рекурсивными.

Рекурсивное перечисление - перечисление, которое имеет другой экземпляр перечисления в качестве ассоциативного значения для одного или более членов перечисления. Компилятору нужно вложить индиректный слой, который позволит работать с рекурсивными перечислениями. Чтобы обозначить член перечисления в качестве рекурсивного, вам нужно написать indirect до него.

Например, ниже объявлено перечисление, которое хранит простые арифметические выражения:

enum ArithmeticExpression {
  case Number(Int)
  indirect case Addition(ArithmeticExpression, ArithmeticExpression)
  indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
}

Вы так же можете написать indirect прямо перед самим перечислением, что позволит обозначить то, что все члены перечисления поддерживают индиректность:

indirect enum ArithmeticExpression {
  case Number(Int)
  case Addition(ArithmeticExpression, ArithmeticExpression)
  case Multiplication(ArithmeticExpression, ArithmeticExpression)
}

Перечисление может хранить три вида арифметических выражений: простое число, сложение двух выражений, умножение двух выражений. Члены Addition и Multiplication имеют два связанных значения, которые так же являются арифметическими выражениями. Эти связанные значения делают возможным вложение выражений.

Рекурсивные функции - самый простой путь работать с данными, которые имеют рекурсивную структуру. Например, ниже приведен пример, как функция вычисляет арифметическое выражение:

func evaluate(expression: ArithmeticExpression) -> Int {
  switch expression {
  case .Number(let value):
    return value
  case .Addition(let left, let right):
    return evaluate(left) + evaluate(right)
  case .Multiplication(let left, let right):
    return evaluate(left) * evaluate(right)
  }
}

// evaluate (5 + 4) * 2
let five = ArithmeticExpression.Number(5)
let four = ArithmeticExpression.Number(4)
let sum = ArithmeticExpression.Addition(five, four)
let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))

print(evaluate(product))
// выводит "18"

Эта функция вычисляет простое число, ростов возвращая ассоциативное значение. Она вычисляет сложение или умножение, вычисляя выражение по левую сторону, затем по правую сторону, затем складывает или умножает их.