しぐまろぐ

勉強したことや読んだ本について書きます。

シェル・ワンライナー160本ノック 問題6

問題6:端末に模様を描く

以下のような模様を出力する。

     x
    x
   x
  x
 x

解答

ワンライナーでfor文をネストする方法がよくわからなかったので、とりあえず複数行で書いてみた。

for(i=0; i<5; i++) {
	for (j=0; j<i; j++) {
		print(" ")
	}
	print("x") 
}

これをワンライナーにしたのが以下。練習1.3.fと参考サイトを参考にした。

$ awk 'BEGIN{for(i=0; i<5; i++){for(j=0; j<i; j++){print " "}{print "x"}}}'

これだと最初のスペースで改行されてしまうので、printではなくprintfにする。
さらに、1つ目のfor分の条件で、i=0だと模様が上下逆になる(1行目がスペースなしになってしまう)*1ので、修正。
解答は以下、これで例通りの出力が得られた!

$ awk 'BEGIN{for(i=5; i>0; i--){for(j=0; j<i; j++){printf " "};{print "x"}}}'

別解

本にたくさん別解が載っているのだが時間がないのでまた後日試して加筆します。→ 2023.01.07追記。

別解1
$ seq 5 | awk '{for(i=0; i<$1; i++){printf " "};print "x"}}' | tail -r
  • tail rオプション:出力を逆順にする。本ではtacコマンドを使用しているが、macでは使用できなかったため、こちらで代用。

私の解答と比較すると、解答で外側のfor文が担っていた役割をseqが果たしているようだ。
{printf ""}の後の;までがfor文の影響範囲で、i回分スペースを出力したらfor文が終わってxを出力するというふうになっているのだと思う。

別解2
$ echo -e "    x\n   x\n  x\n x\nx"

echo力技編。これも全然あり。

別解3, 4
$ seq 5 | awk '{a++;for(i=5; i>a; i--){printf " "};print "x"}'
$ seq 5 | awk '{for(i=5; i>NR;i--){printf " "};print "x"}'
  • NR :行番号が入るawkの変数。例は以下の通り。
$ seq 5 | awk '{print NR}'
1
2
3
4
5
別解5
$ seq 5 -1 1 | awk '{for (i=1; i<$1; i++){printf " "};print "x"}'
  • seq [OPTION]... FIRST INCREMENT LAST : INCREMENTを-1にすると減少させていける。
別解6
$ seq 5 -1 1 | awk '{printf "%*s\n", $1, "x"}'
  • %数字s : 文字列の長さが指定した長さになるように左に余白を入れる。上記の回答では、*にseqから数字を引数で渡している。
別解7
$ printf "%*s\n" 5 x 4 x 3 x 2 x 1 x

別解6と同じ感じで、余白の個数を与えてから%sにxを入れる、を繰り返している。

*1:変に勘違いしていたが、一番古い出力は一番上にくる