• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:文字の整列(printf))

文字の整列(printf)でパスカルの三角形をセンター揃えに表示する方法は?

このQ&Aのポイント
  • 質問者は、printfを使用してパスカルの三角形を表示していますが、表示部分のコードでは簡単にセンター揃えができないことに気づきました。
  • 質問者は、manページにはさまざまな関数があることを知り、どの関数を使うべきかについて疑問を抱いています。
  • 質問者は、一番長い関数名を使うことがレベルの高さを示すのかどうかについても尋ねています。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

お返事が遅くなって申し訳ありません。 > 「# パスカル三角形の数値を計算し、2次元配列で返す」の箇所から既にわかりません。 > @$や\@の基本的な意味は書籍に書いてありますが、コードを追うことが困難です。Cのポインタは多少わかりますので、リファレンスも同じようなものだという理解はあります。 ここでの配列の使い方は、C言語で言うところのmallocによるポインタ配列の確保と同じことをやっています。 配列outputは配列のポインタを入れるための配列で、outputの各要素に動的に生成した配列のポインタを格納しています。 これにより、多次元配列が実現できます。 動的な配列生成やmallocの意味がわからない場合は、そちらから先に勉強してください(^^; Cの入門書でも、mallocに関する話は必ず載っていると思いますし。 あと、「ポインタのポインタ」も理解しておくといいと思います。プログラムをやっていくならこの概念の理解は必須です。 > $rangeと@outputの宣言でshiftや()を代入しているところはなぜそうしているのかわかりません。 > 「my $range;」や「my @output;」だけではないのは、どのような意図があるのでしょうか? > 特に、$rangeはforeachで1..$rangeとしているので、不思議です。 一番最初の my $range = shift; でスコープされている変数は@_です(つまり関数の引数)。 関数pascal_triangleの引数はパスカルの三角形の深度をあらわします。 @outputの宣言時に空配列()を代入しているのは、デバッグ段階の試行の名残です(^^; my $output;もmy $output = ();も結果は同じなので気にしないでください。 > それと2次元配列が理解できていません。最初にpush(@output,[1,1])というのは、2つの1を@outputに入れているようには見えます。 > しかし、その後のコードでは加算した1つの値を@aryに代入しているだけのように思えます。 > (@aryは1次元の配列でしかないように見えます) > 配列の使い方が視覚的に表現されていればわかりやすいですが…。 > C言語では、配列のどこにどんな値を入れるかがarray[x][y]=valueという形で見やすいですが、Perlはそれが見えにくいです。 一番上にも書いたように、配列の要素を動的に生成しています。 ご指摘の通り、@aryは普通の一次元配列です。 @aryが各辺のデータで、@outputには@aryのリファレンス(ポインタ)が配列で入っています。 2次元配列の中身のデータを先にmallocしてるようなものです。 > push(@ary,1); > for(my $j = 0; $j <= $#{$output[$i-1]}-1; $j++) > { > push(@ary,${$output[$i-1]}[$j] + ${$output[$i-1]}[$j+1]); > } > push(@ary,1); 上の部分は、まず@aryの中に、パスカル三角形の次の辺を作ります。 (一番最初の辺は[1,1]という配列です。次の辺は[1,2,1]になりますね) > push(@output,\@ary); この部分で、作った辺のデータ@aryのリファレンスを@outputに格納します。 データのイメージとしては、 @output = [  [1,1]  [1,2,1]  [1,3,3,1]  [1,4,6,4,1] ←中の配列一つ一つはもともと@aryだった    :    : ] 正確な書き方ではありませんが、イメージとしてはこんな感じです。 多次元配列のアクセスのしかたとして、配列の次元要素ごとに{}で囲って${$output[1]}[2]みたいな書き方もできます…が、これは理解の外でいいです。もっとリファレンスに慣れてから。 > 一番大きい数値を探すところで、$max = $n if($max<$n)という書き方があるんですね。 Perlでは、処理部分がが1行だけのifはif節を倒置して書くことができます。 便利ですがソースが読みにくくなります…

noname#17299
質問者

補足

こんにちは、twinkleluzさん。 丁寧なご回答にとても感謝しています。 徐々に理解できてきましたので、もう少しだけおつき合いください。 1ループごとに各変数の内容を表示してみたところ、どのように処理されているかよくわかるようになりました。 ------------------------------------------ 121 ARRAY(0x9e62c28)ARRAY(0x9e7af24) 1331 ARRAY(0x9e62c28)ARRAY(0x9e7af24)ARRAY(0x9e7b050) 14641 ARRAY(0x9e62c28)ARRAY(0x9e7af24)ARRAY(0x9e7b050)ARRAY(0x9e7b0b0) ------------------------------------------ ARRAY(0x9e62c28)などリファレンスが変数名になっているのがわかります。 実務ではこのように書くのが一般的なのでしょうか? 私はまだメモリの内容まで意識してプログラミングできません。 それと、shiftの意味がまだよくわかっていません。 Webや本を調べると、先頭の要素を取り出すとあります。 また、返す値がない場合はundefを返すともあります。 ですが、次のフィボナッチ数列を再帰処理で書いたものをshiftで初期化するのとしないのでは、なぜか結果が異なります。 返す値が何もない時はundefを返すのであれば、undefをあらかじめ$xに代入しておいてもよさそうですが、それだとうまく動作しません。 (引数が5の場合、shift有りだと正しく8と表示され、shift無しだと1になります) #--------------------------------------# #!/usr/bin/perl sub fibonacci { my $x=shift; # my $x; ($x == 0 || $x == 1) ? 1 : &fibonacci($x - 1 ) + &fibonacci($x - 2); } print &fibonacci(@ARGV[0]); print "\n"; #--------------------------------------# よろしくお願いします。

その他の回答 (4)

回答No.5

お返事が遅くなりました。 > 実務ではこのように書くのが一般的なのでしょうか? > 私はまだメモリの内容まで意識してプログラミングできません。 実務で一般的かどうかはわかりません。 が、プログラミングの一般的な方策として、メモリ管理をうまく行うことにより 効率化や高速化を図ることができます。 メモリ管理が上手い人とそうでない人の差は大きいです。 頑張って慣れてください。 shift関数についてです。 引数の配列の先頭の要素を抜き出して返します。 @list = (1,2,3,4,5); という配列があったとき、 $x = shift(@list); とすると、$xには1が入り、@listは @list = (2,3,4,5) となります。 引数を省略すると、サブルーチン内では@_、メインルーチン内では@ARGVが対象になります。 返す値がundefになるのは、引数の配列にもう要素がないときです。 空の配列に対してshiftを行うと、undefが返ります。 undefは未定義を表し、変数が未定義かどうかはdefined関数を使って調べることができます。 > #!/usr/bin/perl > > sub fibonacci { > my $x=shift; > # my $x; > ($x == 0 || $x == 1) ? 1 : &fibonacci($x - 1 ) + &fibonacci($x - 2); > } > print &fibonacci(@ARGV[0]); > print "\n"; このプログラムですが、 > my $x=shift; この部分で、関数の引数の最初の要素を$xに入れています。 shiftで引数の値がはいる理由は、前述の通り省略時には@_が対象になるからです。 my $x;だけの場合、$xは何も中身がない変数として初期化されます。 中身がない変数は、一番最初に数値として評価されたとき、0として扱われます。 なので、 > ($x == 0 || $x == 1) ? 1 : &fibonacci($x - 1 ) + &fibonacci($x - 2); この行で$xは0とみなされ、結果として1が帰ってきます。

noname#17299
質問者

お礼

ありがとうございます。 スッキリしました。 Perlでお決まりの、対象を省略した場合に対象になる$_や@_ですね。 print文などで対象を省略した場合の例はテキストにもありましたが、 頭が固いせいか、shift関数でもそうなっていたとは気づきませんでした。 ただ文法を覚えるだけじゃなくて、いろいろ可能性を考えて実験もしてみます。 インタプリタのPerlでは、メモリ管理による効率化・高速化は必須ですね。 twinkleluzさんのプログラミングのテクニックを見習ってハイレベルなプログラミングができるようにがんばります。 丁寧にご回答いただき、本当にありがとうございました。

回答No.3

どのあたりがわからないでしょうか? リファレンスの部分は確かに難しいと思うので、参考URLにリファレンスの説明が載っているリンクを張りました。 (ソース中で@$や\@とかで始まっている変数のお話です) C言語のポインタの理解があれば、リファレンスの理解も早いと思います。 アルゴリズムについてはかなりベタな書き方をしているつもりですが、わからない箇所を指摘してください。 ちなみに、このコードを書くのにかかった時間はだいたい30-40分くらいだったと思います。

参考URL:
http://www.rfs.jp/sb/perl/02/10.html
noname#17299
質問者

補足

こんばんは、Twinkleluzさん。 「# パスカル三角形の数値を計算し、2次元配列で返す」の箇所から既にわかりません。 @$や\@の基本的な意味は書籍に書いてありますが、コードを追うことが困難です。Cのポインタは多少わかりますので、リファレンスも同じようなものだという理解はあります。 $rangeと@outputの宣言でshiftや()を代入しているところはなぜそうしているのかわかりません。 「my $range;」や「my @output;」だけではないのは、どのような意図があるのでしょうか? 特に、$rangeはforeachで1..$rangeとしているので、不思議です。 それと2次元配列が理解できていません。最初にpush(@output,[1,1])というのは、2つの1を@outputに入れているようには見えます。 しかし、その後のコードでは加算した1つの値を@aryに代入しているだけのように思えます。 (@aryは1次元の配列でしかないように見えます) 配列の使い方が視覚的に表現されていればわかりやすいですが…。 C言語では、配列のどこにどんな値を入れるかがarray[x][y]=valueという形で見やすいですが、Perlはそれが見えにくいです。 一番大きい数値を探すところで、$max = $n if($max<$n)という書き方があるんですね。 @pascal_triがどういった形で値が格納されているか見えないので、整形する部分はまだ解読していません。 現時点ではその程度の理解です。 よろしくお願いします。

回答No.2

printfでは中央揃えというのはできないので、桁と数値を与えて中央揃えに整形した文字列を出力する関数を自作する必要があります。 また、各数値の桁揃えをするためには最終行まで一度計算しなければならないので、最終行までの数値を2次元配列にして保持しておきます。 さらに、各行を中央揃えにするため、一番下の行の長さを元に各行の両端にどのくらいスペースを追加すればいいかを計算し、両端にスペースを追加します。 そうするとソースは以下のようになります。 @pascal_tri = &pascal_triangle(10); # 桁数計算 $max = &search_max(@pascal_tri); $scale = length($max); # 数値を中央揃えにして出力(1行ごとの配列) foreach $line(@pascal_tri) { $out_line = ""; foreach $n(@$line) { $out_line .= &center_print($n,$scale).' '; } push(@output,$out_line); } # 両端のスペースがいくつあればいいかを計算する(@space_countに保存) @pascal_tri = @output; $base_count = length($pascal_tri[$#pascal_tri]); @space_count = map(int(($base_count - length($_)) / 2),@pascal_tri); @output = (); # 両端にスペースをくっつける for($i = 0; $i <= $#pascal_tri; $i++) { $count = $space_count[$i]; $line = $pascal_tri[$i]; push(@output, ' ' x $count . $line . ' ' x $count); } # 各行を改行で連結して出力 print join("\n",@output); # パスカル三角形の数値を計算し、2次元配列で返す sub pascal_triangle { my $range = shift; my @output = (); # 初期値の挿入 push(@output,[1,1]); foreach my $i(1..$range) { my @ary; push(@ary,1); for(my $j = 0; $j <= $#{$output[$i-1]}-1; $j++) { push(@ary,${$output[$i-1]}[$j] + ${$output[$i-1]}[$j+1]); } push(@ary,1); push(@output,\@ary); } return @output; } # パスカル三角形の数値で一番大きいものを探し出す sub search_max { my @pascal_tri = @_; my $max = 0; foreach my $line(@pascal_tri) { foreach my $n(@$line) { $max = $n if($max < $n); } } return $max; } # 中央揃えで出力する関数。$nは数値、$scaleは桁数。 # 桁が奇数の時は左側にスペースが1つ多く入る sub center_print { my $n = shift; my $scale = shift; my $length = $scale - length($n); my $left = 0; my $right = 0; if($length % 2 == 1) { $length--; $left++; } $left += int($length/2); $right += int($length/2); return ' ' x $left . $n . ' ' x $right; }

noname#17299
質問者

補足

ご回答ありがとうございます。 レベルが高いので理解が困難となっています。 もう少しコメントを多くして解説していただけませんでしょうか? 配列の使い方とpushのところが一応の基本理解はありますが、 応用になるとわからなくなってしまいます。 それと、twinkleluzさんはプロのプログラマーでしょうか? プロの人がこのコードを書くのにどのくらいの時間を要するのでしょうか? 質問をしてからそれほど時間が経ってないので驚きです。 よろしくお願いします。

回答No.1

左側にスペースを入れて山のような感じで表示したいということですか? スペースを最初にいくつ入れるかについては規則性があるからそれを考えればいいだけでは? (段数に応じて何段目はいくつ左にスペースを入れる、というのは数えれば分かります。分かったらあとは規則を見付ければいいだけ)。 > また、man page of printfには それってC言語のことでは? perl のマニュアルページは perldoc ですよ。組込み関数は perldoc -f printf のようにして出します。で、Perl の場合は print と sprintf と printf (それと IO::Handle の print と printf) だけで全てのことができます。

noname#17299
質問者

補足

お返事ありがとうございます。 man page of printfはC言語だったとは気づきませんでした。。。 perldocというものがあるのも知りませんでした。 さすがにセンター揃えをする関数はないということでしょうか。 よくよく考えると、表示させる値の桁数を固定長にしない限り、 最終行を計算しないとセンターが出せませんね。 通常のプログラミングでは、左のスペースで調整するのを理解しました。 各行を表示する前に以下のコードを入れ、 for ($k=1; $k<=$n-$i; $k++){ printf "スペース4つ"; } 数値の桁数を8桁に固定してみました。 printf("%8d",$combination); スペースの個数は4で桁数の半分です。 これを実行すると山型にはなります。 任意の桁数に対応させたい場合はどのようにしたらよいのでしょうか? printfの""の中の8が困りました。 よろしくお願いします。

関連するQ&A