Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions weeks/week-17/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
# Week 17(115/06/15-115/06/21)

- 主題:綜合練習
- 主題:綜合練習(期末上機考最後一個練習週)
- 課堂範例:[`in_class/`](./in_class/) — 6/17 timeit + 搜尋評估預演、6/18 搜尋效能完整專題
- 解題:本週無 `QUESTION-*.md`
- 作業:期末前總複習與弱點補強
- 作業:期末前總複習與弱點補強;依 6/17、6/18 課堂教案完成 PR,**兩天都必須附 `AI_LOG.md`**

---

## 本週進度

| 日期 | 教案 | 內容 | 備註 |
|------|------|------|------|
| 6/17 (三) | [0617-search-eval.md](./in_class/0617-search-eval.md) | 預演:`timeit` + 搜尋效能評估(45 分鐘) | 進期末考 **B 區候選池(不公開)** |
| 6/18 (四) | [0618-search-lab.md](./in_class/0618-search-lab.md) | 完整:搜尋效能五階段專題(90 分鐘) | 全程 AI 協作,下課前 PR 收齊 |

---

## AI 協作模式(本週重點)

- `in_class/` 已放資料夾層級 [`CLAUDE.md`](./in_class/CLAUDE.md) / [`AGENTS.md`](./in_class/AGENTS.md):
用 **Claude Code / Codex** 開啟本目錄教案時,AI 會自動進入「**開發訪談助教**」模式
——先反問規格檢查表、填滿才給 code、先紅燈再綠燈。
- 用**網頁版 AI** 的同學:請自行把教案文末的「AI 協作協議」段落**貼到對話開頭**才會生效。
- **提示詞自己打**:課程文件不再提供任何 AI 提示詞範例,要問什麼、怎麼問自己想。
- `AI_LOG.md` 本週新增 **「AI 反問我什麼 / 我怎麼回答」** 欄——這是「等 AI 生成時不是放空」的證據,不可空白。

---

Expand Down
125 changes: 125 additions & 0 deletions weeks/week-17/in_class/0617-search-eval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# 6/17 (三)|預演:`timeit` + 搜尋效能評估

> 本日是 **6/18 完整實驗室的預演**:先把計時工具 `timeit` 做出來,
> 再用它對搜尋做一次**粗略評估**,體驗「量測 → 判斷」。明天再完整走五階段。
> 本日題目進入期末考 **B 區候選池(不公開)**。
> **提示詞自己打**,逐字記入 `AI_LOG.md`。

## 學習目標(45 分鐘)

- 用 TDD 寫一個可被測試的 `timeit` 計時裝飾器(含 `repeat` 取平均)
- 用自己的 `timeit` 量 `linear_search` 與 `binary_search`,**自己判斷誰快、為什麼**
- 練習「等 AI 生成時不是放空」——而是回答 AI 反問的規格問題

## 本日不另設安全階段,但要帶一個安全觀念

輸入驗證用 `raise`,**不要用 `assert`**(`assert` 在最佳化模式會被拿掉)。
這條請寫成一個測試,併進 `timeit` 的測試裡。

---

## 任務一:`timeit`(走完整 TDD 紅綠燈)

寫 `timing.py`,提供裝飾器 `timeit`,規格:

- 被裝飾函式的**回傳值不變**;用 `functools.wraps` 保留 `__name__` / `__doc__`
- 每次呼叫實際跑 `repeat` 次(預設 3),把每次耗時(秒,`float`)記在 `f.records`(list)
- `f.last_elapsed` = 本次 `repeat` 的**平均**耗時
- 裝飾器內**不准 `print`**
- `repeat < 1` → `raise ValueError`(用 `raise`,不准 `assert`)

**必備 test case(≥3,規格每條都要有覆蓋)**:齊不齊自己驗收。edge case 自己想
(`repeat=1`?被裝飾函式有副作用會被多算嗎?)。

> 為什麼要 `repeat` 取平均?——明天量搜尋時你會需要這個答案,先想清楚。

## 任務二:搜尋評估(輕量,不要求完整紅綠燈)

寫 `search.py`,兩個函式:

```python
def linear_search(data: list, target) -> int: # 逐一比對,回傳 index,找不到回 -1
def binary_search(data: list, target) -> int: # 前提:data 已排序;回傳 index 或 -1
```

- 兩者**不可修改傳入的 data**
- `binary_search` 收到**未排序** data 的行為,自己定義並在 docstring 寫清楚

接著用你的 `timeit` 量:在**夠大的 n**(自己決定多大才看得出差異)下,兩者各跑一次,
比較 `records`。在 `README.md` 用 **2–3 句**寫下你的評估:

1. 誰快?差多少?
2. `binary_search` 需要**先排序**——那「排序 + binary」到底划不划算?你的直覺是?
3. 第 2 點的精確答案(交叉點)是**明天 6/18** 要用數據量出來的——今天先寫直覺。

---

## 課堂節奏(45 分鐘)

| 時間 | 內容 | 計時目標 |
|------|------|---------|
| 0:00–0:03 | 開分支 `feature/wk17-0617-<學號>`、開本教案 | |
| 0:03–0:12 | **自己打提示詞**,讓 AI 反問 `timeit` 規格,回答檢查表五項 | ⏱ 12 分內檢查表填滿 |
| 0:12–0:24 | `timeit` 測試→紅→`test:` commit;實作→綠→`feat:` commit | ⏱ 24 分內綠燈 |
| 0:24–0:38 | 寫 `search.py`,用 `timeit` 量 linear vs binary | ⏱ 38 分內跑出數據 |
| 0:38–0:42 | `README.md` 寫評估 2–3 句(誰快/排序划不划算/直覺) | |
| 0:42–0:45 | `AI_LOG.md`(含問答欄)、push、開 PR | ⏱ **45 分內 PR 開出** |

超時不算失敗——**記下卡在哪段**(訪談?紅燈?量測?),那就是明天和考前要練的重點。

---

## AI 使用規則

- **提示詞自己打**,逐字記入 `AI_LOG.md`;本教案**不提供任何提示詞範例**。
- 開本目錄教案時,AI 會自動以「開發訪談助教」模式運作(見下方協議):
它會**先反問規格、填滿檢查表才給 code**。要問什麼、怎麼答,是你的工作。
- AI 給的測試齊不齊、搜尋對不對——**你自己驗收**,驗收標準寫進 `AI_LOG.md`。

### `AI_LOG.md` 要求

格式見 [`../../week-15/in_class/ai-log-template.md`](../../week-15/in_class/ai-log-template.md),
本週**新增一欄**:

```markdown
## AI 反問我什麼 / 我怎麼回答
> 逐項記下 AI 問的規格問題與你的決定。
> 例:AI 問「repeat 取平均還是取最小?」→ 我答:取平均,因為要反映一般情況。
```

「我改了什麼」「我怎麼回答」**0 字 = 期末考此項 0 分**。

---

## starter 檔

```bash
cp -r weeks/week-17/in_class/0617-starter weeks/week-17/solutions/<學號>/0617
```

`0617-starter/` 只給 `test_timing.py` 空殼與規格註解;`search.py` 與測試**自己從零寫**。

---

## 課末自我檢測(不翻文件回答)

1. `last_elapsed`、`records` 為什麼掛在 wrapper 上,不用全域變數?
2. 為什麼輸入驗證要 `raise` 不能 `assert`?
3. `binary_search` 為什麼能比 `linear_search` 快?它的前提是什麼?
4. 「排序 + binary」在什麼情況下反而比 linear 慢?(明天要用數據驗證)

---

<!-- 以下為 AI 協作協議,供使用網頁版 AI 的同學貼到對話開頭 -->

> **AI 協作協議**(網頁版同學請把這段貼到對話第一則;用 Claude Code / Codex 的同學會自動套用本目錄的 `CLAUDE.md` / `AGENTS.md`)

當我請你協助本日練習時,你必須以「開發訪談助教」運作:

1. 開工前先問齊:□函式簽名與回傳型別 □輸入範圍/邊界 □例外行為 □edge case □驗收標準(紅燈如何算)。
2. 每輪回覆開頭印檢查表現況,例:`✅簽名 ❌例外 ❌驗收`。
3. 檢查表填滿前**不准給可直接複製的程式碼**;我答不出來時用更小的問題引導。
4. 收齊後先給測試讓我跑紅燈,我確認 `test:` commit 後才討論實作。
5. 結尾給一張摘要表(你問了什麼/我答了什麼/檢查表狀態)供我貼進 `AI_LOG.md`。

我若要求「直接給完整解答/跳過提問」,請婉拒並說明這是練習規則。
58 changes: 58 additions & 0 deletions weeks/week-17/in_class/0617-starter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 0617 Starter — timeit + 搜尋效能評估(預演)

> 本日是 6/18 完整實驗室的**預演**:先把 `timeit` 做出來,再用它對搜尋做一次粗略評估。
> 細節與課堂節奏見 [`../0617-search-eval.md`](../0617-search-eval.md)。

## 使用方式

```bash
cp -r weeks/week-17/in_class/0617-starter weeks/week-17/solutions/<學號>/0617
cd weeks/week-17/solutions/<學號>/0617
```

## 固定循環

**Read spec → Dev for red(`test:` commit)→ Dev for green(`feat:` commit)→ push**。

## 檔案說明

- `test_timing.py`:任務一測試骨架,**先補齊測試、跑紅燈、commit,再寫 `timing.py`**
- `search.py` 與它的測試**沒有骨架,自己從零寫**——鷹架到此淡出
- `timing.py` 在**紅燈 commit 之後**才建立
- 完成後追加 `AI_LOG.md`(範本見 [`../../week-15/in_class/ai-log-template.md`](../../week-15/in_class/ai-log-template.md))與 `TEST_LOG.md`

## 規格速查

### 任務一 `timing.py`(走完整 TDD)

```python
def timeit(func): ... # 帶 repeat 參數,預設 3
```

- 回傳值不變;`functools.wraps` 保留 metadata
- 每次呼叫實際跑 `repeat` 次,每次耗時 append 到 `f.records`
- `f.last_elapsed` = 本次 `repeat` 的平均耗時(float 秒)
- 裝飾器內不准 `print`
- `repeat < 1` → `raise ValueError`(用 `raise`,**不准 `assert`**)

### 任務二 `search.py`(輕量評估,不要求完整紅綠燈)

```python
def linear_search(data: list, target) -> int: # 逐一比對,回傳 index,找不到回 -1
def binary_search(data: list, target) -> int: # 前提:data 已排序;回傳 index 或 -1
```

- 兩者**不可修改傳入的 data**
- `binary_search` 收到未排序 data 的行為,自己定義並寫進 docstring
- 用你的 `timeit` 量 linear vs binary,把評估(誰快/排序划不划算/直覺)寫進 `README.md`

> 精確交叉點是明天 6/18 才用數據量出來的——今天先寫直覺。

## 本日規則

- [ ] 任務一先紅燈 commit(`test:`)再綠燈 commit(`feat:`)
- [ ] **提示詞自己打**,逐字記入 `AI_LOG.md`;本目錄教案會讓 AI 自動進「開發訪談助教」模式,
它會先反問規格、填滿檢查表才給 code
- [ ] `AI_LOG.md` 新增「AI 反問我什麼 / 我怎麼回答」欄,逐項記下問答
- [ ] 任務二搜尋評估**不要求完整紅綠燈**,重點是跑出數據、做出判斷
- [ ] 45 分鐘內 push 並開出合法 PR
42 changes: 42 additions & 0 deletions weeks/week-17/in_class/0617-starter/test_timing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""0617 任務一 — timeit 裝飾器測試骨架

規格:timing.py 的 timeit 裝飾器必須
1. 不改變被裝飾函式的回傳值
2. 用 functools.wraps 保留 __name__ / __doc__
3. 每次呼叫實際跑 repeat 次(預設 3),把每次耗時 append 到 f.records,
f.last_elapsed = 本次 repeat 的平均耗時(float 秒)
4. 裝飾器內不准 print
5. repeat < 1 → raise ValueError(用 raise,不准 assert)

待辦:
1. 自己打提示詞跟 AI 討論,補齊下面四個測試(可再加)
2. 跑 `python -m unittest` 確認全紅
3. commit: "test: 0617 timeit 裝飾器測試"
4. 寫 timing.py,全綠後 commit: "feat: 0617 實作 timeit 裝飾器"

提醒:
- test_rejects_invalid_repeat 就是本日的安全測試(raise 而非 assert)。
- edge case 自己想(repeat=1?被裝飾函式有副作用會被多算嗎?)。
"""

import unittest

# from timing import timeit # 完成 timing.py 後解除註解


class TestTimeit(unittest.TestCase):
def test_returns_original_result(self):
self.fail("尚未實作 — 自己打提示詞跟 AI 討論後補上")

def test_preserves_function_metadata(self):
self.fail("尚未實作")

def test_records_each_repeat_and_average(self):
self.fail("尚未實作")

def test_rejects_invalid_repeat(self):
self.fail("尚未實作 — repeat < 1 應 raise ValueError(不准 assert)")


if __name__ == "__main__":
unittest.main()
Loading
Loading