PowerShell 有 "window" 函数吗?

Does PowerShell have a "window" function?

我一直在寻找 "window" 函数,例如 F# 的 Seq.windowed 或 Reactive 扩展 Window。它看起来像是 Select-Object 之类的(已经具有 take / skip 功能)提供的,但事实并非如此。

如果没有现成的东西,有什么想法可以在没有不必要的程序循环的情况下实现 "window"?

我正在寻找可以很好地与 PowerShell 管道配合使用的东西。

据我所知没有这样的内置。 PowerShell 对管道的处理,尤其是它对展开枚举的强烈坚持,使得仅编写一个函数来管道输入有点困难,但作为一个通用表达式:

$a = 1..10  # an array contain 1 through 10
$w = 4      # window size of 4

0..($a.Length-$w) | ForEach-Object -Process {
    $a[$_..($_+$w-1)]
}

这确实处理了您想要的 window 数组。这样看更容易:

0..($a.Length-$w) | ForEach-Object -Process {
    "This is my window: $($a[$_..($_+$w-1)])"
}

问题是,如果您尝试以这种方式执行上述操作:

0..($a.Length-$w) | ForEach-Object -Process {
    $a[$_..($_+$w-1)]
} | ForEach-Object -Process {
    "This is my window: $_"
}

您会非常失望,因为 "window" 数组在通过管道传输到 ForEach-Object 之前已展开;您为处理此问题而编写的任何管道函数也会遇到问题。

没有办法在函数中补偿它,你必须小心在 ForEach-Object 块中使用它,因为即使在赋值时:

$myWindows = 0..($a.Length-$w) | ForEach-Object -Process {
    $a[$_..($_+$w-1)]
}

它会被展开。

那么你需要这样的东西:

0..($a.Length-$w) | ForEach-Object -Begin {
    $myWindows = @()
} -Process {
    $myWindows += @(,$a[$_..($_+$w-1)])
}

你可以像这样使用:

$myWindows | ForEach-Object -Process {
    "This is my window: $_"
}  

您需要使用某种队列来累积和轮换足够的先前管道项:

function Window {
    param($Size)
    begin {
        $Queue = [Collections.Queue]::new($Size)
    }
    process {
        $Queue.Enqueue($_)
        if($Queue.Count -eq $Size) {
            @(
                ,$Queue.ToArray()
                [void]$Queue.Dequeue()
            )
        }
    }
}

你可以这样使用它:

1..10 | Window 4 | Window 3 | Format-Custom -Expand CoreOnly