在PowerShell中打高尔夫球的技巧


45

您在Windows PowerShell中打高尔夫球有哪些一般技巧?我正在寻找可以普遍用于解决高尔夫问题的想法,这些想法至少在某种程度上特定于PowerShell(例如,“删除评论”不是答案)。请为每个答案发布一个提示。

-几乎完全不听马尔可格的问题


3
当我在“ PowerShell golf”上搜索时,这是第一个热门!
马特

Answers:


24

具有科学符号的10个文字的幂:

4e6 = 4000000

2个文字的幂:

4KB = 4096
4MB = 4194304
4GB = 4294967296

# TB and PB suffixes also exist, but less useful for golf.

可以派上用场。


19

如果您需要运行一个循环,并且确切知道每次需要运行多少次,请考虑ForEach-Object通过%别名将连续整数数组传递给,而不是使用for

for($x=1;$x-le10;$x++){...}

1..10|%{...}


18

您可以在PowerShell中跳过很多空格。如果感觉不需要它,则很有可能不需要它。这在比较中特别有用。

例:

$x-eq$i-and$x-ne9

$x -eq $i -and $x -ne 9

如果您需要根据可能有多个结果的单个测试的结果来分支脚本,switch则有时可以匹配或击败if语句。

示例(切换与if / else-并列):

if($x%2-eq0){'even'}else{'odd'}

switch($x%2){0{'even'}1{'odd'}}

或(切换vs. if / elseif / else-切换胜15分):

if($x%2-eq0){'even'}elseif($x%2-eq1){'odd'}else{'error'}

switch($x%2){0{'even'}1{'odd'}2{'error'}}

如果开关实际上是基于某些数学结果的,例如上面的模运算,则可以将整个开关替换为数组。在这里,它会再保存13个字符,并且比原始的二选项if / else语句还要短。(感谢Danko Durbic的帮助。)

('even','odd','error')[$x%2]

如果您将大量使用特定命令,尤其是没有预先存在的速记别名的命令,请尽早设置单字符别名。

例:

nal g Get-Random;g 10;g 10;g 10

Get-Random 10;Get-Random 10;Get-Random 10

('even','odd')[$x%2]仅供参考。
TheIncorrigible1

15

将定义变量的命令封装在括号中,可以将变量的定义直接输入其他命令。

例如,您可以设置$ x,然后基于$ x的值在一次拍摄中设置$ y,方法是:

$y=($x=1)+1

代替这个:

$x=1;$y=$x+1

您可以设置$ h并将其输出:

($h='Hello World!')

代替这个:

$h='Hello World!';$h

1
这对于在对象上调用方法特别有用。($x=New-Object Windows.Forms.Form).Controls.Add($t)
SpellingD

14

给定数组时,开关可以像循环一样工作。例如:

$FooBarMeh='a','b','c'
switch ($FooBarMeh)
{
    'a'{'FOO'}
    'b'{'BAR'}
    default{'MEH'}
}

将输出:

FOO
BAR
MEH

我不确定这在什么地方有用,但我希望一段时间后对某些人有用。


2
每当您在其中需要a ForEach-Object和a时switch,它都很方便。例如,(在现实世界中)代码非常适合编写文本文件的快速解析器,您需要根据行匹配的正则表达式执行不同的操作。
乔伊

这是...该死的怪异。数组不应该与char匹配,但是可以匹配。

@Joey这就是switch -File $path
TheIncorrigible1

并非所有解析的内容都恰好是文件。
乔伊


11

比较运算符通过返回匹配值来处理值的集合:

1..5 -gt 2

将产生345。在某些情况下,这可以节省更长的时间|?{$_...}

-match 也是比较运算符。


1
请注意,您不需要在例如空间1..5-gt2
马特

1
我知道; 这更多是关于展示技术。空格在单独的答案中
乔伊(Joey)

11

尽可能使用别名。有很多有用的:

?        Where-Object
%        ForEach-Object
gu       Get-Unique
sort     Sort-Object
iex      Invoke-Expression

11

是否要查找值集合的最大值或最小值?试过了

(...|measure -ma).Maximum

要么

(...|measure -mi).Minimum

已经?

只需排序并使用最后一项或第一项即可:

(...|sort)[-1]  # maximum
(...|sort)[0]   # minimum

1
而且,如果您知道值集合的长度并且小于10 ...
wizzwizz4,2016年

@ wizzwizz4:哦,最大长度通常可以创造性地使用,不一定是10。尽管对于这种特殊情况,我不记得有帮助的实例。
乔伊,

1
我的意思是,如果最终项目被称为是012345678或者9,它会保存一个字节写的已知长度来代替-1
wizzwizz4 2016年

10

缩短属性名称

遗憾的是,与参数不同,.通常不能将属性/方法(用点访问的任何内容)简化为明确的形式。

但是某些cmdlet可以对属性名称进行操作并采用通配符,并且鲜为人知的参数集%?很有用。

通常,我们传入一个脚本块,并使用来引用该项目$_,但是其中的另一种形式是使用属性名称,并且它接受通配符。

$o|select Le*  
$o|%{$_.Length}

对于像.Length我们这样Length的属性,由于数组本身是属性,因此不能使用通常在数组上可用的v3魔术,因此可以使用上述两个获取单个成员的长度。该select进来一点点短。

但是%可以直接采用属性名称并返回该值:

$a|% Length

可以使用通配符将其缩短。通配符必须解析为单个属性(或方法,稍后会详细介绍),因此,通配符将抛出有用的错误,并指出可能会解析为的成员。

对于LengthLe*通常是最短的。即使在单个字符串上,此方法也比仅使用该属性短1个字节。

$a.Length                # 9   #(doesn't work on array)
$a|%{$_.Length}          # 15
$a|% Le*                 # 8

但是根据您在做什么,情况可能会更糟。您可以做,$a.Length*5但要使用管道表达式来做,就必须将其包装起来($a|% Le*)*5;如果它是针对数组的,也许仍然值得,但是重点是它并不总是适合作为直接替换。

它也适用于方法,您可以省略(),使全名具有相同的长度,但有时必须包装它,因此与上述相同。该方法必须有一个不带参数的重载(您可以通过将参数放在方法名称之后来传递参数,这确实不错):

$a.ToUpper()             # 12
$a|% *per                #  9

带参数:

'gaga'-replace'g','r'    # 21
'gaga'|% *ce g r         # 16

这些-replace操作与操作符执行正则表达式替换并不完全相同,但是如果您只是在进行字符串替换,则使用该方法可以(现在)更短一些。这有助于将字符串作为cmdlet参数而不是方法参数,因此不需要将其引起来。

对象对象属性

?也可以采用(部分)属性名称,并对其应用“运算符”(以开关参数的形式)。同样,Where-Object如果属性名称足够长且唯一,则它可以比使用标准脚本块方法短。

$a|?{$_.Length-gt5}      # 19
$a|? Le* -GT 5           # 14

($a|% Le*)-gt5           # 14 - Lengths, not objs

1
少数需要空格的地方之一。很高兴找到。我可以想到几个可以缩短时间的领域。
AdmBorkBork

尼斯编辑乔伊!谢谢。
briantist

找到了一种使它更短的方法;-) ...关于此的可悲的事情是,它总体上还是一个管道,这限制了它的实用性。或者更确切地说,在很多地方,您必须再次将其包装在括号中才能获得表达式。但是,很少有高尔夫技术无法取舍……
Joey

1
@ConnorLSW啊,是的,我打算更新它,因为我意识到您可以以这种方式传递args,这大大提高了它的实用性。大用.ToString()
briantist

2
@briantist,这使PowerShell变成了更具竞争力的语言,确实消除了一些烦人的.Net调用。
colsw '17

9

很长的路要走:

(...|measure -s).Sum

较短的方法:

...|%{$s+=$_};$s

甚至更短:

...-join'+'|iex

9

分号和换行符可以互换。高尔夫球代码如果不卡在一行中,通常更具可读性。并且长度仍然相同(前提是您使用U + 000A作为PowerShell可以毫无问题地处理的换行符)。


2
等一下,我们担心可读性?!
凯文·考克斯

4
如果对长度没有影响...为什么不呢?
乔伊

因为分号比\ r \ n小一个字符,从技术上讲,这是否有所不同?仅在Unix中,它是单数\ n。
Vasili Syrakis 2014年

2
@Vasili:您只需两行之间只有U + 000A就可以保存文件。PowerShell不会抱怨。顺便说一句,我已经在答案中写了。换行符是文件的属性,而不是文件所使用的操作系统的属性。没有人说您不能在Windows上使用Unix行尾。
2014年

@Joey如果您使用全新安装的Windows进行了一些快速编程,则会注意到\\ x0a不能很好地配合记事本
Stan Strum,

9

Get动词是隐含的。这可以缩短任何Get-Frob时间Frob。经常竞争者是daterandom

请注意,这在某些情况下将无法正常工作,因为您的路径(或其他冲突的本机程序)中可能装有GNU实用程序。在这种情况下,命令查找顺序似乎更喜欢本机程序,然后再考虑Get-删除的cmdlet :

PS Home:\> date

Freitag, 15. November 2013 07:13:45


PS Home:\> $Env:Path += ';D:\Users\Joey\Apps\GnuWin32\bin'
PS Home:\> date
Fr Nov 15 07:14:13 W. Europe Standard Time 2013

这并不十分总是工作正常。我有一个脚本,nal g Get-Random其后开始保存字符。将其更改为nal g Random会导致脚本无限期挂起(或至少需要花费非常长的时间来处理-我没有耐心等待它结束,但是它比原始形式要花几个数量级在我中止之前)。
Iszi 2013年

2
两个短Measure-Command调用(100次Get-Randomrandom)告诉我这是关于慢500倍。老实说,我以前不知道。但最好记住这一点,尤其是在具有多次迭代的循环中。话虽如此,打高尔夫球的代码应该简短而不是太快(也就是说,必须等待两天才能解决欧拉计划问题)。
乔伊,

1
我的问题是运行Monty Hall场景的10,000次迭代,每个迭代都需要Get-Random的三次迭代。将处理时间增加50,000%,将30,000次运行倍增,实在令人讨厌。
Iszi 2013年

哇。对于任何别名,看起来似乎都适用。经过测试Get-Variable与,gv并获得了类似的比较。
Iszi 2013年

1
可能就是这样。我进行了一些数学运算,并发现我的Monty Hall脚本不运行大约需要1.5个小时(通常是10-11秒)Get-。这在运行时非常麻烦,仅节省了4个字符。
Iszi 2013年

8

for 循环的标头中可以包含0到三个语句之间的任何值:

无休止的循环:

for(){}

循环初始化:

for($n=0){}

循环初始化和结束条件:

for($n=0;$n-lt7){}

在这种情况下,与通常需要恰好三个语句的类C语言相反,可以省略末尾的其他分号(在语言规范中明确说明,因此这不是实现细节)。

这也使它while更短。相比

while(...){}

for(;...){}

有了额外的奖励,您还可以将前一行(如果有的话)粘贴到行中for,而无需支付额外费用(甚至节省角色)。


8

如果要分配的数组只有两个值,请不要使用索引。

像这样:

$a="apple","orange"
$a[0] # apple
$a[1] # orange

可以很容易地变成这样:

$a,$o="apple","orange"
$a # apple
$o # orange

如果您只需要数组的第一个元素,这也可能很有用:

$a,$b=1..10
$a # 1
$b # 2..10

在这种情况下(带有字符串)$a="apple";$o="orange"的长度是相同的。较长的数组有时可以很好地进行优化,例如,通过将所有元素放在带有分隔符的字符串中,然后使用-split(最好使用空格作为分隔符,因为一元-split就足够了)。
2014年

8

转换为字符串:

[string]$x

"$x"

像这样强制转换为字符串也可以用于扁平化字符串数组,而不是将其连接:

$a = @('a','b','c')
$a -join ' '

$a = @('a','b','c')
"$a"

将字符串转换为数字类型:

[int]$x     [float]$x

+$x

了解PowerShell 始终采用左操作数的类型来确定表达式的最终类型以及要应用的转换也非常有用:

'1'+2    -> '12'
1+'2'    -> 3

这可以帮助确定不必要的演员在哪里。


6

不要忘了,您不一定总是需要提供参数的全名,并且某些参数是位置性的。

Get-Random -InputObject (0..10)

...可以修整为...

Get-Random -I (0..10)

...因为在这种情况下,“ I”足以InputObject从该命令的其他有效参数中唯一标识。

您可以将其进一步修剪为...

Get-Random (0..10)

...因为InputObject是位置参数。

管道通常比作为参数的对象短,特别是当管道可以消除括号时。让我们进一步修剪随机数生成器...

0..10|Get-Random

即使您无法更改命令,也要注意其他方法来完成同一件事。对于上述情况,您可以执行以下操作:

Get-Random 11

或者,结合其他建议 *:

Random 11

**注意:省略Get-命令名称可能会使运行时间膨胀约50,000%。如果只需要一次该命令,但要在长循环中小心使用,就可以了。*

这就是如何将简单命令压缩到其大小的三分之一。


6

伪三元运算符。您可以直接从if语句中分配:

$z=if($x-eq$y){"truth"}else{"false"}

但是您可以使用2元素数组,并使用测试对其进行索引。$ falsey结果获取元素0,$ truthy结果获取元素1:

$z=("false","true")[$x-eq$y]

注意 这确实是在做数组索引,并且如果测试得出的值可以转换为整数,则您将要求在数组范围之外的项并获得$ null,并且需要!(test)强制执行将结果转换为布尔值,并反转选项。


如果我没有记错的话,第一个代码片段无法在某个时间点运行(旧版PowerShell),因此有必要将其包装到中$()。使用由命令和语句构成的管道作为表达式本质上是有区别的。至少在PowerShell 5中似乎已经消失了
Joey

此外,如果数组元素是非静态的,则创建索引和分配结果之前,将对它们进行解析和处理,作为数组创建的一部分。例如
AdmBorkBork '16

6

当使用数字作为运算符的参数(否则需要字符串)时,可以直接使用数字。相比

...-join'0'

...-join0

工程与-split为好。参数总是先转换为字符串。


作为参考,该方法也适用-replace
AdmBorkBork

-replace也不需要您要删除匹配项的第二个参数,也可以使用
Joey

5

绝对值

$n=-123

代替

[math]::abs($n)

采用

$n-replace'-'

当然,如果需要括号,则取消了节余。


或者,如果您需要运算符左侧的结果;-)
Joey

5

如果您需要使错误保持沉默,则显而易见的变体是

try{ <# something that throws errors #> }catch{}

但是,这太长了。一个较短的变体是将try块作为脚本块运行,然后将错误重定向到未设置的变量中($null这是通常的变量,但这仍然太长):

.{ <# something that throws errors #> }2>$x

这样可以节省五个宝贵的字节(如果不是世界)。


5

使用$ofs 特殊变量改变ö安输出˚F ield 小号 eparator字符串化的阵列时使用。如果需要多次将数组转换为字符串,则很有用。

例如:

$a=1,2,3,4
$a-join',';$a-join','
$ofs=',';"$a";"$a"

在2nd上保存2+ n个字符-join,其中n是分隔符的长度,并在第3个上及其后分别保存5+ n 字符-join


1
令人遗憾的是,很少有用(至少到目前为止,在我的高尔夫球比赛中–我倾向于将其简化为最后只有一个连接)。
乔伊,

5

自动变量有真布尔和假作为$true$false,但你可以使用逻辑不是运营商处获得类似的结果!和整数0和1(或任何非零整数)。

PS C:\Users\matt> !1
False

PS C:\Users\matt> !0
True

几乎所有PowerShell表达式都可以被评估为布尔值。因此,只要您知道如何评估某些数据,就可以获取布尔值,而无需显式转换它们。执行这些操作时,请注意LHS值。

  • 整数0为false,非零整数的值为true。
  • 非零长度的字符串为true,为空或为null(本身为null)的字符串为false。

还有其他示例,但是您可以通过强制转换轻松地进行测试

PS C:\Users\matt> [bool]@(0)
False

1
许多其他数据类型也具有相似的状态。未初始化的变量默认为$null,为false。空字符串(并将变量设置为空字符串)为false。等等,然后可以像在FizzBu​​zz中那样,使用它来快速索引到数组中(例如,执行if/时else)。
AdmBorkBork

@TimmyD非常正确。使用ints会更短一些
Matt

@TimmyD我想回答fizzbuzz,直到我看到你....不能击败....至少目前还没有
马特

注意,在很多情况下,你实际上并不需要一个bool。您也可以使用01。隐式转换在这方面有很大帮助(您通常可以使用某些运算符来强制转换)。
乔伊(Joey)

4

Invoke-Expression并且Get-Random还可以获得管道输入而不是参数。

为此,iex可以在某些表达式上保存括号:

iex 1..5-join'+'   # won't work
iex(1..5-join'+')  # does work, but has extra parentheses
1..5-join'+'|iex   # doesn't need the parentheses

random这种情况下,可以对常见情况进行一些优化:

random -mi 10 31   # gets a random integer between 10 and 30
10..30|random      # much better :-)
(random 21)+10     # if needed in another expression that doesn't require extra
                   # parentheses

后一种使用它的方法只是从列表中选择一个项目。-c可以给出该参数以允许多个选择。


4

考虑将重复的脚本块存储在变量中,而不是使用函数。

在我意识到将函数重写为变量甚至使该变量变得不必要之前,我将使用它在我的Rock,Paper,Scissors实现中保存一些字符。但是,这对于其他脚本仍然很有用,因为在这些脚本中您实际上多次运行相同的代码。

function Hi{echo 'Hello, World!'};Hi

$Hi={echo 'Hello, World!'};&$Hi

2
对于不带参数的函数很有用。否则,params(...)将占用比函数定义节省的空间更多的空间。相关阅读:使用filterfunction的时候,你可以这样做。
乔伊,

$args可以避免使用params;即$x={$args[0]+2}(甚至$x={2+"$args"});在某些情况下可能会保存一个字符或2个字符。您还可以将其与另一个用于多个参数的技巧结合起来:$x={$a,$b=$args;$a+$b+3}
JohnLBevan'1

4

您可以使用$s|% t*y,而不是[char[]]$s分裂的字符串的字符数组。从TessellatingHeckler的答案中得出% t*y扩展为| ForEach-Object -Method ToCharArray等效。的"$args".ToCharArray()

例如比较

$s|% t*y

[char[]]$s

$s.ToCharArray()

$args特别适用于:$args|% t*y


1
那很整齐。我也%为会员使用过几次技巧,但是我的大多数高尔夫活动都以;-)为特色。这也是一种通用技术:尝试找到与所需属性/方法相匹配的字母/通配符组合(并且比真实事物短)。
乔伊'18

答案中的另一个有用示例:$s|% *perfor $s.toUpper()$s|% *werfor $s.toLower()。我同意那很整洁。
mazzy

再举一个例子|% t* "ddd\:hh\:mm\:ss"[TimeSpan].toString("ddd\:hh\:mm\:ss")
MAZZY

好吧,那只是在浪费报价。您在这里不需要它们:-)
乔伊(Joey)


3

在循环中使用布尔逻辑代替if计数器

假设您要将所有偶数加到1到10 ... 2+4+6+8+10=30

1..10|%{if($_%2-eq0){$o+=$_}};$o

您可以使用布尔否定符将其缩短为

1..10|%{if(!($_%2)){$o+=$_}};$o

保存一个字节,但是如何使用布尔值的隐式转换为整数,并将条件转换为累加器

1..10|%{$o+=$_*!($_%2)};$o

在此示例中节省6个字节。


2
耶稣基督。我计划在工作中的生产代码中执行此操作,我的同事会为此而爱。
查韦斯

3

在PowerShell中将浮点数转换为整数有点麻烦。默认情况下,该转换会进行“ 银行取整”操作,该操作并不总是舍去小数并保留较小的整数,或者像人们随便做的那样始终将0.5向上舍入到下一个数字,它会以一种方式四舍五入为偶数,以另一种方式为奇数-这可以令人惊讶,例如

PS C:\> [int]1.5
2

PS C:\> [int]2.5
2

并中断codegolf计算。许多其他通用语言也会进行截断取整,因此高尔夫球问题通常需要截断。您可能会碰到[Math]::Floor()下一件最好的事情,但是请注意,这仅与正数截断的行为相同,但是负数要低一些-远离零。[Math]::Truncate()您需要使PS行为与其他语言的默认四舍五入保持一致,但这是很多字符。

正则表达式替换小数点后的数字可以帮助节省几个字符:

[Math]::Truncate(1.5)
[Math]::Floor(1.5)
1.5-replace'\..*'

[Math]::Truncate($a)
[Math]::Floor($a)
$a-replace'\..*'
$a.split('.')[0]        # literal character split, not regex pattern split

3

反转数组

在许多以形式反映输出挑战中派上用场。

假设你有

$a=1,2,3,4,5

传统的反转方法很长,很无聊,并且不会将数组留在管道上立即使用。

[array]::reverse($a)

以相反的顺序直接索引到数组中可以节省一些字节,因为它将结果留在管道上,但是仍然很长:

$a[($a.count-1)..0]

而是尝试循环

$a|%{$a[-++$i]}

当计数有上限时,反向索引会很好地工作
Joey

3

从PowerShell Core 6开始,您还可以使用字符范围:

'a'..'z'

可以代替繁琐的工作

0..25|%{[char]($_+97)}

哦,这将非常方便。
AdmBorkBork

1
[char[]](65..90)也是生成字母的便捷方法
Veskah
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.