# GESP等级：三级 | GESP Python 三级考点

# -*- coding: utf-8 -*-
"""
08第3节4 — 列表排序：sort() 原地排 / sorted() 复制排
GESP 考点：#排序（列表排序方法）
=========================================================
"""
import turtle
import random
import time

# =====================================================
# 【导入】00:00-10:00  乱序数字 [3,1,4,1,5] → 排整齐！
# =====================================================
print("=" * 60)
print("【导入】数字排排队——把乱糟糟的列表整理整齐！")
print("=" * 60)
print()

print("想象一下：你有一排数字 [3, 1, 4, 1, 5, 9, 2, 6]")
print("这么多数字挤在一起，找谁都费劲！")
print()
print("如果排好序：[1, 1, 2, 3, 4, 5, 6, 9]")
print("从最小到最大，一目了然！")
print()
print("Python 帮我们准备了两种排队的办法：")
print("  ① list.sort()   —— '原地'排队（不另外占座）")
print("  ② sorted(list)  —— '复制'排队（新开一列）")
print()

# 展示对比
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(f"原始队伍: {numbers}")
print()

# sort()
sorted_copy = sorted(numbers)
print(f"sorted(numbers) → {sorted_copy}")
print(f"原队伍没变：    {numbers}")
print()

# sorted()
numbers_copy = numbers[:]
numbers_copy.sort()
print(f"numbers.sort()后 → {numbers_copy}")
print(f"原队伍也被改了：  {numbers}")
print()

input("按回车键进入知识点学习...")

# =====================================================
# 【知识点】10:00-30:00  sort() 与 sorted() 详解
# =====================================================
print()
print()
print("=" * 60)
print("【知识点】列表排序的两种武器")
print("=" * 60)

# --- 知识点① list.sort() 原地排序 ---
print()
print("① list.sort() —— 原地排序（队伍自己排队）")
print("-" * 50)
print("  语法：  list.sort(reverse=False)")
print()
print("  特点：")
print("  • 直接在原列表上排序，不创建新列表")
print("  • 原列表的顺序被永久改变")
print("  • 返回 None（不是返回新列表！）")
print("  • 速度快，节省内存")
print()

# 演示
lst = [5, 2, 8, 1, 9]
print(f"  演示：lst = {lst}")
result = lst.sort()
print(f"  lst.sort()  →  返回值 result = {result}")
print(f"  lst 现在是：{lst}")
print()

# --- 知识点② sorted(list) 生成新列表 ---
print()
print("② sorted(list) —— 复制排序（另开一列）")
print("-" * 50)
print("  语法：  new_list = sorted(list, reverse=False)")
print()
print("  特点：")
print("  • 创建一份排序后的新列表")
print("  • 原列表保持不变！")
print("  • 返回排好序的新列表")
print("  • 适用于不想破坏原数据的场景")
print()

lst2 = [5, 2, 8, 1, 9]
print(f"  演示：lst2 = {lst2}")
new_lst = sorted(lst2)
print(f"  new_lst = sorted(lst2)  →  new_lst = {new_lst}")
print(f"  lst2 还是：{lst2}  ← 原列表没变！")
print()

# --- 知识点③ reverse=True 降序 ---
print()
print("③ reverse=True —— 从大到小降序排")
print("-" * 50)
print("  sort() 和 sorted() 都支持 reverse 参数")
print("  reverse=False (默认) → 从小到大（升序）")
print("  reverse=True         → 从大到小（降序）")
print()

lst3 = [3, 1, 4, 1, 5]
print(f"  演示：lst3 = {lst3}")
lst3.sort()
print(f"  lst3.sort()           → {lst3}  ← 升序")
lst3.sort(reverse=True)
print(f"  lst3.sort(reverse=True) → {lst3}  ← 降序")
print()

# sorted 也支持 reverse
lst4 = [3, 1, 4, 1, 5]
print(f"  sorted(lst4)             = {sorted(lst4)}")
print(f"  sorted(lst4, reverse=True) = {sorted(lst4, reverse=True)}")
print()

# --- 知识点④ 颜色名也能排序 ---
print()
print("④ 字符串也能排序！—— 按字母顺序（ASCII码）")
print("-" * 50)

colors = ["red", "blue", "green", "yellow", "orange", "purple"]
print(f"  颜色名列表：{colors}")
print(f"  sorted(colors) → {sorted(colors)}")
print(f"  反向排序      → {sorted(colors, reverse=True)}")
print()
print("  排序规则：按字母顺序 a→z")
print("  字符串排序时，大写字母排在小写字母前面！")
print("  (因为 'A' 的 ASCII 码 65 小于 'a' 的 97)")
print()

chars = ["apple", "Banana", "cherry", "Date"]
print(f"  演示大小写：{chars}")
print(f"  sorted(chars) → {sorted(chars)}  ← B(66) < a(97)")
print()

# --- 知识点总结对比表 ---
print()
print("=" * 60)
print("sort() vs sorted() 对比总结：")
print("=" * 60)
print()
print("  ┌──────────────────┬────────────────────────┐")
print("  │    sort()        │     sorted()           │")
print("  ├──────────────────┼────────────────────────┤")
print("  │ 原地修改原列表    │ 创建新列表，原列表不变  │")
print("  │ 返回 None        │ 返回排序后的新列表     │")
print("  │ 只能用于列表      │ 可用于任何可迭代对象   │")
print("  │ 速度更快         │ 更灵活，不破坏原数据   │")
print("  │ list.sort() 写法 │ sorted(list) 写法      │")
print("  └──────────────────┴────────────────────────┘")
print()
print("  共同点：都支持 reverse=True 降序排列")
print()

input("按回车键进入演示阶段...")

# =====================================================
# 【演示】30:00-40:00  颜色排序 + 数字降序
# =====================================================
print()
print()
print("=" * 60)
print("【演示①】colors.sort() → 按字母排")
print("=" * 60)
print()

# 创建 turtle 画布
t = turtle.Turtle()
t.speed(5)
t.pensize(3)
t.shape("turtle")

# 原始颜色列表
colors = ["red", "blue", "green", "yellow", "orange", "purple"]
print(f"原始颜色名：{colors}")
print()

# 按字母排序
colors_sorted = sorted(colors)
print(f"按字母排序后：{colors_sorted}")
print()

# 用 turtle 按排序后的颜色画彩色圆点
print("用 turtle 展示排序后的颜色圆点：")
t.penup()
start_x = -250
t.goto(start_x, 100)

for i, color_name in enumerate(colors_sorted):
    t.color(color_name)
    t.goto(start_x + i * 100, 100)
    t.dot(40)
    t.write(f" {color_name}", font=("Arial", 10, "normal"))
    print(f"  {i+1}. {color_name}")

print()
print("按字母顺序 A→B→G→O→P→R→Y 排列！")
print()

input("按回车键继续演示②...")

# =====================================================
# 【演示②】numbers.sort(reverse=True) 降序
# =====================================================
t.clear()
print()
print("=" * 60)
print("【演示②】numbers.sort(reverse=True) → 降序")
print("=" * 60)
print()

# 随机生成 8 个数
random.seed(42)  # 固定随机性，保证演示可重复
numbers = [random.randint(10, 99) for _ in range(8)]
print(f"原始数字：{numbers}")
print()

# 升序排列
nums_asc = sorted(numbers)
print(f"升序（默认）：{nums_asc}")

# 降序排列
nums_desc = sorted(numbers, reverse=True)
print(f"降序：       {nums_desc}")
print()

# 用 turtle 画柱状图展示
print("turtle 柱状图展示（红色=升序，蓝色=降序）：")
t.penup()
t.goto(-300, -50)

t.color("black")
t.write("升序排列：", font=("Arial", 12, "bold"))
for i, val in enumerate(nums_asc):
    t.color("red")
    t.goto(-300 + i * 80, -30)
    t.pendown()
    t.begin_fill()
    for _ in range(2):
        t.forward(30)
        t.left(90)
        t.forward(val)
        t.left(90)
    t.end_fill()
    t.penup()
    t.goto(-300 + i * 80, -30 + val + 5)
    t.color("black")
    t.write(str(val), font=("Arial", 8, "normal"))
    print(f"  {val} ", end="")
print("  ✅ 升序完成！")

t.goto(-300, -180)
t.color("black")
t.write("降序排列：", font=("Arial", 12, "bold"))
for i, val in enumerate(nums_desc):
    t.color("blue")
    t.goto(-300 + i * 80, -160)
    t.pendown()
    t.begin_fill()
    for _ in range(2):
        t.forward(30)
        t.left(90)
        t.forward(val)
        t.left(90)
    t.end_fill()
    t.penup()
    t.goto(-300 + i * 80, -160 + val + 5)
    t.color("black")
    t.write(str(val), font=("Arial", 8, "normal"))
    print(f"  {val} ", end="")
print("  ✅ 降序完成！")

print()
print("对比：")
print(f"  升序（最小→最大）：{nums_asc}")
print(f"  降序（最大→最小）：{nums_desc}")
print()

input("按回车键进入实操环节...")
t.clear()

# =====================================================
# 【实操①】40:00-45:00  填空 sort vs sorted 区别
# =====================================================
print()
print()
print("=" * 60)
print("【实操①】填空：sort() vs sorted() 区别")
print("=" * 60)
print()

print("请补全下面每句话：")
print()

fill_questions = [
    ("1. list.sort() 是______排序（选：原地/复制）", "原地"),
    ("2. sorted(list) 返回一个______列表", "新的"),
    ("3. list.sort() 的返回值是______", "None"),
    ("4. sorted(list) 后，原列表______改变（选：会/不会）", "不会"),
    ("5. 想让列表降序排列，用参数______", "reverse=True"),
    ("6. 字符串排序时，按______顺序排列（选：字母/长度/数字）", "字母"),
    ("7. 下面的代码输出什么？", ""),
]

for q, a in fill_questions[:6]:
    print(f"  {q}")
    print(f"    答案：{a}")
    print()

# 第7题：预测输出
print("  7. 下面代码的输出是什么？")
print("     lst = [3, 1, 2]")
print("     result = lst.sort()")
print("     print(result)")
print("     print(lst)")
print()
print("    你的答案：___________________")
print()
import time as _t
_t.sleep(1)
print("    实际输出：")
lst_q = [3, 1, 2]
result_q = lst_q.sort()
print(f"      print(result)  → {result_q}")
print(f"      print(lst)     → {lst_q}")
print()

# 预测输出 2
print("  8. 再来一题：")
print("     lst = [3, 1, 2]")
print("     new_lst = sorted(lst)")
print("     print(new_lst)")
print("     print(lst)")
print()
print("    你的答案：___________________")
print()
_t.sleep(1)
lst_q2 = [3, 1, 2]
new_lst_q2 = sorted(lst_q2)
print(f"      print(new_lst) → {new_lst_q2}")
print(f"      print(lst)     → {lst_q2}")
print()

print("关键区别：sort() 改变了原列表，sorted() 没改变原列表！")
print()

input("按回车键继续实操②...")

# =====================================================
# 【实操②】45:00-50:00  排序数字列表（多种方式）
# =====================================================
print()
print("=" * 60)
print("【实操②】排序数字列表——体验四种写法")
print("=" * 60)
print()

# 准备数据
data = [64, 34, 25, 12, 22, 11, 90]
print(f"原始数据：{data}")
print()

# 方法1：sort() 升序
lst1 = data[:]
lst1.sort()
print(f"方法1 - lst.sort() 升序：      {lst1}")

# 方法2：sort() 降序
lst2 = data[:]
lst2.sort(reverse=True)
print(f"方法2 - lst.sort(reverse=True) 降序：{lst2}")

# 方法3：sorted() 升序
print(f"方法3 - sorted(lst) 升序：      {sorted(data)}")

# 方法4：sorted() 降序
print(f"方法4 - sorted(lst, reverse=True) 降序：{sorted(data, reverse=True)}")

print()
print(f"检查原数据是否被改：{data}  ← 只有方法1和方法2会改！")
print()

# 小挑战：用 turtle 把排序过程可视化
print("turtle 可视化：把数字画成彩色圆点，从下往上排")
print()

t.speed(3)
t.pensize(2)

# 原始数据随机显示
t.penup()
t.goto(-250, 0)
t.write("原始顺序（随机散落）：", font=("Arial", 10, "bold"))
for i, val in enumerate(data):
    t.color(random.choice(["red", "orange", "green", "blue", "purple"]))
    t.goto(-250 + i * 55, -30)
    t.dot(25)
    t.goto(-250 + i * 55, -45)
    t.color("black")
    t.write(str(val), align="center", font=("Arial", 8, "normal"))

_t.sleep(1)

# 排序后显示（升序）
t.penup()
t.goto(-250, -80)
t.color("black")
t.write("sort() 排序后：", font=("Arial", 10, "bold"))

sorted_data = sorted(data)
for i, val in enumerate(sorted_data):
    # 颜色从绿到红渐变（值越大越红）
    r = val / max(sorted_data)
    g = 1 - r
    t.color((r, g, 0.2))
    t.goto(-250 + i * 55, -110)
    t.dot(28)
    t.goto(-250 + i * 55, -125)
    t.color("black")
    t.write(str(val), align="center", font=("Arial", 8, "normal"))

t.penup()
t.goto(0, -200)
t.color("purple")
t.write("从小到大排好队 ✓", align="center", font=("Arial", 14, "bold"))

print()
print("✅ 实操②完成！")
print()

input("按回车键继续实操③...")
t.clear()

# =====================================================
# 【实操③】50:00-55:00  排序颜色名，用 turtle 画圈
# =====================================================
print()
print("=" * 60)
print("【实操③】排序颜色名 → 用 turtle 画彩色圈圈")
print("=" * 60)
print()

# 准备颜色名列表（故意乱序）
messy_colors = ["yellow", "red", "purple", "blue", "orange", "green", "pink", "brown"]
print(f"乱序颜色名：{messy_colors}")
print()

# 排序后
sorted_colors = sorted(messy_colors)
print(f"按字母排序后：{sorted_colors}")
print()

# 用 turtle 画彩色圆圈阵
print("在画布上画出按字母排序的彩色圆环：")
print()

t.speed(4)
t.pensize(2)

# 第1行：乱序
t.penup()
t.goto(-300, 120)
t.color("black")
t.write("乱序：", font=("Arial", 12, "bold"))

for i, color_name in enumerate(messy_colors):
    try:
        t.color(color_name)
    except turtle.TurtleGraphicsError:
        t.color("black")
    t.penup()
    t.goto(-250 + i * 70, 80)
    t.pendown()
    t.begin_fill()
    t.circle(25)
    t.end_fill()
    t.penup()
    t.goto(-250 + i * 70, 45)
    t.color("black")
    t.write(color_name, align="center", font=("Arial", 8, "normal"))

# 第2行：排序后
t.penup()
t.goto(-300, 20)
t.color("black")
t.write("排序后（字母序）：", font=("Arial", 12, "bold"))

for i, color_name in enumerate(sorted_colors):
    try:
        t.color(color_name)
    except turtle.TurtleGraphicsError:
        t.color("black")
    t.penup()
    t.goto(-250 + i * 70, -20)
    t.pendown()
    t.begin_fill()
    t.circle(25)
    t.end_fill()
    t.penup()
    t.goto(-250 + i * 70, -55)
    t.color("black")
    t.write(color_name, align="center", font=("Arial", 8, "normal"))
    print(f"  {i+1}. {color_name}")

print()
print("字母顺序：B→G→O→P→R→Y（褐色brown排在绿green前！）")
print()

# 画个箭头示意
t.penup()
t.goto(-310, 50)
t.color("gray")
t.pendown()
t.goto(-310, 90)
t.penup()
t.goto(-320, 70)
t.write("↑ 排序", font=("Arial", 8, "normal"))

t.penup()
t.goto(0, -130)
t.color("purple")
t.write("颜色名按字母排序 → 五彩缤纷圈圈阵 ✓",
        align="center", font=("Arial", 12, "bold"))

print()
print("✅ 实操③完成！")

input("按回车键进行小测...")
t.clear()
t.penup()
t.home()

# =====================================================
# 【小测】55:00-60:00  "sort() 和 sorted() 最大区别？"
# =====================================================
print()
print()
print("=" * 60)
print("【小测】sort() 和 sorted() 最大区别是什么？")
print("=" * 60)
print()

print("题目：")
print("  sort() 和 sorted() 都能对列表排序，")
print("  它们最大的区别是什么？")
print()
print("  A. sort() 只能排数字，sorted() 可以排任何类型")
print("  B. sort() 原地修改原列表，sorted() 返回新列表")
print("  C. sort() 是函数，sorted() 是方法")
print("  D. sort() 不能降序，sorted() 可以")
print()

input("请想一想你的答案，按回车查看答案...")
print()

print("正确答案：B")
print()
print("解析：")
print("  sort()  →  原地修改原列表，返回 None")
print("    lst.sort() 之后，原来的 lst 永远被改变了！")
print()
print("  sorted()  →  创建并返回一个排好序的新列表")
print("    原列表不变，可以用新变量接住结果")
print()

# 验证
check_lst = [3, 1, 2]
print("  验证代码：")
print(f"    lst = {check_lst}")
orig_id = id(check_lst)
check_lst.sort()
print(f"    lst.sort()   → lst = {check_lst}, id 变了? {id(check_lst) != orig_id}")
print(f"    原地修改，id 没变 → B 正确！")
print()

check_lst2 = [3, 1, 2]
orig_id2 = id(check_lst2)
new_lst = sorted(check_lst2)
print(f"    sorted(lst)  → 新列表 = {new_lst}, id = {id(new_lst)}")
print(f"    原列表 lst  = {check_lst2}, id = {id(check_lst2)}")
print(f"    id 不同 → 确实是新列表！")
print()

print("其他选项为什么不对？")
print("  A 错：sort() 和 sorted() 都能排数字和字符串")
print("  C 错：sort() 是列表的方法，sorted() 是内置函数")
print("  D 错：两者都支持 reverse=True 降序")
print()

# 延伸小测2
print()
print("【附加题】下面代码输出什么？")
print()
print("  colors = ['red', 'blue', 'green']")
print("  colors.sort(reverse=True)")
print("  print(colors)")
print()

_t.sleep(1)
colors_test = ['red', 'blue', 'green']
colors_test.sort(reverse=True)
print(f"  输出：{colors_test}")
print("  解析：reverse=True 降序，按字母从后往前：r→g→b")
print()

# 延伸小测3
print()
print("【附加题2】下面代码会报错吗？")
print()
print("  result = [3, 1, 2].sort()")
print("  print(result + [4])")
print()

_t.sleep(1)
try:
    result_test = [3, 1, 2].sort()
    print(f"  result = {result_test}")
    print(f"  print(result + [4])  会报错！因为 result 是 None")
    print("  报错：TypeError: can only concatenate list (not 'NoneType') to list")
except TypeError as e:
    print(f"  TypeError: {e}")
    print("  原因：sort() 返回 None，None 不能和列表拼接！")
print()

# =====================================================
# 课堂总结
# =====================================================
print()
print("=" * 60)
print("【课堂总结】")
print("=" * 60)
print()
print("  今天学了两种列表排序方法：")
print()
print("  ◇ sort()  — 原地排序，原列表被改，返回 None")
print("  ◇ sorted() — 复制排序，原列表不变，返回新列表")
print()
print("  共同参数：")
print("    reverse=False（默认，升序）")
print("    reverse=True（降序）")
print()
print("  适用范围：")
print("    • 数字：按数值大小")
print("    • 字符串：按字母顺序（ASCII码）")
print("    • 颜色名等文本同样适用！")
print()
print("记忆钩子（背下来！）：")
print("  ★ sort()原地排  sorted()复制排 ★")
print()
print("  谐音记忆：")
print('    "原地" = sort 本身就有"整理"的意思')
print('    "复制" = sorted 像是"sort + ed" 做完后的结果')
print()
print("=" * 60)
print()

# 用 turtle 画最终总结图
t.penup()
t.goto(0, 150)
t.color("blue")
t.write("sort() = 原地排", align="center", font=("Arial", 16, "bold"))
t.goto(0, 110)
t.color("red")
t.write("sorted() = 复制排", align="center", font=("Arial", 16, "bold"))
t.goto(0, 60)
t.color("purple")
t.write("reverse=True = 降序", align="center", font=("Arial", 14, "bold"))

# 画两个框区分
t.penup()
t.goto(-180, 170)
t.pendown()
t.color("blue")
for _ in range(2):
    t.forward(360)
    t.right(90)
    t.forward(40)
    t.right(90)
t.penup()

t.goto(-180, 80)
t.pendown()
t.color("red")
for _ in range(2):
    t.forward(360)
    t.right(90)
    t.forward(40)
    t.right(90)
t.penup()

t.goto(0, -50)
t.color("green")
t.write("记忆钩子：sort()原地排  sorted()复制排",
        align="center", font=("Arial", 12, "bold"))

turtle.done()
