PyPython で学ぶ建築デザインブラウザだけで動く GHPython 演習環境
数理・データサイエンス・AI 入門 第 13 回

関数・オブジェクト・リスト/GHPython でジオメトリデザイン

広島工業大学2025/12/22演習 6 問

処理に名前をつけてまとめる——それが関数です。リストと辞書を組み合わせれば、複数の要素を生成しながら同時に集計できます。ジオメトリの生成を「部品の組み立て」として書けるようにします。

1関数の定義

関数とは、入力(引数)を受け取り、決まった手順で計算を実行し、 結果(戻り値)を返すルールのことです。

組み込み関数Python に標準で用意されている関数(例:print(), input()
ユーザー定義関数def で自分で定義する関数
引数 48.0, 150 slab_cost() 厚みを m に直す 体積 × 単価を計算 戻り値(return) 100800.0 一度書けば、面積と厚みを変えて何度でも呼び出せる
関数は「入口と出口の決まった箱」。中身を一度書けば何度でも使い回せる。
書き方
# 関数の定義
def 関数名(引数名1, 引数名2, ...):
    処理1
    処理2
    return 値        # 関数を実行した結果(戻り値)
定義と呼び出し
# 二つの数字を足し算した結果を返す関数を定義する
def add_numbers(a, b):
    result = a + b
    return result           # 関数の戻り値を指定する

# 関数を呼び出す
sum_result = add_numbers(5, 3)
print(f"5 + 3 = {sum_result}")   # 出力: 5 + 3 = 8

ローカル変数とグローバル変数

ローカル変数関数の内部で代入した変数。その関数を実行している間しか参照できない
グローバル変数関数の外部で代入された変数。どこからでも参照できる
グローバル(ファイル全体) global_value = 100 ローカル(関数の中だけ) def test_global(arg): arg は関数の外からは見えない global_value は中からでも見える
外から中は見えるが、中から外へは出られない。
スコープを確かめる
# global_value はグローバル変数
global_value = 100

def test_global(arg):
    # ローカル変数 arg と グローバル変数 global_value の積
    return arg * global_value

print(test_global(10))   # 10 * 100

デフォルト引数

値が指定されなかったときに使われる既定値を、引数に持たせておけます。

デフォルト値
def func1(arg=999):
    print(arg)

func1()        # 引数を省略するとデフォルト値が使われる
func1(123)     # 指定すればそちらが使われる
宛名をデフォルト引数にする
# 資材の金額に宛名を付ける関数を定義する
def material_price(number_of_stone, number_of_plywood, name="上"):
    total_stone = number_of_stone * 500
    total_plywood = number_of_plywood * 2000
    total = total_stone + total_plywood
    print(name, "様:石材", number_of_stone, "個と合板", number_of_plywood, "枚で", total, "円です")

# 関数を呼び出す
material_price(10, 20)
material_price(10, 20, "萬屋")
P1スラブのコンクリ量と材料費

床スラブの面積と厚みから体積(m³)を求め、材料費を計算するプログラムを完成させなさい。厚みは mm、面積は m² で与えられます。

# 1m^3 あたりの材料費(円)
UNIT_COST = 14000

def slab_cost(area_m2, thickness_mm):
    # thickness_mm を m に変換
    thickness_m = ________
    # 体積(m^3)= 面積 × 厚み
    volume_m3 = ________
    return volume_m3 * UNIT_COST

result = round(slab_cost(48.0, 150))
print(result)
ヒント
関数の中では、引数の名前をそのまま変数として使えます。UNIT_COST はグローバル変数なので関数の中からも見えます。
P2資材の領収書

レンガと合板の購入金額に宛名を付けるプログラムを完成させなさい。宛名はデフォルト引数にしておき、省略もできるようにします。

brick = ______("レンガは何個買いますか?")          # レンガの個数を入力する
number_of_brick = ______(brick)                     # 文字列を整数値に変換する

plywood = ______("合板は何枚買いますか?")          # 合板の枚数を入力する
number_of_plywood = ______(plywood)                 # 文字列を整数値に変換する

# 資材の金額に宛名を付ける関数を定義する
def material_price(number_of_brick, number_of_plywood, ___ = "上"):
    total_brick = number_of_brick * 500
    total_plywood = number_of_plywood * 2000
    total = total_brick + total_plywood
    print(___, "様:レンガ", number_of_brick, "個と合板", number_of_plywood,
          "枚で", total, "円です")

material_price(number_of_brick, number_of_plywood)   # 宛名を省略
material_price(40, 70, ___)                          # 宛名を指定
ヒント
3 つの ___ はすべて同じ「宛名を入れる引数の名前」です。最後の 1 つだけは呼び出し側なので、文字列を渡します。

2オブジェクトとリスト

オブジェクトとは、Python が操作するいろいろな種類のデータや プログラムのことです。整数の 7、文字列の "art"、 関数の print() ——これらはすべてオブジェクトです。 ただし >=while といった実行命令はオブジェクトではありません。

リスト

リストは、決まった順番に並んだ値で構成されるデータです。 インデックス(順番)は 0 から数えることに注意してください。

リストとインデックス
stations = ["五日市", "新井口", "西広島", "横川", "新白島", "広島"]
print(stations)
print(stations[0], stations[5])   # インデックスは 0 から数える
insert / 置き換え / del
values = ['A', 'B', 'C']
print("修正前:", values)

values.insert(0, 999)     # 0 番目に挿入
print("修正後:", values)

values[1] = 'D'           # 1 番目を置き換え
print("修正後:", values)

del values[0]             # 0 番目を削除
print("修正後:", values)

辞書

辞書は、キーと値を対応づけるデータです。 「材料名 → 密度」「階名 → 面積」のように、名前で引きたいデータに向いています。 キーが登録されているかどうかは in 演算子で確かめられます。

辞書と in 演算子
english_words = {"apple": "りんご", "orange": "みかん", "peach": "もも"}
print(english_words)
print(english_words["apple"])

print("apple" in english_words)   # キーがあるか → True
P3資材の辞書

資材の辞書を読み込み、それぞれの名前と密度を表示するプログラムを完成させなさい。

materials = {"oak": 700, "cedar": 380, "steel": 7850}

___ name, rho in __________.items():   # in 演算子による反復処理
    ______(name, rho)
ヒント
辞書.items() は「キーと値の組」を順に取り出します。受け取る変数が 2 つ並んでいるのはそのためです。

ネスト

ネストとは、ある構造の中に別の構造を組み込むことです。 リストの中のリスト、ループの中のループ、条件文の中の条件文——いずれも設計の集計や 判定でよく使います。

ネストされたリストとループ
# ネストされたリスト
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(nested_list[0][1])   # [1,2,3] の [1] → 2

# ネストされたループ
for sublist in nested_list:
    for item in sublist:
        print(item, end=" ")
    print()
P4じゃんけんゲーム

じゃんけんゲームのプログラムを完成させなさい。ネストされた条件文で勝敗を判定します。

右の「標準入力」に 1(グー)・2(チョキ)・3(パー)のいずれかを書いて実行します。コンピュータの手は毎回ランダムに決まります。

import random

# じゃんけんの手のリスト
rock_paper_scissors = ["グー", "チョキ", "パー"]

# ゲーム開始のメッセージ
______("じゃんけんしましょう")
______("番号を入力してね")
______("1 番・グー 2 番・チョキ 3 番・パー")

# ユーザーの手の入力
number = ______("あなたの手は?")

# ユーザーの手の判定
if number == "1":
    user = "グー"
______ number == "2":
    user = "チョキ"
______:
    user = "パー"

# コンピューターの手の選択
cpu = random.choice(rock_paper_scissors)

# ユーザーとコンピューターの手を表示
______(f"あなたは{user}。向こうは{cpu}。")

# 勝敗の判定と結果の表示

## あなたがグーの場合
if user == "グー":
    if cpu == "グー":
        print("あいこ!")
    elif cpu == "チョキ":
        print("あなたの勝ち!")
    else:
        print("あなたの負け!")

## あなたがチョキの場合(ネストされた条件文)
elif ___________________

## あなたがパーの場合(ネストされた条件文)
else:
    _____________________
ヒント
グーの場合の 6 行をそっくり真似して、チョキ用・パー用に書き換えます。「自分と同じ手ならあいこ、自分が勝つ相手なら勝ち、それ以外は負け」。

3GHPython でジオメトリ設計

関数・リスト・辞書がそろうと、ジオメトリの生成を「部品」に分けて書けるようになります。 1 フロア分をつくる関数、1 部材をつくる関数——それを組み合わせて全体を組み立てるのが、 Grasshopper でいう「コンポーネントを自分でつくる」ことにあたります。

ファサードの階モジュール

窓 1 枚の寸法や余白は辞書 DEFAULT_SPEC にまとめ、 階数ぶんループを回して全体を組み立てます。集計はネスト辞書 report に入れます。

Floors = 4 / Cols = 4 Floors = 6 / Cols = 3 Floors = 3 / Cols = 6
同じ関数のまま Floors と Cols を変えるだけで割付が変わる。
G7ファサードの階モジュールの関数化

1 フロア分の窓をつくる関数 make_floor() を定義し、階数ぶん呼び出してファサード全体を組み立てます。既定仕様は辞書 DEFAULT_SPEC にまとめ、デフォルト引数で渡します。

import Rhino.Geometry as rg

# グローバル:既定仕様(辞書)
DEFAULT_SPEC = {
    "margin_x": 300.0,
    "margin_y": 300.0,
    "win_w": 900.0,
    "win_h": 1200.0
}

def make_floor(floor_index, W, H, Cols, spec=DEFAULT_SPEC):
    # 1 フロア分の窓(矩形 Curve のリスト)と、面積合計を返す

    # ローカル変数:この関数内だけで使う値
    floor_h = H / ________          # Floors で割って 1 フロア高さ
    # 窓配置の基準 Y(下からのオフセット)
    y0 = floor_index * floor_h + spec["margin_y"]

    curves = []
    area_sum = 0.0

    # 窓のピッチ
    pitch = (W - 2 * spec["margin_x"]) / Cols

    for j in range(Cols):
        # 各窓の中心 X
        cx = spec["margin_x"] + pitch * (j + 0.5)
        pl = rg.Plane.WorldXY
        pl.Origin = rg.Point3d(cx, y0 + spec["win_h"] * 0.5, 0)
        # Rectangle3d(plane, xInterval, yInterval)
        rect = rg.Rectangle3d(pl,
                              rg.Interval(-spec["win_w"] * 0.5, spec["win_w"] * 0.5),
                              rg.Interval(-spec["win_h"] * 0.5, spec["win_h"] * 0.5))
        crv = rect.ToNurbsCurve()
        curves.append(crv)
        area_sum += spec["win_w"] * spec["win_h"]

    return curves, area_sum

# ---- メイン ----
windows_all = []
report = {}          # ネスト辞書:各階の集計を入れる
total_area = 0.0

for i in range(Floors):
    # 関数呼び出し(デフォルト引数で spec を渡さない)
    win_crvs, area = make_floor(________, W, H, Cols)

    windows_all.extend(win_crvs)
    total_area += area

    floor_name = "F{}".format(i + 1)
    report[floor_name] = {
        "count": len(win_crvs),
        "area": area
    }

a = windows_all
b = report
c = total_area
ヒント
1 つめの空欄は「全体の高さを何で割れば 1 フロアの高さになるか」。
2 つめは「いま何階目か」を関数に渡す変数です。
出力タブを開くと b の集計辞書が読めます。

トラスの重量推定

部材 1 本を返す関数を用意しておけば、長さと重量の集計はループの中で自然に書けます。 材料ごとの密度・断面積はグローバル辞書から引くことで、材料を変えた比較が簡単になります。

Span 12000 / Height 1500 / Panels 6 上弦・下弦 斜材
下弦・上弦・斜材。上弦は偶数の節点だけ持ち上げている。
G8トラスの重量推定

部材 1 本をつくる関数 member() と、トラス全体を組む関数 make_truss() を組み合わせます。材料の密度と断面積はグローバル辞書 MAT_DB から引きます。

import Rhino.Geometry as rg

# グローバル:材料 DB(密度は仮の相対値、断面積も仮)
MAT_DB = {
    "Steel":  {"rho": 7.85e-6, "A": 1200.0},   # rho: kg/mm^3 のつもり
    "Timber": {"rho": 0.60e-6, "A": 2000.0}
}

def member(p0, p1, mat="Steel"):
    """部材 1 本を作って、(Line, 長さ, 重量) を返す"""
    ln = rg.Line(p0, p1)
    L = ln.Length                  # ローカル変数

    # グローバル辞書参照(MAT_DB)
    rho = MAT_DB[mat]["rho"]
    A   = MAT_DB[mat]["A"]

    w = L * A * rho
    return ln, L, w

def make_truss(Span, Height, Panels, mat=________):
    lines = []
    length_sum = 0.0
    weight_sum = 0.0
    dx = Span / Panels
    # 節点(下弦)
    bot = [rg.Point3d(i*dx, 0, 0) for i in range(Panels+1)]
    # 節点(上弦:偶数だけ持ち上げる)
    top = [rg.Point3d(i*dx, 0, Height if i%2==0 else Height*0.6) for i in range(Panels+1)]
    # 下弦・上弦
    for i in range(Panels):
        for pA, pB in [(bot[i], bot[i+1]), (top[i], top[i+1])]:
            ln, L, w = member(pA, pB, mat=mat)
            lines.append(ln); length_sum += L; weight_sum += w
    # 斜材
    for i in range(Panels):
        pA = bot[i]
        pB = top[i+1] if i%2==0 else top[i]
        ln, L, w = member(pA, pB, mat=mat)
        lines.append(ln); length_sum += L; weight_sum += w
    return lines, length_sum, weight_sum

# ---- メイン ----
all_lines = []
report = {}
total_weight = 0.0

for mat_key in [DefaultMat, "Timber"]:
    lines, Lsum, Wsum = make_truss(Span, Height, Panels, mat=mat_key)
    all_lines.extend(lines)
    total_weight += Wsum

    report[mat_key] = {
        "lines": lines,
        "length": Lsum,
        "weight": Wsum
    }

a = all_lines
b = report
c = total_weight
ヒント
空欄は make_truss のデフォルト引数です。MAT_DB のキーになっている文字列を書きます。
出力タブで c(総重量)を見ながら Panels を変えてみましょう。

4まとめと課題

本日のまとめ

  • 関数によって処理をまとめ、名前をつけて呼び出せるようになった。
  • リスト・辞書・ネストで、複数の要素とその集計を扱えるようになった。
  • これらを組み合わせて、ファサードやトラスを「部品の組み立て」として生成した。

次回までのミニ課題(最終課題の準備)

Colab で、以下の三つの中から一つを選んでプログラムを完成させてください。

  • じゃんけんの対戦結果を記録し、勝率・手の偏りなどを自動集計するプログラム
  • じゃんけんの CPU がプレイヤーの癖を学んで勝ちにくるプログラム
  • じゃんけんのカードゲーム化
設計に引きつけて考えるなら「記録して集計する」「傾向を読んで次の手を変える」という構造は、案の比較・最適化とまったく同じ形をしています。じゃんけんで書いた集計処理は、そのまま案の評価にも使えます。

参考リンク

5自由制作

本日の学びを踏まえ、穴あきではなく一からコードを書いて提出する課題です。生成AIの助けを借りて構いません。右の演習環境で開いて制作し、完成したら 提出 を押してください。

進め方テーマを決める → 生成AIに方針を相談 → 自分で書いて動かす → 提出(何度提出しても最新が残ります)。スライダは + スライダ で追加、各スライダの で削除できます。サンドボックスは 自由制作 ページにもあります。
第13回関数でまとめるジオメトリデザイン

第13回の学び(関数・リスト・オブジェクト)を踏まえ、部品を関数に分けて組み立てる作品を提出してください。ロフト・押し出し・配置の組み合わせで、自分のテーマを形にしてみましょう。

import Rhino.Geometry as rg
import math

# 一から書いてみましょう。生成AIに相談しながらで構いません。
# スライダ x, y, z, n が使えます。完成したら「提出」を押してください。

a = None