はじめに
.NET は10年以上前にかじった程度なので構文に自信がない上、PowerShellはJavaと比べて個人的に理解しづらいなぁ・・・と悩む日々を送りながらも、なんだかんだ最近PowerShellにはまってます。
特にPowerShellのパイプライン処理、簡潔でいいですねぇ~。
使っていくうちに、JavaのStream APIの方が冗長なんじゃねぇか?と錯覚するような気分に陥っていきます。
ただそのパイプライン処理にかける配列の要素数と配列部分式演算子@()
の関係で苦しんだことがあるので、これから述べていきます。
要素数が2以上のとき
要素数が2以上のときは、次のコードのように配列部分式演算子なんか気にせずに記述しても期待どおりの出力が得られます。
$array = "hoge", "huga"
$array1 = $array | % { $_ }
$array3 = if ($array) { $array | % { $_ } }
echo ($array1.Length, $array1[0] -join ",") # 出力:2,hoge
echo ($array3.Length, $array3[0] -join ",") # 出力:2,hoge
要素数が1のとき
問題はこのケースです。前述したノリでそのまま処理しようとすると・・・、
[array]$array = "hoge"
$array1 = $array | % { $_ }
$array3 = if ($array) { $array | % { $_ } }
echo ($array1.Length, $array1[0] -join ",") # 出力:4,h
echo ($array3.Length, $array3[0] -join ",") # 出力:4,h
こうなります。出力が1,hoge
とはなりません。
これは、パイプライン処理全体を配列部分式演算子で囲めばうまくいきます。ただし、if文やif-else文などの制御構文のブロック内にパイプライン処理が存在する場合は、その制御構文全体を囲まないとうまくいきません1。
[array]$array = "hoge"
$array2 = @($array | % { $_ })
$array4 = @(if ($array) { $array | % { $_ } })
echo ($array2.Length, $array2[0] -join ",") # 出力:1,hoge
echo ($array4.Length, $array4[0] -join ",") # 出力:1,hoge
なぜこうなるの?
出力が4,h
と出てくるものだから、てっきり$array
配列の要素が一文字ずつ分解されて、長さ4の新たな配列が生成されたのかと思っていました。
ただ、echo $array1.GetType()
を実行してみると・・・
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
・・・これはつまり、文字列ですけぇ?
パイプラインは要素1の奴が来ると、配列を文字列にしてしまうのか・・・。
更に、文字列に添字というちょっとよくわからない概念をいきなり突きつけられたので、試しに適当な文字列で試してみたら、
echo "hoge"[1] # 出力:o
何だこれは。
まるでJavaのString#charAt
メソッドみたい。
添字って、配列の要素のインデックスのことじゃないのか?
それとも、これはそもそも添字とは違う概念なのか・・・?
はたまた、PowerShellではこれも添字とするのか・・・?
いまいち、整理がつきません。
追記
@ktz_alias さんより、自動ラップ・アンラップに関する情報をご提供いただきました。ありがとうございます!
パイプラインの出力結果を代入する変数に対し明示的に型指定を行うことでも、String型へのアンラップを防げるとのことでした。
[array]$array = "hoge"
[array]$array1 = $array | % { $_ }
[array]$array3 = if ($array) { $array | % { $_ } }
echo ($array1.Length, $array1[0] -join ",") # 出力:1,hoge
echo ($array3.Length, $array3[0] -join ",") # 出力:1,hoge
更に、パイプラインに流し込むと、暗黙的に配列にラップされるようです。パイプラインは如何なるときも必ず配列を処理するということなのでしょうかね。
$array = "hoge"
echo $array | % { $array.GetType() }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
-
このとき、制御構文のブロック内にあるパイプライン処理全体が配列部分式演算子で囲まれているかどうかによって、出力結果が変わることはないようです。 ↩