# 列表的列表

### 列表的列表

让我们再为层次结构添加一个层级。如果我们从原始示例中获取数据卡组并创建包含多个数据卡组的框，则该框现在表示一列数据卡组，每个数据卡组表示一列数据卡。这是一列列表。在本节的类比中，下图包含一列硬币卷，每个卷包含一列便士。

> 照片由 [Dori](https://commons.wikimedia.org/wiki/File:Stack_of_coins_0214.jpg) 拍摄。

### 查询

我们可以在该列列表中进行哪些**查询**？这将访问现有特性。

* 硬币类型的数量？2.
* 硬币类型值？$0.01 和 $0.25。
* 两角五分的材料？75% 铜和 25% 镍。
* 便士材料？97.5% 锌和 2.5% 铜。

### 操作

我们可以对该列列表执行哪些**操作**？这会根据给定的操作更改该列列表。

* 选择一叠特定的两角五分或便士。
* 选择一个特定的两角五分或便士。
* 重新排列各叠两角五分和便士。
* 将各叠堆叠在一起。

同样，Dynamo 为上述每项操作都提供了一个分析节点。由于我们处理的是抽象数据而不是物理对象，因此我们需要一组规则来控制如何上下移动数据层次结构。

在处理列表的列表时，数据是分层且复杂的，但这使得有机会执行一些很棒的参数化操作。让我们来分解基础知识，并在下面的课程中再讨论几项操作。

## 练习

### 自上而下层次结构

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

{% file src="<https://338946474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZkPl5tDHbu5X9VJTozDs%2Fuploads%2Fgit-blob-18b9ae372ae93c96592de4e6572ffebadc5ffea2%2FTop-Down-Hierarchy.dyn?alt=media>" %}

要从本部分中了解的基本概念：**Dynamo 将列表视为自身内部和自身的对象**。这种自上而下层次结构是在考虑面向对象编程的情况下开发的。Dynamo 会选择数据结构中主列表的索引，而不是使用诸如 **“List.GetItemAtIndex”** 之类的命令选择子元素。该项可以是另一个列表。让我们用示例图像进行分解：

\![top-down](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20top%20down%20hierachy.jpg)

> 1. 使用 **“代码块”**，我们已定义两个范围：`0..2; 0..3;`
> 2. 这些范围连接到 **“Point.ByCoordinates”** 节点，其中连缀设置为 *“叉积”*。这将创建点栅格，并且还会返回一列列表作为输出。
> 3. 请注意，**“Watch”** 节点提供 3 个列表，其中每个列表中有 4 个项目。
> 4. 使用 **“List.GetItemAtIndex”** 时，在索引为 0 的情况下，Dynamo 会选择第一个列表及其所有内容。其他程序可能会选择数据结构中每个列表的第一个项目，但 Dynamo 在处理数据时采用自上而下层次结构。

### List.Flatten

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

{% file src="<https://338946474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZkPl5tDHbu5X9VJTozDs%2Fuploads%2Fgit-blob-d7e3103007a2128fa758e5e9d987f0ccca2ec80b%2FFlatten.dyn?alt=media>" %}

“展平”会从数据结构中删除所有层级的数据。这在操作不需要数据层次结构时非常有用，但由于它会删除信息，因此可能存在风险。下面的示例显示了展平一列数据的结果。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20flatten%2001.jpg)

> 1. 在 **“代码块”** 中插入一行代码以定义一个范围：`-250..-150..#4;`
> 2. 通过将 *“代码块”* 插入到 **“Point.ByCoordinates”** 节点的 *“x”* 和 *“y”* 输入，我们会将连缀设置为 *“叉积”* 以获取点栅格。
> 3. **“Watch”** 节点显示我们有一列列表。
> 4. **“PolyCurve.ByPoints”** 节点将引用每个列表并创建相应的复合线。请注意，在 Dynamo 预览中，我们有四条复合线分别表示栅格中的每一行。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20flatten%2002.jpg)

> 1. 通过在复合线节点之前插入 *“展平”*，我们为所有点创建了一个列表。**“PolyCurve.ByPoints”** 节点引用列表来创建一条曲线，并且由于所有点都在一个列表中，因此我们得到了一条锯齿形复合线，该复合线贯穿整个点列表。

还有用于展平孤立层级数据的选项。使用 **“List.Flatten”** 节点，可以定义一定数量的数据层级，以从层次结构的顶部展平。如果要处理的复杂数据结构不一定与工作流相关，那么这是一个非常有用的工具。另一个选择是在 **“List.Map”** 中将展平节点用作函数。我们将在下面详细介绍 **“List.Map”**。

### 切除

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

{% file src="<https://338946474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZkPl5tDHbu5X9VJTozDs%2Fuploads%2Fgit-blob-7ce55ab7dd93389724ac13a6be49f709ff27998d%2FChop.dyn?alt=media>" %}

使用参数化建模时，有时您还希望将修改现有列表的数据结构。此外，还有许多节点可用于此操作，其中最基本的版本是“切除”。使用“切除”，我们可以将一个列表划分为具有一定数量项目的子列表。

“切除”命令会根据给定的列表长度来分割列表。在某些方面，切除与展平相反：它不是删除数据结构，而是向其中添加新的层级。这是一个有助于执行几何操作（如以下示例）的工具。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20chop.jpg)

### List.Map

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

{% file src="<https://338946474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZkPl5tDHbu5X9VJTozDs%2Fuploads%2Fgit-blob-57c4b500460eef5ae902315dfa6922d97e1e74f7%2FMap.dyn?alt=media>" %}

**“List.Map/Combine”** 会将设置的函数应用于输入列表，但在层次结构中向下一步。组合与贴图相同，但组合可以具有与给定函数的输入相对应的多个输入。

*注意：此练习是使用 Dynamo 的先前版本创建的。通过添加***“List\@Level”***功能，***“List.Map”***的大部分功能已经解决。有关详细信息，请参见 下面 的*[*“List@Level”*](#lists-of-lists)。

通过简单介绍一下，我们来回顾上一节中的 **“List.Count”** 节点。

**“List.Count”** 节点对列表中的所有项目进行计数。我们将使用此节点来演示 **“List.Map”** 如何工作。

!

> 1. 将两行代码插入到 **“代码块”**：`-50..50..#Nx; -50..50..#Ny;`
>
>    在键入此代码后，代码块将为 Nx 和 Ny 创建两个输入。
> 2. 使用两个 *整数滑块*，通过将它们连接到 **“代码块”** 来定义 *“Nx”* 和 *“Ny”* 值。
> 3. 将代码块的每行连接到 **“Point.ByCoordinates”** 节点的相应 *“X”* 和 *“Y”* 输入。在节点上单击鼠标右键，选择“连缀”，然后选择 *“叉积”*。这将创建点栅格。因为我们定义的范围是 -50 到 50，所以我们跨越了默认的 Dynamo 栅格。
> 4. ***“Watch”*** 节点显示所创建的点。请注意数据结构。我们已创建一列列表。每个列表都表示栅格的一行点。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20map%2002.jpg)

> 1. 将上一步中的 **“List.Count”** 节点附加到“Watch”节点的输出。
> 2. 将 **“Watch”** 节点连接到 **“List.Count”** 输出。

请注意，“List.Count”节点提供的值为“5”。这等于代码块中所定义的“Nx”变量。这是为什么呢？

* 首先，**“Point.ByCoordinates”** 节点使用“x”输入作为用于创建列表的主输入。当 Nx 为 5 且 Ny 为 3 时，我们得到一列 5 个列表，每个列表都包含 3 个项目。
* 由于 Dynamo 将列表视为自身内部和自身的对象，因此 **“List.Count”** 节点会应用于层次结构中的主列表。结果为值 5 或主列表中列表的数量。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20map%2003.jpg)

> 1. 通过使用 **“List.Map”** 节点，我们在层次结构中向下一步，然后在此级别上执行 *“函数”*。
> 2. 请注意，**“List.Count”** 节点没有输入。它将用作一个函数，因此 **“List.Count”** 节点将应用于层次结构中向下一步的每个单独列表。**“List.Count”** 的空白输入对应于 **“List.Map”** 的列表输入。
> 3. **“List.Count”** 的结果现在提供一列 5 个项目，每个项目的值为 3。这表示每个子列表的长度。

### **List.Combine**

*注意：此练习是使用 Dynamo 的先前版本创建的。通过添加***“List\@Level”***功能，“List.Combine”的大部分功能已经解决。有关详细信息，请参见下面的*[*“List@Level”*](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/5_essential_nodes_and_concepts/5-4_designing-with-lists/6-3_lists-of-lists.md#listlevel)*。*

在本练习中，我们将使用 **“List.Combine”** 演示如何使用它来在单独的对象列表中应用函数。

首先，设置两个点列表。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20combined%2001.jpg)

> 1. 使用 **“Sequence”** 节点生成 10 个值，每个值都有 10 步增量。
> 2. 将结果连接到 **“Point.ByCoordinates”** 节点的 x 输入。这将在 Dynamo 中创建点列表。
> 3. 将第二个 **“Point.ByCoordinates”** 节点添加到工作空间、使用相同的 **“Sequence”** 输出作为其 x 输入，但使用 **“Interger Slider”** 作为其 y 输入并将其值设置为 31（它可以是任何值，只要它们不与第一组点重叠），这样 2 组点就不会相互重叠。

接下来，我们将使用 **“List.Combine”** 对 2 个单独列表中的对象应用函数。在本例中，它将是一个简单的绘制线函数。

\![Exercise](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20combined%2002.jpg)

> 1. 将 **“List.Combine”** 添加到工作空间，并连接 2 组点作为其 list0 和 list1 输入。
> 2. 使用 **“Line.ByStartPointEndPoint”** 作为 **“List.Combine”** 的输入函数。

完成后，通过 **“Line.ByStartPointEndPoint”** 函数将 2 组点压缩/成对组合在一起，并在 Dynamo 中返回 10 行。

{% hint style="info" %}
请参见 n 维列表中的练习，以查看使用“List.Combine”的另一个示例。
{% endhint %}

### List\@Level

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

{% file src="<https://338946474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZkPl5tDHbu5X9VJTozDs%2Fuploads%2Fgit-blob-c42e43aa83bda508aa4ff595f7a658440c80c992%2FListatlevel.dyn?alt=media>" %}

相较于 **“List.Map”**，**“List\@Level”** 功能允许直接选择要在节点的输入端口上使用的列表级别。此功能可应用于节点的任何传入输入，并让您可以更快、更容易地访问列表的各级别（相较于其他方法）。只需告诉节点要使用列表的哪个级别作为输入，然后让节点执行其余操作即可。

在本练习中，我们将使用 **“List\@Level”** 功能隔离特定级别的数据。

\![List@Level](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20list%20at%20level%2001.jpg)

我们将从一个简单的三维点栅格开始。

> 1. 该栅格使用 X、Y 和 Z 范围构建，因此我们知道数据结构由 3 个层级组成：X 列表、Y 列表和 Z 列表。
> 2. 这些层级存在于不同的**级别**上。级别显示在预览气泡的底部。“列表级别”列与上述列表数据相对应，以帮助确定要在哪个级别工作。
> 3. “列表级别”按相反顺序进行组织，以便最低级别数据始终位于“L1”。这将有助于确保图形按计划工作，即使上游发生任何更改也是如此。

\![List@Level](https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/.gitbook/assets/lists%20of%20lists%20-%20list%20at%20level%2002.jpg)

> 1. 要使用 **“List\@Level”** 函数，请单击“>”。在此菜单中，您会看到两个复选框。
> 2. **使用级别** - 这会启用 **“List\@Level”** 功能。单击此选项后，将能够点进，然后选择希望节点使用的输入列表级别。使用此菜单，可以通过单击上下箭头来快速试用不同的级别选项。
> 3. *保持列表结构* - 如果已启用，则可以选择保持该输入的级别结构。有时，您可能会有目的地将数据组织到子列表中。选中此选项，即可使列表组织保持完整，而不会丢失任何信息。

使用简单的三维栅格，我们可以通过切换“列表级别”来访问和可视化列表结构。每个列表级别和索引组合将从原始三维集中返回一组不同的点。

!

> 1. DesignScript 中的“@L2”允许我们仅选择级别 2 的列表。级别 2 列表（索引为 0）仅包含第一组 Y 点，从而仅返回 XZ 栅格。
> 2. 如果将级别过滤器更改为“L1”，则我们将能够看到第一列表级别中的所有内容。级别 1 列表（索引 0）将所有三维点都包括在一个展平列表中。
> 3. 如果我们尝试对“L3”执行相同操作，则我们仅会看到第三列表级别点。级别 3 列表（索引为 0）仅包含第一组 Z 点，从而仅返回 XY 栅格。
> 4. 如果我们尝试对“L4”执行相同操作，则我们仅会看到第三列表级别点。级别 4 列表（索引为 0）仅包含第一组 X 点，从而仅返回 YZ 栅格。

尽管也可以使用 **“List.Map”** 创建此特定示例，但 **“List\@Level”** 会极大地简化交互，从而可以轻松访问节点数据。请在下面查看 **“List.Map”** 和 **“List\@Level”** 方法的比较：

!

> 1. 尽管这两种方法都将允许我们访问相同的点，但 **“List\@Level”** 方法允许我们轻松地在一个节点内的数据层之间切换。
> 2. 要使用 **“List.Map”** 访问点栅格，我们需要使用 **“List.GetItemAtIndex”** 节点以及 **“List.Map”**。对于我们要向下步进的每个列表级别，我们需要使用额外的 **“List.Map”** 节点。根据列表的复杂程度，这可能需要将大量 **“List.Map”** 节点添加到图形中，才能访问正确级别的信息。
> 3. 在此示例中，**“List.GetItemAtIndex”** 节点与 **“List.Map”** 节点结合使用会返回列表结构与 **“List.GetItemAtIndex”**（在选择“@L3”的情况下）相同的一组相同点。

### Transpose

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

“Transpose”是一个处理列表的列表时的基本函数。与在电子表格程序中一样，转置会翻转数据结构的列和行。我们将在下面的基本矩阵中对此进行演示，并且在以下部分中我们将演示如何使用转置来创建几何关系。

让我们从上一个练习中删除 **“List.Count”** 节点，然后转到一些几何图形以查看数据的结构。

!

> 1. 从 **“Point.ByCoordinates”** 将 **“PolyCurve.ByPoints”** 连接到“Watch”节点的输出。
> 2. 输出会显示 5 条复合线，我们可以在 Dynamo 预览中看到这些曲线。Dynamo 节点将查找一列点（或在本例中为一列点列表）并基于它们创建一条复合线。实际上，每个列表已转换为数据结构中的曲线。

!

> 1. **“List.Transpose”** 节点将切换一列列表中所有列表的所有项目。这听起来很复杂，但其逻辑与 Microsoft Excel 中的“转置”相同：在数据结构中切换列和行。
> 2. 请注意抽象结果：“转置”将列表结构从 5 列（每列 3 个项目）更改为 3 列（每列 5 个项目）。
> 3. 请注意几何结果：使用 **“PolyCurve.ByPoints”**，我们在与原始曲线的垂直方向上获得 3 条复合线。

## 用于创建列表的代码块

代码块简写使用“\[]”来定义列表。与 **“List.Create”** 节点相比，这种方法可更快、更流畅地创建列表。**代码块**会在[“代码块和 DesignScript”](https://primer2.dynamobim.org/zh-cn/8_coding_in_dynamo/8-1_code-blocks-and-design-script)中进行更详细的介绍。请参照下图，以注意如何使用代码块定义具有多个表达式的列表。

!

#### 代码块查询

**代码块**简写使用“\[]”作为要从复杂数据结构中选择所需特定项目的快速简便方法。**代码块**会在[“代码块和 DesignScript”](https://primer2.dynamobim.org/zh-cn/8_coding_in_dynamo/8-1_code-blocks-and-design-script)一章中进行更详细的介绍。请参照下图，以注意如何使用代码块查询具有多个数据类型的列表。

!

## 练习 - 查询和插入数据

> 单击下面的链接下载示例文件。
>
> 可以在附录中找到示例文件的完整列表。

本练习使用在上一个练习中建立的某些逻辑来编辑曲面。我们在此处的目标一目了然，但将更多涉及数据结构导航。我们希望通过移动控制点来接合曲面。

从上述节点字符串开始。我们将创建一个跨越默认 Dynamo 栅格的基本曲面。

!

> 1. 使用 **“代码块”**，插入以下两行代码并分别连接到 **“Surface.PointAtParameter”** 的 *“u”* 和 *“v”* 输入：`-50..50..#3;` `-50..50..#5;`
> 2. 确保将 **“Surface.PointAtParameter”** 的“连缀”设置为 *“叉积”*。
> 3. **“Watch”** 节点显示我们有一列 3 个列表（每个列表都含有 5 个项目）。

在此步骤中，我们要在已创建的栅格中查询中心点。为此，我们将选择中间列表中的中间点。有道理，对吧？

!

> 1. 要确认这一点是正确的，我们还可以单击“Watch”节点项目来确认我们面向的目标是正确的。
> 2. 使用 **“代码块”**，我们将编写一行基本代码，用于查询一列列表：\
>    `points[1][2];`
> 3. 使用 **“Geometry.Translate”**，我们会将选定点在 *Z* 方向上上移 *20* 个单位。

!

> 1. 我们还要使用 **“List.GetItemAtIndex”** 节点选择中间行的点。注意：与上一步类似，我们还可以使用 **“代码块”**，通过一行代码 `points[1];` 查询列表

到目前为止，我们已成功查询中心点并将其向上移动。现在，我们需要将此移动的点插回原始数据结构。

!

> 1. 首先，我们要替换在上一步中隔离的列表项。
> 2. 使用 **“List.ReplaceItemAtIndex”**，我们会将使用且索引为 *“2”* 的中间项替换为与移动的点（ **“Geometry.Translate”** ）相连的替换项。
> 3. 输出显示我们已将移动的点输入到列表的中间项。

现在，我们已经修改了列表，我们需要将此列表插回原始数据结构：列表的列表。

!

> 1. 遵循相同的逻辑，使用 **“List.ReplaceItemAtIndex”** 将中间列表替换为我们修改的列表。
> 2. 请注意，为这两个节点定义索引的 “**代码块***”* 为 1 和 2，这与 **“代码块”** (*points\[1]\[2]*) 中的原始查询匹配。
> 3. 通过选择 *“索引 1”* 处的列表，我们会看到数据结构在 Dynamo 预览中亮显。我们已成功将移动的点合并到原始数据结构中。

基于这组点生成曲面的方法有多种。在本例中，我们将通过一起放样曲线来创建曲面。

!

> 1. 创建 **“NurbsCurve.ByPoints”** 节点并连接新的数据结构，来创建三条 NURBS 曲线。

!

> 1. 将 **“Surface.ByLoft”** 连接到 **“NurbsCurve.ByPoints”** 的输出。现在，我们得到了一个修改的曲面。我们可以更改几何图形的原始 *Z* 值。平移并观察几何图形更新！
