当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 解决 PowerShell 计数挑战

作者:精品下载站 日期:2024-12-14 07:58:47 浏览:15 分类:玩电脑

解决 PowerShell 计数挑战


几周前,发布了 Iron Scripter PowerShell 脚本挑战赛。与所有这些挑战一样,过程比最终结果更重要。如何找出解决方案就是如何作为 PowerShell 专业人士进行发展。一些人已经分享了他们的工作。今天,我想分享一下我的。

初学者

初学者的挑战是求 1 到 100 之间的偶数之和。您应该能够想出至少 3 种不同的技巧。

For循环

我的第一个解决方案是使用 For 循环。

$t = 0
for ($i = 2; $i -le 100; $i += 2) {
    $t += $i
}
$t

变量 $t 设置为 0。这将是总值。 For 循环的语法是:“从 $i 开始,值为 2,当 $i 小于或等于 100 时继续循环。每次循环时,将 $i 的值增加 2。” += 运算符是表示 $i=$i+2 的快捷方式。这应该得到 100 以内的所有偶数。我使用的是 -le 运算符,而不是 -lt 运算符。每次通过循环时,{} 内的代码都会将 $t 增加 $i 的值。最后,我得到的价值是 2550 美元。

模运算符

下一个技术是使用模运算符 - %。

1..100 | Where-Object {-Not($_%2)} | Measure-Object -sum

管道表达式的第一部分是使用 Range 运算符获取 1 到 100 之间的所有数字。每个数字都通过管道传输到使用 Modulo 运算符的Where-Object。我将每个数字除以 2。如果有余数,例如 3/2 是 1.5,则结果为 1。否则,该运算符将生成结果 0。1 和 0 也可以解释为布尔值。 1 为 $True,0 为 $False。在这种情况下,所有除以 2 且没有余数的数字将产生值 0。但是当表达式为 True 时,我需要Where-Object 来传递对象,因此我使用 -Not 运算符来反转该值。因此 False 变为 True,并且数字被传递到 Measure-Object,在那里我可以得到总和。是的,还有其他方法可以编写Where-Object 表达式。

ForEach 循环

我的第三个想法是使用 ForEach 循环。

$t = 0
foreach ($n in (1..100)) {
    if ($n/2 -is [int]) {
        $t += $n
    }
}
$t

代码表示:“对于集合中的每个事物,执行某些操作。”该集合的范围是 1..100。 “东西”可以被称为任何你想要的东西。在我的代码中,这是 $n。对于每个值,我使用 IF 语句来测试 $n/2 是否是 [int] 类型的对象。从技术上讲,像 1.5 这样的值是 [double]。假设该值通过测试,我将 $t 增加该值。

奖金

“正确”的解决方案取决于代码的其余部分以及最有意义的解决方案。这就是为什么有不同的技术可供您学习。如果我只想要一个简单的单行解决方案,我可以这样做:

(1..100 | where-object {$_/2 -is [int]} | measure-object -sum).sum

我没有得到测量对象,而是得到了 sum 属性。 ( ) 告诉 PowerShell,“运行此代码并保留对象”。这是一个单行版本:

$m = 1..100 | where-object {$_/2 -is [int]} | measure-object -sum
$m.sum

通过(),$m 被隐式定义。

中间的

下一个级别的挑战是编写一个函数,获取 1 和用户指定的最大值之间的每个 X 值的总和。我们还被要求获得平均值,最好包括计算中使用的所有数字。

出于开发目的,我总是从简单的代码开始。我测试了获取 1 到 100 之间每 6 个数字的总和。

$total = 0
$max = 100
$int = 6
$count = 0
$Capture = @()

for ($i = $int; $i -le $max; $i += $int) {
    $count++
    $Capture += $i
    $total += $i
}
[pscustomobject]@{
    Start    = 1
    End      = $Max
    Interval = $int
    Sum      = $total
    Average  = $total/$count
    Values   = $Capture
}

您会发现我正在使用类似的运算符和技术。新的一步是我正在动态创建一个自定义对象。哈希表键成为属性名称。

Start    : 1
End      : 100
Interval : 6
Sum      : 816
Average  : 51
Values   : {6, 12, 18, 24...}

这样,我就能够创建这个函数了。

Function Get-NFactorial {

    [CmdletBinding()]

    Param  (
        [Parameter(Position = 0)]
        [int32]$Start = 1,
        [Parameter(Mandatory, Position = 1)]
        [int32]$Maximum,
        [Parameter(Mandatory)]
        [ValidateRange(1, 10)]
        [ArgumentCompleter({1..10})]
        [int32]$Interval
    )

    Begin {
        Write-Verbose "Starting $($MyInvocation.Mycommand)"
        Write-Verbose "Getting NFactorial values between $start and $Maximum"
        Write-Verbose "Using an interval of $Interval"

        #initialize some variables
        $count = 0
        $Capture = @()
        $Total = 0
    } #begin

    Process {
        Write-Verbose "Looping through the range of numbers"
        for ($i = $Interval; $i -le $Maximum; $i += $Interval) {
            $count++
            $Capture += $i
            $Total += $i
        }
        Write-Verbose "Writing result to the pipeline"
        [pscustomobject]@{
            PSTypeName  = "nFactorial"
            Start       = $Start
            End         = $Maximum
            Interval    = $Interval
            Sum         = $Total
            Average     = $total/$count
            Values      = $Capture
            ValuesCount = $Capture.Count
        }
    } #process

    End {
        Write-Verbose "Ending $($MyInvocation.Mycommand)"
    } #end

} #close function

对于 Interval 参数,我正在做一些你以前可能没有见过的事情。我限制用户使用 1 到 10 之间的间隔值。这就是您以前可能见过的 ValidateRange 属性。另一个元素是 ArgumentCompleter。当有人运行该函数时,我希望他们能够使用制表符补全 -Interval 的值。我注意到,当您使用 ValidateSet 时,制表符补全功能可以正常工作。但 ValidateRange 则不然。 (是的,我可以使用 ValidateSet,但这样我就没有任何东西可以教你了!)。 ArgumentCompleter 使用 {} 内的代码结果作为自动完成值。

这是正在运行的函数。

PS C>\> Get-Nfactorial -Start 1 -Maximum 100 -Interval 2

Start       : 1
End         : 100
Interval    : 2
Sum         : 2550
Average     : 51
Values      : {2, 4, 6, 8...}
ValuesCount : 50

让我们再迈出一步。

格式化结果

默认输出是一个列表,也许我实际上并不需要默认查看所有这些属性。如果您注意到,在自定义对象哈希表中,我定义了一个名为 PSTypeName 的属性。为了进行自定义格式设置,您的对象需要一个唯一的类型名称。我的叫 nFactorial。

格式化需要 ps1xml 文件。但不要惊慌。从 PowerShell 库安装 PSScriptTools 模块并使用 New-PSFormatXML。

Get-Nfactorial -Start 1 -Maximum 100 -Interval 2 | New-PSFormatXML -Path .\nfactorial.format.ps1xml -Properties Start,End,Interval,Sum,Average -FormatType Table

您可以编辑该文件并根据需要进行调整。这是我的版本。

<?xml version="1.0" encoding="UTF-8"?>
<!--
format type data generated 05/19/2020 10:06:19 by BOVINE320\Jeff
-->
<Configuration>
  <ViewDefinitions>
    <View>
      <!--Created 05/19/2020 10:06:19 by BOVINE320\Jeff-->
      <Name>default</Name>
      <ViewSelectedBy>
        <TypeName>nFactorial</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <!--Delete the AutoSize node if you want to use the defined widths.
        <AutoSize /> 
        -->
        <TableHeaders>
          <TableColumnHeader>
            <Label>Start</Label>
            <Width>5</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>End</Label>
            <Width>6</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>Interval</Label>
            <Width>8</Width>
            <Alignment>center</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>Sum</Label>
            <Width>9</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>Average</Label>
            <Width>10</Width>
            <Alignment>right</Alignment>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Start</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>End</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Interval</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>"{0:n0}" -f $_.Sum</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Average</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

要使用,您需要更新 PowerShell。

Update-FormatData .\nfactorial.format.ps1xml

有了这个,我现在就得到了格式良好的输出。

[玩转系统] 解决 PowerShell 计数挑战

请记住,如果我不告诉 PowerShell 执行任何其他操作,这只是我的新默认设置。我仍然可以运行该函数并通过管道连接到 Select-Object 或 Format-List。

这个愚蠢的功能显然离实用还很远,但我可以在未来的项目中使用这些技术和模式。我希望您能从中有所收获,并鼓励您应对 Iron Scripter 的挑战。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯