tech.guitarrapc.cóm

Technical updates

「シェルスクリプト大喜利 第八回 ~"for,while禁止"に喜びを感じる人の何と多い事か!」をPowerShellでやってみた

毎度おなじみのシェル芸です。 前回の挑戦はこちら
第2回チキチキ!シェル芸人養成勉強会をPowerShellでやってみた Hbstudy#38シェルスクリプトでいろいろやってみよう!をPowerShellでやってみた
たまたまUPS友の会様を覗いていたらあるじゃないですかー。 ということで、今回のお題はこれです。
シェルスクリプト大喜利 第八回 ~"for,while禁止"に喜びを感じる人の何と多い事か!
曰くマゾな人が多い? ん~、forとかwhileは……なるべくなら避けてるので…
この投稿のサブタイトルにもありますように、"for,while禁止"という苦痛を伴うお題が今回あるんですが、ナゼかこれに対する投稿が殺到しとります。
早速やってみました。 あと、繰り返しですが…一応。
※シェル環境前提なので、なるべくAliasを利用しているのはご了承ください。 ※私はAlias余り好きじゃない派です。 ※PowerShellとBashの大きな違いは | (パイプ)で渡されるのが文字列ではなくオブジェクトということを念頭に…
Get-Content #cat #gc
Foreach-Object #%
Sort-Object #sort

第1問 整数数列ネタ其の三「累乗数」

お題はこれです。
1~n(nは引数で指定)の自然数の中の累乗数を全て求めるシェルスクリプトを書いてください。 累乗数とは何かと言うと、a = bc (a,b,cは全て自然数)と書き表せる数aのことです。
加えてこの縛りです。 (縛りなのか逆に謎)
これまで寄せられている投稿を見ると、1を特別扱いしているものが多いのですね。もちろん特別扱いしようが答えをちゃんと出してくれさえすれば正解なのですよ。でもやっぱり1を特別扱いしないプログラムになっているとポイント高いですね。
では、PowerShellで…nは引数で渡せってことなのでfunctionとも思いましたが一応ワンライナーで解いてます。って、牟田口先生から累乗数はそうじゃないと指摘をいただきました。 このワンライナー結果の下に修正したものを追記しています。
begin{$n=10}process{1..$n | %{ $x=$_ ; 1..$n | % {"{0,-5}, {1}" -f "$x^$_",[System.Math]::Pow($x,$_)}}}
結果表示です。
1^1  , 1
1^2  , 1
1^3  , 1
1^4  , 1
1^5  , 1
1^6  , 1
1^7  , 1
1^8  , 1
1^9  , 1
1^10 , 1
2^1  , 2
2^2  , 4
2^3  , 8
2^4  , 16
2^5  , 32
2^6  , 64
2^7  , 128
2^8  , 256
2^9  , 512
2^10 , 1024
3^1  , 3
3^2  , 9
3^3  , 27
3^4  , 81
3^5  , 243
3^6  , 729
3^7  , 2187
3^8  , 6561
3^9  , 19683
3^10 , 59049
4^1  , 4
4^2  , 16
4^3  , 64
4^4  , 256
4^5  , 1024
4^6  , 4096
4^7  , 16384
4^8  , 65536
4^9  , 262144
4^10 , 1048576
5^1  , 5
5^2  , 25
5^3  , 125
5^4  , 625
5^5  , 3125
5^6  , 15625
5^7  , 78125
5^8  , 390625
5^9  , 1953125
5^10 , 9765625
6^1  , 6
6^2  , 36
6^3  , 216
6^4  , 1296
6^5  , 7776
6^6  , 46656
6^7  , 279936
6^8  , 1679616
6^9  , 10077696
6^10 , 60466176
7^1  , 7
7^2  , 49
7^3  , 343
7^4  , 2401
7^5  , 16807
7^6  , 117649
7^7  , 823543
7^8  , 5764801
7^9  , 40353607
7^10 , 282475249
8^1  , 8
8^2  , 64
8^3  , 512
8^4  , 4096
8^5  , 32768
8^6  , 262144
8^7  , 2097152
8^8  , 16777216
8^9  , 134217728
8^10 , 1073741824
9^1  , 9
9^2  , 81
9^3  , 729
9^4  , 6561
9^5  , 59049
9^6  , 531441
9^7  , 4782969
9^8  , 43046721
9^9  , 387420489
9^10 , 3486784401
10^1 , 10
10^2 , 100
10^3 , 1000
10^4 , 10000
10^5 , 100000
10^6 , 1000000
10^7 , 10000000
10^8 , 100000000
10^9 , 1000000000
10^10, 10000000000
結果だけ出力するならこれでも。
begin{$n=10}process{1..$n | %{ $x=$_ ; 1..$n | % {[System.Math]::Pow($x,$_)}}}
[13/Feb/2013] 牟田口先生から、累乗数はそうじゃないと指摘をいただきました。 ご指摘を受けて、修正します。 単純に、小さい方から順に10個ならこうです。
1..10 | %{ $x=$_ ; 2..10 | % {[System.Math]::Pow($x,$_)}} | sort -Unique | select -First 10
出力結果です。
1
4
8
9
16
25
27
32
36
49
引数で渡せるようにします。 $scriptの小さい方から後ろに取得したい数を入れます。(一応最大数を大きくしてますが漏れがある場合は1..99と2..99を大きくすれば…!) 下のサンプルでは20個まで取得しています。
$script={param($n)1..99 | %{ $x=$_ ; 2..99 | % {[System.Math]::Pow($x,$_)}} | sort -Unique | select -First $n};& $script 20
改行するとこうです。
$script={param($n)1..99 | %{ $x=$_ ; 2..99 | % {[System.Math]::Pow($x,$_)}} | sort -Unique | select -First $n}
& $script 20
出力結果です。
1
4
8
9
16
25
27
32
36
49
64
81
100
121
125
128
144
169
196
216

第2問 同一行内ソート

求める結果はこれだそうです。
つまり英文字ソートをした結果文字列を、元の文字列の手前に挿入し、一行二単語にする。ただしデータは標準入力から受け取る、という動作です。
これは、もう某星人に教わったLinqの御力の断片をお借りしてみたいわけで。(めそっどちぇーん とか 構文じゃなくてごめんさない) 他の解法とかどうでもいいです。(おい ちなみにここで教わっています。
PowerShellでStringをChar[]に変換する
まずは、指定のファイルを作ります。
@("pans","pots","opt","snap","stop","tops") | Out-File -Encoding default -FilePath .\dic.txt
読んでみましょう。
cat dic.txt
出来てますね。
pans
pots
opt
snap
stop
tops
では、お題をワンライナーでサクッと
cat dic.txt | %{"{0} $_" -f (("$(([system.Linq.Enumerable]::ToArray($_)) | sort )") -replace " ") }
Linq!!Linq!!
anps pans
opst pots
opt opt
anps snap
opst stop
opst tops

第3問 for,while文なしの九九

求める結果はこれだそうです。
for文やwhile文を一切使わずに、九九の表を書いてください。   01*01=01 01*02=02 01*03=03 … 01*09=09   02*01=02 02*02=04 02*03=06 … 02*09=18             :             :   09*01=09 09*02=18 09*03=27 … 09*09=81 といった感じです。
ワンライナーでサクッと
1..9 | %{ "$($_ | %{ $x=$_; 1..9 | %{ "{0:00}*{1:00}={2:00}" -F $x, $_, $($_ * $x) }})"}
結果です。
01*01=01 01*02=02 01*03=03 01*04=04 01*05=05 01*06=06 01*07=07 01*08=08 01*09=09
02*01=02 02*02=04 02*03=06 02*04=08 02*05=10 02*06=12 02*07=14 02*08=16 02*09=18
03*01=03 03*02=06 03*03=09 03*04=12 03*05=15 03*06=18 03*07=21 03*08=24 03*09=27
04*01=04 04*02=08 04*03=12 04*04=16 04*05=20 04*06=24 04*07=28 04*08=32 04*09=36
05*01=05 05*02=10 05*03=15 05*04=20 05*05=25 05*06=30 05*07=35 05*08=40 05*09=45
06*01=06 06*02=12 06*03=18 06*04=24 06*05=30 06*06=36 06*07=42 06*08=48 06*09=54
07*01=07 07*02=14 07*03=21 07*04=28 07*05=35 07*06=42 07*07=49 07*08=56 07*09=63
08*01=08 08*02=16 08*03=24 08*04=32 08*05=40 08*06=48 08*07=56 08*08=64 08*09=72
09*01=09 09*02=18 09*03=27 09*04=36 09*05=45 09*06=54 09*07=63 09*08=72 09*09=81
[13/Feb/2013] 牟田口先生から、foreachもだめではと指摘があり……え…。 先生がここで再帰でとかれています。 やられてることは、foreachの内容を分解して$out変数にstring書き込み、これをforと同じ要領で引数で渡した$x,$yそれぞれを1..9まで順次実行ですね。
$out=""
$s={
    param($x,$y)
    $script:out+="0$x*0$y=$("{0:00}" -f ($x*$y))"
    if($y -lt 9)
    {
        $script:out+=" "
        $y++
        &$s $x $y
    }
    elseif($x -lt 9)
    {
        $script:out+="`n"
        $x++
        &$s $x 1
    }
}
&$s 1 1
$out

まとめ

楽しかったです!! ただ、ちょっとシェル芸のアレと比べると簡単すぎたような……。 他の解法がどんどんでそうですね。