PowerShell で LINQ
id:NyaRuRu:20080112:p1 の続き.
初版からの変更点
- take のバグフィックス
- 実験的に PowerShell v2.0 対策 (内部実装に依存する対策なので微妙)
- ストリーミング出力の自動フラット化を抑制 (LINQ のセマンティクスを採用)
- xrepeat の引数を scriptblock で一本化し,switch で using を抑制できるようにした
- C# 版に .ForEach(Console.WriteLine) を,PowerShell 版に暗黙の Out-Default を明示して対応を明確にした
- (追記)PowerShell v2.0 (CTP) で end block が実行されない問題対策
#PowerShell 2.0 function xrepeat { param([scriptblock] $initializer = $(throw "element-initializer should be set"), [switch] $suppressdispose) begin { $element=&$initializer } process { trap { continue } while($true){ ,$element } } end { if(! $suppressdispose) { $disposable = $element -as [IDisposable] if($disposable -ne $null) { $disposable.Dispose() } } } } function xtake { param([int] $count) begin { if(0 -ge $count) { break } } process { ,$_ if(0 -ge --$count) { break } } } filter xselect { param([scriptblock] $selector) ,(&$selector $_) } filter xwhere { param([scriptblock] $predicate) if(&$predicate $_) { ,$_ } } filter xtakewhile { param([scriptblock] $predicate) if(&$predicate $_) { ,$_ } else { break } }
こんな感じで filter を用意して,『C# 3.0 と while(true) と Iterator』を再現.
win.ini のダンプ
// C# 3.0 with EnumerableEx EnumerableEx.Repeat(() => new StreamReader(@"c:\windows\win.ini")) .Select(sr => sr.ReadLine()) .TakeWhile(line => line != null) .ForEach(Console.WriteLine);
# PowerShell xrepeat {new-object IO.StreamReader 'c:\windows\win.ini'} ` | xselect { $_.ReadLine() } ` | xtakewhile { $_ -ne $null } ` | Out-Default
1 桁の乱数を 100 個得る
// C# 3.0 with EnumerableEx EnumerableEx.Repeat( new Random() ) .Select(rand => rand.Next(0, 10)); .Take(100) .ForEach(Console.WriteLine);
# PowerShell xrepeat {new-object Random} ` | xselect { $_.Next(0,10) } ` | xtake 100 ` | Out-Default
10 秒カウントダウン
// C# 3.0 with EnumerableEx EnumerableEx.Repeat(() => DateTime.Now.AddSeconds(10.0)) .Select(time => time - DateTime.Now) .TakeWhile(span => span.TotalSeconds > 0); .Select(span => span.TotalSeconds) .ForEach(Console.WriteLine);
# PowerShell xrepeat { (Get-Date).AddSeconds(10.0) } ` | xselect { $_ - (Get-Date) } ` | xtakewhile { $_.TotalSeconds -gt 0 } ` | xselect { $_.TotalSeconds } ` | Out-Default