Monad in PowerShell?



>我有以下代码

function Get($url) {
$x = Invoke-RestMethod $url 
# ....
$result # return a json which some values (id, name, color) and a list
}
Get "http://...." |
% {
$id = $_.id
$url = "${$_.url}/xxx"
Get $ur  |
% {
$name = $_.name
$url = $_.url
Get $url | 
% {
$color = $_.color
$url = $_.url
Get $url | % {
# other layer omitted
}
}
}
}

代码看起来很糟糕。似乎嵌入层应该由monad解决。有没有办法在PowerShell中做到这一点?

下面显示了一个伪 F# 代码。

myMonad {
let! (id1, _,     _,      urls1, _    ) = Get ["http://..."]
let! (_,   name2, _,      urls2, _    ) = Get urls1
let! (_,   _,     color3, urls3, names) = Get urls2
// ....
printfn "%d %s %s %A" id1, name2, color3, names
}

PowerShell显然不是一种函数式语言,尽管它具有一些函数特性,并且它的许多构造和动态特性允许您模拟函数特性:

下面的代码定义了一个名为recurse的递归和聚合函数,该函数可以按如下方式调用Get函数,以便通过每个返回对象的.URL属性聚合递归调用GetIdNameColor属性:

recurse -Function  Get                 <# target function #> `
-Argument  http://example.org  <# input URL #> `
-RecurseOn URL                 <# what result property to recurse on #> `
-PropertyNames Id, Name, Color <# what properties to aggregate successively, 
in each iteration #>

recurse带有示例代码的源代码:

# A fairly generic recurse-and-aggregate function that aggregates properties from
# result objects from recursive calls to a given function based on a single result property.
function recurse($Function, $Arguments, $RecurseOn, $PropertyNames, $outProperties = [ordered] @{ }) {
# Call the target function with the specified arguments.
$result = & $Function $Arguments
# Split into the names of the current and the remaining properties to aggregate.
$propName, $remainingPropNames = $PropertyNames
# Add the value of the current property of interest to the output hashtable, if present.
if ($null -ne $result.$propName) {
if ($outProperties.Contains($propName)) { # not the first value
if ($outProperties.$propName -is [array]) { # already an array -> "extend" the array.
$outProperties.$propName += $result.$propName
}
else { # not an array yet -> convert to array, with the previous value and the new one as the elements. 
$outProperties.$propName = $outProperties.$propName, $result.$propName
}
}
else { # first value -> assign as-is
$outProperties.$propName = $result.$propName
}
}
if ($remainingPropNames) {
# Recurse on the value(s) of the property specfied with -RecurseOn
foreach ($newArgument in $result.$RecurseOn) {
$null = recurse -Function $function -Argument $newArgument -RecurseOn $RecurseOn -PropertyNames $remainingPropNames -outProperties $outProperties
}
}
# Return the aggregated properties.
$outProperties
}
# Sample input function:
# It makes a REST call to the given URL and returns the
# result object.
function Get($url) {
Write-Host "Get $url"
$id = 1
$name = ''
$color = ''
if ($url -eq 'http://example.org') { 
$urls = 'http://example.org/1', 'http://example.org/2'
$id = 1
}
elseif ($url -match 'http://example.org/d$') { 
$urls = "$url/a", "$url/b" 
$name = 'test'
}
elseif ($url -match 'http://example.org/d/a') {
$urls = '...'
$name = 'test'
$color = "[color of $url] #1", "[color of $url] #2"
}
elseif ($url -match 'http://example.org/d/b') {
$urls = '...'
$name = 'test'
$color = "[color of $url] #1", "[color of $url] #2"
}
[pscustomobject] @{
URL = $urls
Id  = $id
Name = $name
Color = $color
}
}
# Call the recurse-and-aggregate function, passing it the name of the Get() 
# function, an input URL, what result property to recurse on, and a list of properties to aggregate.
# The output is an ordered hashtable containing the aggregated property values.
recurse -Function  Get                 <# target function #> `
-Argument  http://example.org  <# input URL #> `
-RecurseOn URL                 <# what result property to recurse on #> `
-PropertyNames Id, Name, Color <# what properties to aggregate successively, 
in each iteration #>

以上结果:

Name                           Value
----                           -----
Id                             1
Name                           {test, test}
Color                          {[color of http://example.org/1/a] #1, [color of http://example.org/1/a] #2, [color of http://example.org/1/b] #1, [color of htt…

好吧,你可以使用 Add-Type 将 F# 代码插入到 Powershell 中。 请参阅示例 7:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-5.1

最新更新