web-dev-qa-db-ja.com

PowerShell配列の初期化

PowerShellで配列を初期化する最良の方法は何ですか?

たとえば、コード

$array = @()
for($i=0; $i -lt 5;$i++)
{
    $array[$i] = $FALSE
}

エラーを生成します

Array assignment failed because index '0' was out of range.
At H:\Software\PowerShell\TestArray.ps1:4 char:10
+         $array[$ <<<< i] = $FALSE
70
Eric Ness

さらに別の選択肢:

for ($i = 0; $i -lt 5; $i++) 
{ 
  $arr += @($false) 
}

これは、$ arrがまだ定義されていない場合に機能します。

NOTE-これを行うためのより良い(そしてよりパフォーマンスの高い)方法があります... https://stackoverflow.com/例としてa/234060/457 .

43
David Mohundro

さらに2つの方法がありますが、どちらも非常に簡潔です。

$arr1 = @(0) * 20
$arr2 = ,0 * 20
88
halr9000

型付き配列を作成する場合は、コンストラクタのデフォルト値を使用することもできます。

> $a = new-object bool[] 5
> $a
False
False
False
False
False

boolのデフォルト値は明らかにfalseなので、これはあなたのケースで機能します。同様に、型付きのint []配列を作成すると、デフォルト値の0が取得されます。

配列を初期化するために使用するもう1つのクールな方法は、次の速記を使用することです。

> $a = ($false, $false, $false, $false, $false)
> $a
False
False
False
False
False

または、範囲を初期化することができる場合、私はこれが役立つことが時々あります:

> $ a =(1..5)
> $ a 
 1 
 2 
 3 
 4 
 5 

これがいくらか役立つことを願っています!

51
Scott Saad

元の例では、配列が空で作成されているためエラーが返されます。次に、n番目の要素にアクセスして値を割り当てようとします。

ここには多くの創造的な答えがありますが、多くはこの投稿を読む前に知りませんでした。小さなアレイにはすべて問題ありませんが、n0rdが指摘するように、パフォーマンスに大きな違いがあります。

ここでは、Measure-Commandを使用して、各初期化にかかる時間を調べます。ご想像のとおり、明示的なPowerShellループを使用するアプローチは、.NetコンストラクターまたはPowerShell演算子(ILまたはネイティブコードでコンパイルされる)を使用するアプローチよりも低速です。

概要

  • New-Object@(somevalue)*nは高速です(10万個の要素に対して約2万ティック)。
  • 範囲演算子n..mを使用して配列を作成すると、10倍遅くなります(20万ティック)。
  • Add()メソッドでArrayListを使用すると、for()またはForEach-Object(別名foreach%)。
  • +=を追加するのが最悪です(要素が1000個の場合は200万ティック)。

全体的に、私は言うだろうarray*nが「最良」である理由:

  • これは速い。
  • タイプのデフォルトだけでなく、任意の値を使用できます。
  • 繰り返し値を作成できます(例として、powershellプロンプトでこれを入力します:(1..10)*10 -join " "または('one',2,3)*3
  • 簡潔な構文。

唯一の欠点:

  • 非自明。このコンストラクトを以前に見たことがなければ、それが何をするのかは明らかではありません。

ただし、配列要素を何らかの値に初期化したい多くの場合、厳密に型指定された配列がまさに必要なものであることに注意してください。すべてを$falseに初期化する場合、配列は$falseまたは$true以外の何かを保持しますか?そうでない場合、New-Object type[] nが「最良の」アプローチです。

テスト中

デフォルトの配列を作成してサイズを設定し、値を割り当てます。

PS> Measure-Command -Expression {$a = new-object object[] 100000} | Format-List -Property "Ticks"
Ticks : 20039

PS> Measure-Command -Expression {for($i=0; $i -lt $a.Length;$i++) {$a[$i] = $false}} | Format-List -Property "Ticks"
Ticks : 28866028

ブールの配列の作成は、オブジェクトの配列よりも少し遅くなります。

PS> Measure-Command -Expression {$a = New-Object bool[] 100000} | Format-List -Property "Ticks"
Ticks : 130968

これが何をするのかは明らかではありません。New-Objectのドキュメントでは、2番目のパラメーターは.Netオブジェクトコンストラクターに渡される引数リストであると述べています。配列の場合、パラメーターは明らかに目的のサイズです。

+ =を追加

PS> $a=@()
PS> Measure-Command -Expression { for ($i=0; $i -lt 100000; $i++) {$a+=$false} } | Format-List -Property "Ticks"

私はそれが完了するのを待つのにうんざりしたので、ctrl + c:

PS> $a=@()
PS> Measure-Command -Expression { for ($i=0; $i -lt    100; $i++) {$a+=$false} } | Format-List -Property "Ticks"
Ticks : 147663
PS> $a=@()
PS> Measure-Command -Expression { for ($i=0; $i -lt   1000; $i++) {$a+=$false} } | Format-List -Property "Ticks"
Ticks : 2194398

(6 * 3)が概念的に(6 + 6 + 6)に似ているように、($ somearray * 3)は($ somearray + $ somearray + $ somearray)と同じ結果を与えるはずです。ただし、配列の場合、+は加算ではなく連結です。

$ array + = $ elementが遅い場合、$ array * $ nも遅いと思われるかもしれませんが、そうではありません:

PS> Measure-Command -Expression { $a = @($false) * 100000 } | Format-List -Property "Ticks"
Ticks : 20131

JavaにStringBuilderクラスがあるのと同様に、追加時に複数のオブジェクトを作成しないようにするため、PowerShellにはArrayListがあるようです。

PS> $al = New-Object System.Collections.ArrayList
PS> Measure-Command -Expression { for($i=0; $i -lt 1000; $i++) {$al.Add($false)} } | Format-List -Property "Ticks"
Ticks : 447133
PS> $al = New-Object System.Collections.ArrayList
PS> Measure-Command -Expression { for($i=0; $i -lt 10000; $i++) {$al.Add($false)} } | Format-List -Property "Ticks"
Ticks : 2097498
PS> $al = New-Object System.Collections.ArrayList
PS> Measure-Command -Expression { for($i=0; $i -lt 100000; $i++) {$al.Add($false)} } | Format-List -Property "Ticks"
Ticks : 19866894

範囲演算子、およびWhere-Objectループ:

PS> Measure-Command -Expression { $a = 1..100000 } | Format-List -Property "Ticks"
Ticks : 239863
Measure-Command -Expression { $a | % {$false} } | Format-List -Property "Ticks"
Ticks : 102298091

ノート:

  • 各実行の間に変数を無効にしました($a=$null)。
  • テストは、Atomプロセッサーを搭載したタブレットで行われました。他のマシンではおそらくより高速になります。 [編集:デスクトップマシンでは約2倍の速度。]
  • 複数回実行してみたところ、かなりのばらつきがありました。正確な数ではなく桁数を探してください。
  • テストは、Windows 8のPowerShell 3.0を使用して行われました。

謝辞

Array * nの@ halr9000、New-Objectの@Scott SaadとLee Desmond、ArrayListの@EBGreenに感謝します。

パフォーマンスについて考えさせてくれた@ n0rdに感謝します。

36
Celery Man
$array = 1..5 | foreach { $false }
12
Peter Seale

別のアイデアがあります。下の.NETであることを覚えておく必要があります。

$arr = [System.Array]::CreateInstance([System.Object], 5)
$arr.GetType()
$arr.Length

$arr = [Object[]]::new(5)
$arr.GetType()
$arr.Length

結果:

IsPublic IsSerial Name                                     BaseType                                                                                               
-------- -------- ----                                     --------                                                                                               
True     True     Object[]                                 System.Array                                                                                           
5
True     True     Object[]                                 System.Array                                                                                           
5

new()を使用すると、1つの明確な利点があります。ISEでプログラミングしているときにオブジェクトを作成する場合、ISEはすべてのパラメーターの組み合わせとそのタイプのヒントを提供します。 New-Objectでは、引数のタイプと順序を覚えておく必要があります。

ISE IntelliSense for new object

9
Adam Luniewski
$array = @()
for($i=0; $i -lt 5; $i++)
{
    $array += $i
}
9
EBGreen

私が見つけた解決策は、New-Objectコマンドレットを使用して適切なサイズの配列を初期化することでした。

$array = new-object object[] 5 
for($i=0; $i -lt $array.Length;$i++)
{
    $array[$i] = $FALSE
}
7
Eric Ness

前もってサイズがわからない場合は、配列の代わりにarraylistを使用します。

$al = New-Object System.Collections.ArrayList
for($i=0; $i -lt 5; $i++)
{
    $al.Add($i)
}
5
EBGreen

または、これを考えてみてください。 powershell 5.0以降で動作します。

[bool[]]$tf=((,$False)*5)
0
Michael Koza