PowerShellでfoldl

概要

PowerShellのパイプで流れてきたオブジェクト群をたたみこむ。

mapとfilter

Windows PowerShellは、その名の通りWindowsのシェルで、
.NETのオブジェクトがパイプラインで流れるという特徴がある。

PowerShellで気になっているところは、
パイプラインから流れてきたオブジェクトを
高階関数mapとfilterのようなもので処理することができること。

コマンドはそれぞれForeach-ObjectとWhere-Objectで、簡単なエイリアスは"%"と"?"。

PS C:\> 1, 2, 3 | % {$_ * 3}
3
6
9
PS C:\> 1, 2, 3, 4, 5 | ? {$_ % 2 -eq 1}
1
3
5

これらの実現に使われているのが、スクリプトブロックという機能。
{$_ * 3}の部分がそれ。
これはScriptBlock型をもつオブジェクトのリテラルで、
&演算子で好きなときに実行することができる。

foldl (reduce)

mapとfilterがあったら次はreduceだ!ということで、
foldlとreduce(reduceはHaskellでいうとfoldl1)をfunctionで実装してみた。

function Main
{
  2, 3, 4 | foldl {$_l * 100 + $_r} 1
  2, 3, 4 | foldl {[String]$_l + "たす" + $_r} 1  
  2, 3, 4 | reduce {$_l * $_r}
  1 | reduce {$_l * $_r}
  ($null | reduce {}) -eq $null
}

function foldl
{
  Param ([ScriptBlock] $script, [Object] $init)
  begin
  {
    $_l = $init
  }
  
  process
  {
    $_r = $_
    $_l = & $script
  }
  
  end
  {
    $_l
  }
}

function reduce
{
  Param ([ScriptBlock] $script)
  begin
  {
    $isFirst = $true
  }
  
  process
  {
    
    if($isFirst)
    {
      $isFirst = $false
      $_l = $_
    }
    else
    {
      $_r = $_
      $_l = & $script
    }
    
  }
  
  end
  {
      $_l
  }
}

. Main

実行結果

1020304
1たす2たす3たす4
24
1
True

Main関数は必須ではないが
長いfoldl, reduceの定義よりも実行部分を先に持ってくるのに使える。

Foreach-Object, と違って変数が2つ必要なのでそれぞれ
$_l, $_rと名付けた。アンダースコアは穴が開いているイメージ。

reduceでパイプから何も流れてこなかった場合、結果は$nullになる。

fold"r"はスタックがいりそうなので、コレクションの使い方を調べないと。