#でピラミッドを出すまでの4ステップ:写経から構造へ

0001_二重ループ learn

🎯 問題

#を使って三角形を出力してください。

次のような出力が得られたらOKです。

    #
   ###
  #####
 #######
#########

📘はじめに:#でピラミッドを出せるやつは、構造を知っている

「#でピラミッドを出力せよ」──こう聞いて、
似たコードを検索してコピペして動いたら満足してるようじゃ、おまえの手は止まる。

オレは「写経」自体を否定はしない
でもな、“なぜそのコードになるのか”がわからないまま書き続けてるやつは、早晩詰む。

このサイトは「Just keep typing」=とにかく打て!を合言葉にしてるが、
それは“書きながら考えること”を放棄しろって意味じゃない

今回はこの問題を、4つのステップに分けて
「日本語で考える→コメントを書く→コードにする」
って流れを、おまえの中に刻み込む。

以下の中から、自分の理解レベルにあった場所から読み始めてくれ。

  • 🔰 forループがまだピンときてない人は → ステップ1
  • 🔁 二重ループ(ネスト)が怪しい人は → ステップ2
  • 📊 ループの回数を変数によって変化させるのが難しい人は → ステップ3
  • 🧩 この問題(ピラミッド出力)に直接チャレンジしたい人は → ステップ4

ステップ1:一重ループで # を5つ横に並べろ

いきなり難しい問題に立ち向かうのではなく、一歩ずつやっていこう。
まずは、いきなりピラミッドを描こうとするのではなく、「#を5つ横に並べる」ということからだ。

🎯ステップ1 問題

一重のfor文を使って、横に ##### を出力せよ。

// こう表示されたら正解
#####

🧠 まず、日本語で考えろ

・#を5個出力したい
・1個ずつループで出せばよさそう
・1回出力するたびに、次の#を出す
・改行はいらない(1行で終わらせたい)

✏️ コメントを書け

いきなりfor(int i=0; …などと書き始めるんじゃない。
とにかく今から書こうと思っているコードのアルゴリズムを日本語にして、それをコメントとして書き始めるのだ。

// #を5回出力する
// 改行しないで出力する

💻 コメントに従ってコードを書く

考えたアルゴリズムをコメントとしてちゃんと書くことができたら、そこからやっとコードを書き始める。コメントを見ただけで、どういう道筋で処理すれば求められているところにたどり着くか、ということがはっきりするまではまだコーディングを始めてはだめだ。

この問題に関しては下記のようなコードになる。

// #を5回出力する
// 改行しないで出力する
for (int j = 0; j < 5; j++) {
    System.out.print("#");
}

この「コメント→コード」の対応が見えるか?
見えないやつは「コメントって書き方のマナーでしょ?」とか言い出す。
違う。これはおまえの思考の軌跡なんだよ。

まず、#を横並びに表示することについて。
何かを表示するにはSystem.out.printlnって覚えたと思うが(覚えたよな?)、ここでは改行を入れずに連続して#を5つ入れたいのでprintlnではなく、printを使う。

  • System.out.print() は、改行せずに出力を横に並べる
  • System.out.println() は、出力のあとに改行する

この違いを知らずに使うと、「全部同じ行に出てしまった」や「1文字ずつ縦に出力された」など、出力が意図通りにならない原因になる。

// tesh:
// 改行はしない。System.out.print() を使え。
// j は列を表すカウンタ。
// iじゃなくてjを使う理由? それは次でわかる。

そして、もう一つ。

なぜ j = 1; j <= 5; j++じゃなくて j = 0; j < 5; j++なのか?

答えは、ほとんどのプログラミングは0開始で考えるからだ。 配列もカウントも、インデックスも「最初は0」で始まる。

だから、自分の頭を「ずらしていく」感覚を持っとけ。

隣のやつは1から数えてるかもしれない。 だけど、オレたちプログラマは、たった1ずらすことで全体を知るフリしやすさを手に入れてる。

なぜ「0始まり + < 比較」が良いのか?

1. 配列やリストが0始まりだから

  • JavaでもCでもPythonでも、配列のインデックスは0から始まる。
  • array[0] が最初の要素、array[length - 1] が最後の要素。
  • for (int i = 0; i < array.length; i++) は鉄板パターン。

2. 「範囲」の思考に合ってる

  • i = 0; i < 5; i++ → 「0以上、5未満」=半開区間 [0, 5) の発想。
  • この考え方は数学でも、日付範囲でも、UIでもよく使われる。

3. ループの数が一目でわかる

  • i = 0; i < 5; i++ → 明確に「5回」だと分かる。
  • i = 1; i <= 5; i++ → 慣れてないと境界が不明瞭になりやすい。

4. エラーが起きにくい

i < length のほうが安全設計。

i = 0; i < n; i++ で書け」っていうのは、
“配列とループの思考を一致させるクセ”をつけるってことだ。

最初の一歩で世界の“数え方”とズレるな。
オレたちは常に0から始める人間だ。

ステップ2:それを5行繰り返せ(二重ループ)

ステップ1で描いた、横に5つ並んだ#を今度は縦に5行繰り返すんだ。

🎯ステップ2 問題

横に ##### を出す処理を、5行分繰り返せ。

// 以下のように表示されること
#####
#####
#####
#####
#####

🧠 アルゴリズムを日本語で考える

・1行分の#を出す処理はもうできてる
・それを5回繰り返したい
・つまり、外側のループで「行」、内側で「列」
・1行出したら改行する

✏️ コメントを書く

// 5行分繰り返す
// 1行ごとに、#を5個出力する
// そのあと改行する

💻 コメントを書いてからコードを書く

// 5行分繰り返す
for (int i = 0; i < 5; i++) {

    // 1行ごとに、#を5個出力する
    for (int j = 0; j < 5; j++) {
        System.out.print("#");
    }

    // そのあと改行する
    System.out.println();
}
  • for (int i = 0; i < 5; i++) は、全体で5行繰り返すためのループ
  • その中に、for (int j = 0; j < 5; j++)で#を5個出力
  • System.out.println()は改行するため。()の中に何も書かない場合は「改行」を意味する。
  • この改行は内側(j)ループが終わったところ、外側(i)ループの内側、ということに注意。つまり、#を5個出した度に改行する。

ここで「ネスト」って言葉が初めて出てくる。ネストってのは、ループの中にループをぶち込むことだ。構造を増やせば、出力は表情を持ち始める。
forの中にforが入ってる=“構造が深くなってる”ってことだ。
これを頭の中でちゃんと“縦横のマス目”として思い描けるか?

// tesh:
// ・外側が「行」、内側が「列」。
// ・内側が動いてから1行改行=ネストの基本。
// 「forの中にfor」──これが書けないやつは、構造を見てない。
// 縦×横、行×列、マス目…そういう空間の意識を持て。

ステップ3:階段型に出せ(出力の数が変化する)

🎯問題

次のような出力をせよ:

#
##
###
####
#####

🧠 アルゴリズムを日本語で考える

・1行ごとに、#の数が増えていく
・1行目は1個、2行目は2個...
・つまり、行番号(i)に応じて # の数を変える

✏️ まずコメントを書け

// 5行分繰り返す
// 各行ごとに、#をi個出力する
// 出力のあとに改行する

// 各行ごとに、#をi個出力する」って?

1. 「各行ごとに」

  • for (int i = 1; i <= 5; i++) という外側のループが「行」を担当している。
  • このループが1回まわると、1行分の出力が行われる。

2. 「#をi個出力する」

  • i は現在の行番号。
  • たとえば i = 3 のときは、3個の # を出力すればいい。
  • つまり「行番号 = 出力する#の個数」という直接対応関係がある。

3. どうやって「i個出力」するか?

  • for (int j = 0; j < i; j++) という内側のループを使う。
  • j = 0 から始めて、j < i まで繰り返すから、jの回数 = i回 になる。

i = 3 なら # を3つ打て。
それを「ループ回数 = 文字数」に置き換えるには、もう一個ループが必要だってことに気づけるかどうか。
「何個出力すればいいのか」は、たいてい何かの数字に比例してる
その「対応関係」を先に日本語にしてからコードに落とし込め。

💻 コメントに沿ってコードを書く

// 5行ぶん繰り返す
for (int i = 1; i <= 5; i++) {

    // 各行ごとに、#をi個出力する
    for (int j = 0; j < i; j++) {
        System.out.print("#");
    }

    // 出力のあとに改行する
    System.out.println();
}

・for (int i = 1; i <= 5; i++)
→ 5行分繰り返す。つまり「今は何行目か?」を i が教えてくれる。

・for (int j = 0; j < i; j++)
→ 出力する # の数は「その行の番号(i)」と同じだけ必要だから、j を i 未満まで回す。

System.out.print("#")
→ 改行せずに # を横に並べて打つ。

System.out.println()
→ その行の終わりで改行。次の行の準備をする。

このコードで注目すべきは、「i に応じて構造が変わっていく」ところだ。
ただの for 文を、“変化する構造を持ったもの”に変換できるかどうか。
それが“ただの写経”と“プログラミング”の境目だ。

// tesh:
// ・注目は内側のループの条件:j < i
// ・i によって 「1行の#の数が変わる」
// ・ここでやっと、「変化する構造」を出力する感覚がつかめる。
//
// ここが分かれ道だ。
// “ループは全部同じ回数”って思ってたやつは、今ここで殻を破れ。

ステップ4:ピラミッド形に出力せよ

        #
      ###
    #####
  #######
#########

このステップで大事なのは、単に # を増やすだけじゃない。 「左側に空白を置いて、ピラミッドの形を整える」という点だ。

つまり、

  • # の前に「空白」を出力する
  • その空白の数は行番号に応じて減っていく
  • 一方で、#数は奇数で増えていく(1, 3, 5…)

この「空白の数」と「#の数」の両方を計算し、それぞれループで出力するのがポイントになる。

じゃあまず、構造を言葉で考えてみよう。

🧠 アルゴリズムを日本語で考える

・行番号に対して # の数が増えていく
・その一方で、左側の空白(空欄)は減っていく
・それぞれ、空白の数 + # の数 + 空白の数 で1行を構成
・行番号が i だとしたら:
  - # の数 = 2 * i - 1
  - 空白の数 = totalRows - i

✏️ まず、コメントを書こうな(そろそろコメントから書き始めるクセはついたか?)

// 5行分繰り返す
// 各行のはじめに空白を (5 - i) 個出力する
// 次に # を (2 * i - 1) 個出力する
// 改行する

💻 コメントに沿ってコードを書く

// 出力する行数を変数で定義(ピラミッドの高さ)
int totalRows = 5;

for (int i = 1; i <= totalRows; i++) {

    // 左側の空白(中央揃えのため)
    for (int j = 0; j < totalRows - i; j++) {
        System.out.print(" ");
    }

    // # を 2*i - 1 個出力
    for (int j = 0; j < 2 * i - 1; j++) {
        System.out.print("#");
    }

    // 改行
    System.out.println();
}
  • i = 1 から i <= 5 まで:5行を処理する外側のループ
  • 5 - i 回空白を出力することで、中央揃えの形を作る
  • 2 * i - 1# を出力することで、行ごとに奇数個の # が並ぶ
  • 各行の最後に改行して、次の行へ移動する

これはまさに計算アルゴリズムの極み。 「繰り返し」だけでなく、「いま何行目か」によって形を変える。
ここでは i を使って、空白と # の数を両方導き出してる。 ロジックじゃなくて、ループ自体を計算で操れるようになったら一人前だ。

ここでは「いま何行目か」によって 「空白の数」も「#の数」も決まる。

計算はループの中に突っ込める。 ロジックじゃなくて、ループ自体を計算で操ること。

// tesh:
// ・空白と # を組み合わせて1行にしている
// ・それぞれのループで同じ j を使ってるが、スコープが違うから問題なし
// ・2 * i + 1 → 1,3,5,7…の奇数列を作ってる
//
// ここまできたら、おまえはもう
// 「写して動いた」じゃなくて「意味を読んで動かした」やつだ。
// 
// コードはな、思考の形を写す道具なんだよ。
// 書いて、読んで、また書け。Just keep typing, baby.

🧠 コードの意味、腹に落ちたか?

動けばOK?
見た目どおりに出力されたらそれで終わり?

──いや、そうじゃない。
「なぜこう書くのか」が自分の中に根を下ろして、
「次に自分が何をすべきか」を自分の手で選べるようになって、
はじめて、プログラムは“おまえのもの”になる。

for文も、ネストも、変数で変化する構造も、
全部「ただの道具」だ。
でもその道具を自分の身体感覚で使えるようになるまで、
書き続けろ。

Just keep typing, baby.
コードの意味は、あとからおまえが追いつけ。