【问题标题】:What is the idiomatic way to slice an array relative to both of its ends?相对于其两端对数组进行切片的惯用方法是什么?
【发布时间】:2015-04-12 04:04:29
【问题描述】:

Powershell 的数组表示法在对数组末尾进行切片时具有相当奇怪的行为,尽管已记录在案。来自the official documentation 的这一部分很好地总结了这种奇异性:

负数从数组末尾开始计数。例如,“-1” 指的是数组的最后一个元素。显示最后三个 数组元素,类型:

$a[-3..-1]

但是,使用这种表示法时要小心。

$a[0..-2]

这个命令并不引用数组的所有元素,除了 最后一个。它指的是第一个、最后一个和倒数第二个 数组中的元素。

下面的代码证实了这个离奇:

$a = 0,1,2,3
$a[1..-1]

这确实输出了这个奇怪的结果:

1
0
3

所以,问题是,用一个相对于数组的开头和另一个相对于数组结尾的索引进行切片的惯用方式?

请告诉我这比这个丑陋的烂摊子更好:

$a[1..($a.Count-1)]

编辑:

描述我正在寻找的另一种方式是:这个 Python 表达式的惯用 Powershell 等效项:

a=1,2,3,4
a[1:-1]

当然,计算结果为(2,3)

【问题讨论】:

  • 我相信一个人认为奇怪或奇怪的东西在另一个人看来可能完全正常。举个例子,我通读了你的 sn-ps 没有阅读你的理由——只是为了看看我如何自然地解释表达式——而 Powershell 的行为方式似乎与我的预期一致。只是说 - 意见会有所不同。总是。
  • @amn 您的评论也促使我重新阅读。事实上,PowerShell 的行为方式与我现在所期望的一致。显然,即使我与五年前的我不同,我的看法也不同。
  • 我想我知道你的感受。 Powershell 解析器一个接一个地向我投掷曲线球,好像在告诉我我永远不会理解 Powershell 的工作原理,但至少我打败了它,或者感觉如此。还有一些我有点不确定的怪癖——数组文字(是的,我读过this piece),举个例子——但解析器似乎或多或少地被消化了。是的,老观点——哎呀,我经常对我之前的问题、答案和 cmets 感到畏缩:/ 希望这不会是其中之一!
  • 我同意旧 OP,这不是切片运算符,这是一个奇怪的结果。但不像以前的我,我知道“..”是一个范围生成器,所以它确实有意义。我只是对 Powershell 没有数组切片运算符感到失望。

标签: arrays powershell slice idioms language-comparisons


【解决方案1】:

结合Select-Object -SkipSelect-Object -SkipLast 喜欢:

$a = 0,1,2,3
$a | Select-Object -Skip 1 | Select-Object -SkipLast 1

返回:

1
2

不如 Python 优雅,但至少您不必使用 CountLength,这意味着如果数组未存储在变量中,这也可以。

【讨论】:

    【解决方案2】:

    $arr = @(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    $arr | Select-Object -First 5 | Select-Object -Index (@(0..4) | Where-Object { $_ % 2 -eq 0}) 
    $arr | Select-Object -Last 5
    $arr | Select-Object -Unique
    $arr | Sort-Object | Select-Object -Unique
    $arr | Where-Object {  $_ % 5 -eq 0 } | Sort-Object | Select-Object -Unique
    $arr | Select-Object -First ($arr.Count - 3)
    

    实际上,代码不言自明。 我事件不需要解释。

    然而, 1) 提供前五个元素,但提供这五个元素中的每一个。等于 Python 中的 arr[:5:2] 2)获取最后五个元素。 3) 赋予独特的元素 4) 先排序后提供唯一性 5) 通过应用 5 的模,排序,唯一的,只给出等于 0 的元素。 5) 仅提供该数组中的第一个元素数减去三个元素。

    【讨论】:

    • 这里有一个解释。
    【解决方案3】:

    如果您想从数组末尾获取 n 个元素,只需将元素从 -n 获取到 -1: p> PS C:\> $a = 0,1,2,3 PS C:\> $n = 2 PS C:\> $a[-$n..-1] 2 3

    编辑: 由于 $a[$i..$j] 的工作方式,PowerShell 不支持相对于数组的开头 结尾的索引。在 Python 表达式 a[i:j] 中,您分别指定 ij 作为第一个和最后一个索引。但是,在 PowerShell 中 ..range operator,它会生成一个数字序列。在表达式$a[$i..$j] 中,解释器首先将$i..$j 计算为一个整数列表,然后该列表用于检索这些索引上的数组元素:

    PS C:\> $a = 0,1,2,3 PS C:\> $i = 1; $j = -1 PS C:\> $index = $i..$j PS C:\> $index 1 0 -1 PS C:\> $a[$index] 1 0 3

    如果您需要模拟 Python 的行为,您必须使用子表达式:

    PS C:\> $a = 0,1,2,3 PS C:\> $i = 1; $j = -1 PS C:\> $a[$i..($a.Length+$j-1)] 1 2

    【讨论】:

    • 嗯...这似乎只是相对于数组末尾的索引(即与文档$a[-3..-1] 中的第一个示例相同的想法)。我真正要寻找的是一种惯用的方法,可以在一次切片中从数组的开头和结尾进行索引。我已经编辑了我的问题,希望能更清楚地说明这一点。
    • @alx9r 查看更新的答案。 Python 中的 : 和 PowerShell 中的 .. 做的事情根本不同,所以 AFAICS 你不走运。
    • @matt 如果数组未排序,则该组合不起作用。
    • @AnsgarWiechers 感谢您的准确回答。现在一切都清楚了。 0..-2 的计算结果为 0,-1,-2,这与记录在案的奇异切片行为完全一致。
    • 如果你需要从头到尾的一些索引,我觉得$a[-$a.Count..-2]值得一提。这也为最后模拟 Python 的行为提供了一种替代方法:$a[(-$a.Count+$i)..($j-1)]。我认为这一点点看起来更直观,因为它在每一端都有一个简单的操作,但这可能是一个见仁见智的问题。
    【解决方案4】:

    我相信这是正确的做法。所有其他方法都需要更多代码。

    $a[1..($a.Count-1)]
    

    此外,如果将数组转换为字符串,则可以轻松获取数据,如下所示:

    $a = 0,1,2,3
    [string]::Concat($a).Substring(1)
    

    【讨论】:

      【解决方案5】:

      虽然不像您想要的那样简洁,但 PowerShell 的工作方式更简洁......

      (@(1,2,3,4)) | Select-Object -Skip 1
      

      返回 ...

      2
      3
      4
      

      【讨论】:

      • 如果您使用类型化数组,则不能使用此方法。 Select-Object 将返回一个 Object[] 因此,如果您使用需要特定类型的接口,它将失败。 ([int[]] @(1,2,3) | Select-Object -Skip 1).GetType()
      • 如果结果长度为 1 项,您将获得裸项而不是长度为 1 的数组!意味着后续代码必须检查发生了什么,不能只说a[0]。
      【解决方案6】:

      这可能是对数组的两端进行切片的最惯用方式:

      $array[start..stop] 其中 stop 是通过将数组的长度减去从数组末尾偏移的值来定义的:

      $a = 1,2,3,4,5,6,7,8,9
      $start = 2
      $stop = $a.Length-3
      $a[$start..$stop]
      

      这将返回 3 4 5 6 7

      起始值从零开始计数,因此起始值“2”为您提供数组的第三个元素。停止值使用 ($a.Length-3) 计算,这将删除最后两个值,因为 $a.Length-3 本身包含在切片中。

      为了清楚起见,我已经定义了 $start 和 $stop,显然你也可以这样写:

      $a = 1,2,3,4,5,6,7,8,9
      $a[2..($a.Length-3)]
      

      这也将返回 3 4 5 6 7

      【讨论】:

        【解决方案7】:

        如果您要查找数组中的前三个和后三个元素,并将结果放在一个数组中,则只需添加一点数组即可。

        [array]$A = (([int][char]'A')..([int][char]'Z')) | ForEach-Object {[char]$_}
        $B = $A[0..2]+$A[-3..-1]
        Clear-Host
        Write-Host "Original List"
        Write-Host $A -NoNewline -Separator ', '
        Write-Host
        Write-Host "First three and last three"
        Write-Host $B -NoNewline -Separator ', '
        

        产量:

        Original List
        A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
        First three and last three
        A, B, C, X, Y, Z
        

        【讨论】:

          猜你喜欢
          • 2021-12-26
          • 1970-01-01
          • 2022-01-18
          • 2017-09-28
          • 1970-01-01
          • 1970-01-01
          • 2016-03-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多