请注意:
C:\> $x = @(1)
C:\> $x = @($x,2)
C:\> $x = @($x,3)
C:\> $x = @($x,4)
C:\> $x = @($x,5)
C:\> $x.Length
2
C:\> @($x |% { $_ }).Length
3
C:\> $x
Length : 2
LongLength : 2
Rank : 1
SyncRoot : {System.Object[] 2, 3}
IsReadOnly : False
IsFixedSize : True
IsSynchronized : False
Count : 2
4
5
C:\>
我希望管道能使列表变平。但这不会发生。我在做什么错了?
答案 0 :(得分:1)
回复:
我希望管道能使列表变平。但这不会发生。我在做什么错了?
实际上,当您键入$x
(最后一个命令)时,会发生一些变平的发生。请注意,前一个$x.Length
是如何给您2的,而$x
是给您3个元素的:
4
5
这表明发生了“一级展平” 。我希望您承认这算是扁平化。
现在,从对结果的不满意来看,您很想知道:
“为什么不一直走下去??”
我也想知道这种行为,我学到的是:
对脚本(至少包括脚本文件,脚本块,函数)的评估,如果将其结果提供给调用者,则会发生“一级”变平现象。 也就是说,如果脚本生成的数组中任何项目也都是数组,则这些项目的数组将在其位置(在同级之间)被串联。这种扁平化仅在顶级数组上发生一次,因此仅其直接的“子级数组”将被扁平化,而没有任何更深的子级数组。
看看您的示例,这种扁平化实际上发生在两个语句中:
C:\> $x
,当shell执行评估并返回3个项目时($x.Length
上没有发生,就像$x
评估那样)不屈服于调用外壳)@($x |% { $_ }).Length
还返回了3个项目。由于% { $_ }
是一个“脚本块”,它变得扁平了,它为调用者%
(本身是ForEach-Object
的别名)引入了新的求值方法和结果收益。没有其他收益,因为.Length
紧随其后,因此仅导致了一次平坦化。好吧,我找不到专门用于此行为的文档部分。我什至不确定如何查找它,因为...该机制将如何调用? “数组结果平整”?在这些关键字下找不到任何相关内容。我承认,鉴于隐式更改数据结构可能是一件非常危险的事情,因此没有在任何地方都显眼地记录此文档是令人惊讶的。
由于缺乏文档,我只能推测,但是考虑一下,它似乎内置在PowerShell中,实际上是其结构的一部分。总的来说,我认为通过减少所需的修改,可以更轻松地向现有代码添加更多行为。
要详细说明,请考虑某种语言必不可少的两个属性:
Write-Output
添加到返回数组。尤其需要这样做的原因是,shell脚本一直都在处理多个项目(显而易见,多行文本)。。
现在,如果不存在扁平化的行为,则将包含多个输出语句的一组语句提取到子例程中将意味着您突然得到一个数组,该数组以前只具有多个Write-Output
的平面。现在,如果您在原始顶级例程中没有任何输出(因为您只能return
数组)就没有问题,但是如果您仍然有多个Write-Output
,那么这就是问题在里面。需要创建一个数组变量并将每个Write-Output
更改为附加变量,或者需要在每个提取的子例程周围使用for
循环(因为ForEach-Object
,AKA %
或ForEach
,在其内部范围内运行。
也许针对此问题的另一种解决方案可能是使用特殊的运算符(例如JavaScript的yield*
)来返回数组的每个项目,但是PowerShell似乎选择了最少的运算符或最少的写操作来实现这一目标-您只需像往常一样Write-Output
整个数组(因此,运算符对单个项目的工作方式类似于JS yield
,对于数组自动为yield*
)。
我认为,某种程度上值得注意的是,这样做是以牺牲某些情况为代价的,这些情况实际上是人们不希望将数组展平的,我认为这考虑了PowerShell的极端情况。 This SO question是其中之一。有趣的是,答案实际上指向一种解决方法:它使用unary variant of the comma operator (,$someArrayItem)
在“也是数组的项目”周围创建了一个额外的数组,以使该数组变平,而不是内部的)
同样,如果没有文档,这只能是一个猜测。我能想到的是:
虽然“扁平化”看起来像是最常见的根本需要扁平化的情况所期望的行为,但这实际上是让人们处于困境中而没有任何简单的解决方法 >(添加嵌套级别的额外(,$someArrayItem)
将是徒劳的)。对于那些不需要扁平化的人来说,摆脱这种泡菜的唯一方法就是使用不同于数组的数据结构。这会有点昂贵。
这里有点题外话了:
它可能太如意了,但是如果需要的话,这种令人惊讶的行为并不是PowerShell无法摆脱的。例如,它可以引入某种永远不会自动展平的数组(仍然可以使用显式语句对其进行展平)。为此,顺便说一句,我觉得比较合适的是一个新的基于括号的运算符(类似于@(...)
,例如!(...)
),因为使用{{1 }}似乎有点奇怪(在某些语言中,例如JavaScript,(,$item)
表示长度为2的数组-未定义第一项)。