Swift 中的递归枚举

Recursive Enumerations in Swift

我学习 Swift 2(和 C,但也不会学太久)的时间不长,而且我在递归枚举方面遇到了很多困难。

如果是递归的,好像我需要把indirect放在enum之前。然后我有第一个在括号之间有 Int 的情况,因为稍后在 switch 中它 returns 和 Integer,对吗?

第二个案例的第一个问题出现了Addition。我必须把 ArithmeticExpression 放在括号之间。我尝试将 Int 放在那里,但它给了我一个必须是 ArithmeticExpression 而不是 Int 的错误。我的问题是为什么?我无法想象那是什么。为什么我不能只放两个 Int 在那里?

下一个问题又是关于ArithmeticExpression的。在 func solution 中,它包含一个名为 expression 的值,该值属于 ArithmeticExpression 类型,对吗?其余的,至少现在,是完全清楚的。如果有人能用简单的方式向我解释一下,那就太好了。

完整代码如下:

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

func solution(expression: ArithmeticExpression) -> Int {
    switch expression {
    case .Number(let value1):
        return value1;
    case . Addition(let value1, let value2):
        return solution(value1)+solution(value2);
    }
}

var ten = ArithmeticExpression.Number(10);
var twenty = ArithmeticExpression.Number(20);
var sum = ArithmeticExpression.Addition(ten, twenty);
var endSolution = solution(sum);
print(endSolution);

Addition 案例需要两个 ArithmeticExpression 而不是两个 Int 的原因是它可以处理这样的递归情况:

ArithmeticExpression.Addition(ArithmeticExpression.Addition(ArithmeticExpression.Number(1), ArithmeticExpression.Number(2)), ArithmeticExpression.Number(3))

或者,不止一行:

let addition1 = ArithmeticExpression.Addition(ArithmeticExpression.Number(1), ArithmeticExpression.Number(2))
let addition2 = ArithmeticExpression.Addition(addition1, ArithmeticExpression.Number(3))

代表:

(1 + 2) + 3

递归定义不仅可以添加数字,还可以添加其他算术表达式。这就是这个 enum 的强大之处:它可以表达多个嵌套的加法运算。

PeterPan,我有时认为过于逼真的示例只会带来混淆而不是帮助,因为在尝试理解示例代码时很容易陷入困境。

递归枚举只是一个具有关联值的枚举,这些值是枚举自身类型的情况。而已。只是一个枚举,其中的案例可以设置为与枚举类型相同的关联值。 #结束

为什么这是个问题?为什么关键字 "indirect" 而不是 "recursive"?为什么需要任何关键字?

枚举 "supposed" 按值复制,这​​意味着它们应该具有大小写相关的大小写关联值 - 由具有 Integer 等基本类型的大小写组成。然后,编译器可以根据可以实例化它的原始值或关联值的类型来猜测常规枚举的最大可能大小。毕竟你得到的枚举只选择了一个案例——所以无论案例中关联值类型的最大选项是什么,这就是枚举类型在初始化时可以获得的最大大小。然后,编译器可以在堆栈上留出该数量的内存,并知道该枚举实例的任何初始化或重新分配永远不会大于该内存量。如果用户将枚举设置为关联值较小的案例,则可以,如果用户将其设置为关联值类型最大的案例。

但是,一旦您定义了一个枚举,它混合了具有不同大小关联类型的案例,包括也是相同类型枚举的值(因此它们本身可以用任何枚举案例初始化)它变得不可能猜测枚举实例的最大大小。用户可以继续使用允许与枚举类型相同的关联值的情况进行初始化 - 本身使用也是相同类型的情况进行初始化,依此类推:无限递归或可能性树。这种指向枚举的枚举的递归将继续,直到使用 "simple" 类型的关联值初始化一个不指向另一个枚举的枚举。想一个简单的 Integer 类型,它将“终止”枚举链。

因此编译器无法在堆栈上为此类枚举留出正确大小的内存块。相反,它将大小写关联值视为指向存储关联值的堆内存的指针。该枚举本身可以指向另一个枚举,依此类推。这就是为什么关键字是 "indirect" - 关联值是通过指针间接引用的,而不是直接由值引用的。

它类似于将 inout 参数传递给函数 - 编译器不是将值复制到函数中,而是传递一个指针来引用堆内存中的原始对象。

就这些了。一个无法轻易猜测其最大大小的枚举,因为它可以在不可预测长度的链中使用相同类型和不可预测大小的枚举进行初始化。

如各种示例所示,此类枚举的典型用途是构建值树,例如在括号内嵌套计算的公式,或者节点和分支都包含在一个树中的祖先树初始化枚举。编译器通过使用指针来引用枚举的关联值而不是堆栈上的固定内存块来处理所有这些问题。

所以基本上 - 如果您能想到代码中的一种情况,您希望枚举链相互指向彼此,并为关联值提供各种选项 - 那么您将使用并理解递归枚举!