【问题标题】:Powershell equivalent to F# Seq.forallPowershell 相当于 F# Seq.forall
【发布时间】:2010-01-19 08:39:41
【问题描述】:

我已经写了这个(有效):

function ForAll {
    BEGIN {
        $allTrue = $true
    }
    PROCESS {
        if ($_ -lt 1) { $allTrue = $false }  
    }
    END {
        $allTrue
    }
}

$aList = (0..4)
$bList = (1..4)

$aList | ForAll # returns false
$bList | ForAll # returns true

但我想做的是用一个名为 $predicate 的函数替换 ($_ -lt 1) ,我将它传递给 ForAll 函数。我似乎无法让它工作。有什么想法吗?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    使用 [scriptblock],恕我直言,这比在这里使用函数要容易得多。这就是发明脚本块的目的。

    function ForAll([scriptblock]$predicate) {
    
        BEGIN {
            $allTrue = $true
        }
        PROCESS {
            if (!(& $predicate $_)) { $allTrue = $false }  
        }
        END {
            $allTrue
        }
    }
    
    $aList = (0..4)
    $bList = (1..4)
    
    $aList | ForAll {$args[0] -le -10 } # returns false
    $bList | ForAll {$args[0] -le 10 } # returns true
    

    $args[0] 表示传递给脚本块的第一个参数 - 在本例中是 $_。

    【讨论】:

    • Stej,我注意到如果用 $_ 代替 $args[0],实际上不需要 [scriptblock]。前几天晚上我让自己很困惑,试图让它发挥作用。最后,我得到的最短版本只是对你的稍作改动。函数 ForAll($predicate) { ...} $aList = (0..4) $bList = (1..4) $aList | ForAll {$_ -gt 0 } # 返回错误 $bList | ForAll {$_ -gt 0 } # 返回真 $bList | ForAll {$_ -gt 0 -and $_ -lt 5 } # 返回 true
    • 是的,这绝对是真的。您可以使用自动变量 - 它看起来与 foreach-object cmdlet 相同。但是,我在一些更复杂的脚本中遇到了 $_ 的问题,所以我犹豫要不要使用 $_。
    【解决方案2】:

    如果您使用的是 PowerShell 2.0,则可以使用带有脚本块的 param 来声明参数,例如:

    function ForAll {
        param(
            [Parameter(Position=0,Mandatory=$true)]
            [scriptblock]
            $Expression,
    
            [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
            [psobject]
            $InputObject
        ) 
    
        Begin { $allTrue = $true } 
    
        Process { 
            foreach ($obj in $InputObject) {
                if (!(&$Expression $obj)) { $allTrue = $false }
            }
        } 
    
        End { $allTrue } 
    } 
    
    $aList = (0..4) 
    $bList = (1..4) 
    
    ForAll {param($val) $val -gt 0} (0..4) # returns false 
    
    $aList | ForAll {param($val) $val -gt 0} # returns false 
    $bList | ForAll {param($val) $val -gt 0} # returns true 
    $aList | ForAll {param($val) $val -ge 0 -and $val -le 4} # returns true
    

    【讨论】:

    • 我一直希望看到对来自管道和参数的参数进行正确处理。我会记住 foreach 的诀窍。有什么理由不管它吗? $输入对象| % { if (!(&$Expression $_)) { $allTrue = $false } } }
    • 不 - 这也可以。当我编写函数时,我倾向于回到我的 C#(非流水线)循环方式。 :-) 小管道很容易阅读,但是当它们逐行运行时,当我回到脚本时,我发现显式循环更容易阅读。
    • 我不明白,你为什么需要foreach?我认为流程是为了从管道中获取个人价值? $InputObject 是否只绑定到进程内的单个项目? (因为属性)如果是这样,为什么要使用开始、处理、结束?
    • 是的,但时不时有人会尝试通过管道发送集合,例如,$aList | ForAll { ... }。 foreach 遍历传入的集合,对于标量值,循环只执行一次。
    【解决方案3】:

    为了有效处理,我们将使用过滤器而不是函数,并且一旦发现虚假元素,我们就会“中断”。这也会停止所有上游进程:

    filter ForAll([scriptblock] $predicate) {
        PROCESS {
            if ( @($_ | Where-Object $predicate).Length -eq 0 ) {
                $false;
                break;
            }
        }
        END {
            $true;
        }
    }
    

    习惯性地使用它:

    (0..4) | ForAll { $_ -gt 0 }     # returns false
    (1..4) | ForAll { $_ -gt 0 }     # returns true
    

    【讨论】:

      猜你喜欢
      • 2014-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      • 2011-12-09
      • 2023-02-02
      相关资源
      最近更新 更多