简体   繁体   English

Swift中的递归枚举

[英]Recursive Enumerations in Swift

I'm learning Swift 2 (and C, but also not for long) for not too long and I came to a point where I struggle a lot with recursive enumerations. 我正在学习Swift 2(和C,但也不会很久),并且我已经达到了一个让我在递归枚举中挣扎很多的地方。

It seems that I need to put indirect before the enum if it is recursive. 如果它是递归的,我似乎需要在enum之前放置indirect Then I have the first case which has Int between the parentheses because later in the switch it returns an Integer , is that right? 然后我有第一个在括号之间有Int情况,因为稍后在switch中它返回一个Integer ,是吗?

Now comes the first problem with the second case Addition . 现在是第二个案例Addition的第一个问题。 There I have to put ArithmeticExpression between the parentheses. 我必须在括号之间放置ArithmeticExpression I tried putting Int there but it gave me an error that is has to be an ArithmeticExpression instead of an Int . 我尝试将Int放在那里,但它给了我一个错误,必须是ArithmeticExpression而不是Int My question is why? 我的问题是为什么? I can't imagine anything what that is about. 我无法想象这是什么。 Why can't I just put two Int s there? 为什么我不能把两个Int放在那里?

The next problem is about ArithmeticExpression again. 下一个问题是关于ArithmeticExpression问题。 In the func solution it goes in an value called expression which is of the type ArithmeticExpression , is that correct? func solution它是一个名为expression的值,它是ArithmeticExpression类型,是正确的吗? The rest is, at least for now, completely clear. 其余的至少目前是完全清楚的。 If anyone could explain that to me in an easy way, that'd be great. 如果有人能够以一种简单的方式向我解释,那就太好了。

Here is the full code: 这是完整的代码:

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);

The reason the Addition case takes two ArithmeticExpression s instead of two Int s is so that it could handle recursive situations like this: Addition案例采用两个ArithmeticExpression而不是两个Int的原因是它可以处理这样的递归情况:

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

or, on more than one line: 或者,不止一行:

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

which represents: 代表:

(1 + 2) + 3

The recursive definition allows you to add not just numbers, but also other arithmetic expressions. 递归定义允许您不仅添加数字,还允许添加其他算术表达式。 That's where the power of this enum lies: it can express multiple nested addition operations. 这就是这个enum的力量所在:它可以表达多个嵌套的加法运算。

PeterPan, I sometimes think that examples that are TOO realistic confuse more than help as it's easy to get bogged down in trying to understand the example code. PeterPan,我有时认为过于现实的例子比帮助更令人困惑,因为在试图理解示例代码时很容易陷入困境。

A recursive enum is just an enum with associated values that are cases of the enum's own type. 递归枚举只是一个枚举,其关联值是枚举自身类型的情况。 That's it. 而已。 Just an enum with cases that can be set to associated values of the same type as the enum. 只是一个包含案例的枚举,可以设置为与枚举相同类型的关联值。 #endof #结束

Why is this a problem? 为什么这是个问题? And why the key word "indirect" instead of say "recursive"? 为什么关键词“间接”而不是说“递归”? Why the need for any keyword at all? 为什么需要任何关键字?

Enums are "supposed" to be copied by value which means they should have case associated values that are of predictable size - made up of cases with the basic types like Integer and so on. 枚举被“假定”按值复制,这​​意味着它们应该具有可预测大小的大小写相关的值 - 由具有基本类型(如整数等)的大小写组成。 The compiler can then guess the MAXIMUM possible size of a regular enum by the types of the raw or associated values with which it could be instantiated. 然后,编译器可以通过可以实例化的原始或关联值的类型来猜测常规枚举的最大可能大小。 After all you get an enum with only one of the cases selected - so whatever is the biggest option of the associated value types in the cases, that's the biggest size that enum type could get on initialisation. 毕竟你得到的枚举只有一个选择的案例 - 所以无论什么是案例中相关价值类型的最大选择,这是枚举类型在初始化时可以获得的最大尺寸。 The compiler can then set aside that amount of memory on the stack and know that any initialisation or re-assignment of that enum instance could never be bigger than that. 然后,编译器可以在堆栈上留出大量内存,并且知道该枚举实例的任何初始化或重新分配永远不会大于该大小。 If the user sets the enum to a case with a small size associated value it is OK, and also if the user sets it to a case with the biggest associated value type. 如果用户将枚举设置为具有小尺寸关联值的情况,则可以,并且如果用户将其设置为具有最大关联值类型的情况。

However as soon as you define an enum which has a mixture of cases with different sized associated types, including values that are also enums of the same type (and so could themselves be initialised with any of the enums cases) it becomes impossible to guess the maximum size of the enum instance. 但是,只要您定义一个具有不同大小相关类型的案例混合的枚举,包括也是相同类型枚举的值(因此可以使用任何枚举案例进行初始化),就无法猜测枚举实例的最大大小。 The user could keep initialising with a case that allows an associated value that is the same type as the enum - itself initialised with a case that is also the same type, and so on and so on: an endless recursion or tree of possibilities. 用户可以使用允许与枚举相同类型的关联值的情况进行初始化 - 自身也使用同样类型的情况初始化,依此类推:无限递归或可能性树。 This recursion of enums pointing to enums will continue until an enum is initialised with associated value of "simple" type that does not point to another enum. 指向枚举的枚举的递归将继续,直到枚举初始化为具有“简单”类型的相关值而不指向另一个枚举。 Think of a simple Integer type that would “terminate” the chain of enums. 想想一个简单的Integer类型,它将“终止”枚举链。

So the compiler cannot set aside the correct sized chunk of memory on the stack for this type of enum. 因此编译器无法为此类枚举在堆栈上留出正确大小的内存块。 Instead it treats the case associated values as POINTERS to the heap memory where the associated value is stored. 相反,它将案例关联值作为POINTERS处理到存储关联值的堆内存。 That enum can itself point to another enum and so on. 这个枚举本身可以指向另一个枚举,依此类推。 That is why the keyword is "indirect" - the associated value is referenced indirectly via a pointer and not directly by a value. 这就是关键字是“间接”的原因 - 关联值是通过指针间接引用的,而不是直接通过值引用的。

It is similar to passing an inout parameter to a function - instead of the compiler copying the value into the function, it passes a pointer to reference the original object in the heap memory. 它类似于将inout参数传递给函数 - 而不是将值复制到函数中的编译器,它传递一个指针来引用堆内存中的原始对象。

So that's all there is to it. 所以这就是它的全部。 An enum that cannot easily have its maximum size guessed at because it can be initialised with enums of the same type and unpredictable sizes in chains of unpredictable lengths. 一个不容易达到其最大大小的枚举,因为它可以使用相同类型的枚举和不可预测长度的链中不可预测的大小进行初始化。

As the various examples illustrate, a typical use for such an enum is where you want to build-up trees of values like a formula with nested calculations within parentheses, or an ancestry tree with nodes and branches all captured in one enum on initialisation. 正如各种示例所示,这种枚举的典型用法是,您希望在括号内构建具有嵌套计算的公式的值树,或者在初始化时在一个枚举中捕获具有节点和分支的祖先树。 The compiler copes with all this by using pointers to reference the associated value for the enum instead of a fixed chunk of memory on the stack. 编译器通过使用指针引用枚举的关联值而不是堆栈上的固定内存块来处理所有这些。

So basically - if you can think of a situation in your code where you want to have chains of enums pointing to each other, with various options for associated values - then you will use, and understand, a recursive enum! 所以基本上 - 如果你能想到你的代码中你想让枚举链指向彼此的情况,以及相关值的各种选项 - 那么你将使用并理解一个递归枚举!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM