Merge branch 'camera-2018:master' into master
12
.github/workflows/link-pr.yaml
vendored
@@ -30,11 +30,13 @@ jobs:
|
||||
# -i, --insecure Proceed for server connections considered insecure (invalid TLS)
|
||||
# -n, --no-progress Do not show progress bar.
|
||||
# -t, --timeout <timeout> Website timeout in seconds from connect to response finished [default:20]
|
||||
# --max-concurrency <max-concurrency> Maximum number of concurrent network requests [default: 128]
|
||||
# -a --accept <accept> Comma-separated list of accepted status codes for valid links
|
||||
|
||||
# .vitepress/dist the site directory to check
|
||||
# --max-concurrency <max-concurrency> Maximum number of concurrent network requests [default: 128]
|
||||
# -a --accept <accept> Comma-separated list of accepted status codes for valid links
|
||||
# --max-retries <MAX_RETRIES> Maximum number of retries per request
|
||||
# -r, --retry-wait-time <RETRY_WAIT_TIME> Minimum wait time in seconds between retries of failed requests
|
||||
# -u, --user-agent <USER_AGENT> User agent
|
||||
# *.md all markdown files in the root directory
|
||||
args: -E -i -n -t 45 --max-concurrency 64 -a 401,403 -- '.vitepress/dist' '*.md'
|
||||
args: -E -i -n -t 45 -r 3 --max-retries 5 --max-concurrency 64 -a 401,403 -u "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" -- '.vitepress/dist' '*.md'
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
24
.github/workflows/link-schedule.yaml
vendored
@@ -1,10 +1,13 @@
|
||||
name: links
|
||||
name: links schedule
|
||||
|
||||
on:
|
||||
# repository_dispatch:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "30 8 * * *"
|
||||
- cron: "0 0 */3 * *"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
linkChecker:
|
||||
@@ -28,18 +31,19 @@ jobs:
|
||||
# -i, --insecure Proceed for server connections considered insecure (invalid TLS)
|
||||
# -n, --no-progress Do not show progress bar.
|
||||
# -t, --timeout <timeout> Website timeout in seconds from connect to response finished [default:20]
|
||||
# --max-concurrency <max-concurrency> Maximum number of concurrent network requests [default: 128]
|
||||
# -a --accept <accept> Comma-separated list of accepted status codes for valid links
|
||||
|
||||
# .vitepress/dist the site directory to check
|
||||
# *.md all markdown files in the root directory
|
||||
args: -E -i -n -t 45 --max-concurrency 64 -a 401,403 -- '.vitepress/dist' '*.md'
|
||||
# --max-concurrency <max-concurrency> Maximum number of concurrent network requests [default: 128]
|
||||
# -a --accept <accept> Comma-separated list of accepted status codes for valid links
|
||||
# --max-retries <MAX_RETRIES> Maximum number of retries per request
|
||||
# -r, --retry-wait-time <RETRY_WAIT_TIME> Minimum wait time in seconds between retries of failed requests
|
||||
# -u, --user-agent <USER_AGENT> User agent
|
||||
# *.md all markdown files in the root directory
|
||||
args: -E -i -n -t 45 -r 3 --max-retries 5 --max-concurrency 64 -a 401,403 -u "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" -- '.vitepress/dist' '*.md'
|
||||
output: out.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
- name: Create Issue From File
|
||||
uses: peter-evans/create-issue-from-file@v3
|
||||
uses: peter-evans/create-issue-from-file@v4
|
||||
with:
|
||||
title: Broken Link Detected
|
||||
content-filepath: out.md
|
||||
assignees: nightwhite
|
||||
assignees: camera-2018
|
||||
|
||||
@@ -4,4 +4,6 @@ https://adworld.xctf.org.cn/challenges/list
|
||||
https://e5c78mnhgz.feishu.cn/docx/doxcnxBONvnxSLi0MfaNZWvrcSb
|
||||
https://datawhale.feishu.cn/docs/doccn0AOicI3LJ8RwhY0cuDPSOc#
|
||||
https://message.hdu-cs.wiki/
|
||||
https://adworld.xctf.org.cn/
|
||||
https://adworld.xctf.org.cn/
|
||||
http://sequence-gallery.chal.crewc.tf:8080/
|
||||
https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf
|
||||
65
.vitepress/components/Confetti.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<script setup>
|
||||
import confetti from 'canvas-confetti'
|
||||
import { watch } from 'vue'
|
||||
|
||||
const props = defineProps(
|
||||
{
|
||||
passed: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
['passed'],
|
||||
)
|
||||
|
||||
function congrats() {
|
||||
const defaults = {
|
||||
colors: [
|
||||
'#5D8C7B',
|
||||
'#F2D091',
|
||||
'#F2A679',
|
||||
'#D9695F',
|
||||
'#8C4646',
|
||||
],
|
||||
shapes: ['square'],
|
||||
ticks: 500,
|
||||
}
|
||||
confetti({
|
||||
...defaults,
|
||||
particleCount: 80,
|
||||
spread: 100,
|
||||
origin: { y: 0 },
|
||||
})
|
||||
setTimeout(() => {
|
||||
confetti({
|
||||
...defaults,
|
||||
particleCount: 50,
|
||||
angle: 60,
|
||||
spread: 80,
|
||||
origin: { x: 0 },
|
||||
})
|
||||
}, 250)
|
||||
setTimeout(() => {
|
||||
confetti({
|
||||
...defaults,
|
||||
particleCount: 50,
|
||||
angle: 120,
|
||||
spread: 80,
|
||||
origin: { x: 1 },
|
||||
})
|
||||
}, 400)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.passed,
|
||||
(v) => {
|
||||
if (v)
|
||||
setTimeout(congrats, 300)
|
||||
},
|
||||
{ flush: 'post' },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div />
|
||||
</template>
|
||||
111
.vitepress/components/CustomNotFound.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { withBase } from 'vitepress'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
|
||||
import { useLangs } from 'vitepress/dist/client/theme-default/composables/langs'
|
||||
import Sweep from './sweep.vue';
|
||||
|
||||
const { site, theme } = useData()
|
||||
const { localeLinks } = useLangs({ removeCurrent: false })
|
||||
|
||||
const root = ref('/')
|
||||
onMounted(() => {
|
||||
const path = window.location.pathname
|
||||
.replace(site.value.base, '')
|
||||
.replace(/(^.*?\/).*$/, '/$1')
|
||||
if (localeLinks.value.length) {
|
||||
root.value =
|
||||
localeLinks.value.find(({ link }) => link.startsWith(path))?.link ||
|
||||
localeLinks.value[0].link
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="NotFound">
|
||||
<p class="code">{{ theme.notFound?.code ?? '404' }}</p>
|
||||
<h1 class="title">{{ theme.notFound?.title ?? 'PAGE NOT FOUND' }}</h1>
|
||||
<div class="divider" />
|
||||
<div class="action">
|
||||
<a
|
||||
class="link"
|
||||
:href="withBase(root)"
|
||||
:aria-label="theme.notFound?.linkLabel ?? 'go to home'"
|
||||
>
|
||||
{{ theme.notFound?.linkText ?? 'Take me home' }}
|
||||
</a>
|
||||
</div>
|
||||
<blockquote class="quote">
|
||||
{{
|
||||
theme.notFound?.quote ??
|
||||
"虽然你迷路了,但是这是一个扫雷小游戏"
|
||||
}}
|
||||
</blockquote>
|
||||
<Sweep />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.NotFound {
|
||||
padding: 64px 24px 96px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.NotFound {
|
||||
padding: 96px 32px 168px;
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
line-height: 128px;
|
||||
font-size: 128px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-top: 12px;
|
||||
letter-spacing: 2px;
|
||||
line-height: 20px;
|
||||
font-size: 35px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 24px auto 1px;
|
||||
width: 64px;
|
||||
height: 1px;
|
||||
background-color: var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.quote {
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
max-width: 256px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.action {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline-block;
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
border-radius: 16px;
|
||||
padding: 3px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand);
|
||||
transition:
|
||||
border-color 0.25s,
|
||||
color 0.25s;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
border-color: var(--vp-c-brand-dark);
|
||||
color: var(--vp-c-brand-dark);
|
||||
}
|
||||
</style>
|
||||
183
.vitepress/components/CustomSwitchAppearance.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
|
||||
import { APPEARANCE_KEY } from 'vitepress/dist/client/shared'
|
||||
import VPSwitch from 'vitepress/dist/client/theme-default/components/VPSwitch.vue'
|
||||
import VPIconSun from 'vitepress/dist/client/theme-default/components/icons/VPIconSun.vue'
|
||||
import VPIconMoon from 'vitepress/dist/client/theme-default/components/icons/VPIconMoon.vue'
|
||||
|
||||
const { site, isDark } = useData()
|
||||
const checked = ref(false)
|
||||
const toggle = typeof localStorage !== 'undefined' ? useAppearance() : () => {}
|
||||
|
||||
onMounted(() => {
|
||||
checked.value = document.documentElement.classList.contains('dark')
|
||||
})
|
||||
|
||||
// @ts-expect-error: Transition API
|
||||
const isAppearanceTransition = document.startViewTransition &&
|
||||
!window.matchMedia(`(prefers-reduced-motion: reduce)`).matches
|
||||
|
||||
function useAppearance() {
|
||||
const query = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const classList = document.documentElement.classList
|
||||
|
||||
let userPreference = localStorage.getItem(APPEARANCE_KEY)
|
||||
|
||||
let isDark =
|
||||
(site.value.appearance === 'dark' && userPreference == null) ||
|
||||
(userPreference === 'auto' || userPreference == null
|
||||
? query.matches
|
||||
: userPreference === 'dark')
|
||||
|
||||
query.onchange = (e) => {
|
||||
if (userPreference === 'auto') {
|
||||
setClass((isDark = e.matches))
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(event: MouseEvent) {
|
||||
if (!isAppearanceTransition) {
|
||||
setClass((isDark = !isDark))
|
||||
|
||||
userPreference = isDark
|
||||
? query.matches ? 'auto' : 'dark'
|
||||
: query.matches ? 'light' : 'auto'
|
||||
|
||||
localStorage.setItem(APPEARANCE_KEY, userPreference)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const x = event.clientX
|
||||
const y = event.clientY
|
||||
const endRadius = Math.hypot(
|
||||
Math.max(x, innerWidth - x),
|
||||
Math.max(y, innerHeight - y),
|
||||
)
|
||||
|
||||
// @ts-expect-error: Transition API
|
||||
const transition = document.startViewTransition(() => {
|
||||
setClass((isDark = !isDark))
|
||||
|
||||
userPreference = isDark
|
||||
? query.matches ? 'auto' : 'dark'
|
||||
: query.matches ? 'light' : 'auto'
|
||||
|
||||
localStorage.setItem(APPEARANCE_KEY, userPreference)
|
||||
})
|
||||
|
||||
transition.ready.then(() => {
|
||||
const clipPath = [
|
||||
`circle(0px at ${x}px ${y}px)`,
|
||||
`circle(${endRadius}px at ${x}px ${y}px)`,
|
||||
]
|
||||
|
||||
document.documentElement.animate(
|
||||
{
|
||||
clipPath: isDark ? clipPath : [...clipPath].reverse(),
|
||||
},
|
||||
{
|
||||
duration: 300,
|
||||
easing: 'ease-in',
|
||||
pseudoElement: isDark ? '::view-transition-new(root)' : '::view-transition-old(root)',
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function setClass(dark: boolean): void {
|
||||
const css = document.createElement('style')
|
||||
css.type = 'text/css'
|
||||
css.appendChild(
|
||||
document.createTextNode(
|
||||
`:not(.VPSwitchAppearance):not(.VPSwitchAppearance *) {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
transition: none !important;
|
||||
}`
|
||||
)
|
||||
)
|
||||
document.head.appendChild(css)
|
||||
|
||||
checked.value = dark
|
||||
classList[dark ? 'add' : 'remove']('dark')
|
||||
|
||||
const _ = window.getComputedStyle(css).opacity
|
||||
document.head.removeChild(css)
|
||||
}
|
||||
|
||||
return toggle
|
||||
}
|
||||
|
||||
watch(checked, (newIsDark) => {
|
||||
isDark.value = newIsDark
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<label title="toggle dark mode">
|
||||
<VPSwitch
|
||||
class="VPSwitchAppearance"
|
||||
:class="{ 'VPSwitchAppearanceTransition': isAppearanceTransition }"
|
||||
:aria-checked="checked"
|
||||
@click="toggle"
|
||||
>
|
||||
<VPIconSun class="sun" />
|
||||
<VPIconMoon class="moon" />
|
||||
</VPSwitch>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sun {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.moon {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dark .sun {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dark .moon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.VPSwitchAppearance.VPSwitchAppearanceTransition {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.dark .VPSwitchAppearance:not(.VPSwitchAppearanceTransition) :deep(.check) {
|
||||
/*rtl:ignore*/
|
||||
transform: translateX(18px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
::view-transition-old(root) {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
::view-transition-new(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dark::view-transition-old(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dark::view-transition-new(root) {
|
||||
z-index: 9999;
|
||||
}
|
||||
</style>
|
||||
73
.vitepress/components/MineBlock.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<script setup>
|
||||
defineProps(
|
||||
{
|
||||
block: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
['block']
|
||||
)
|
||||
|
||||
const numberColors = [
|
||||
'text-transparent',
|
||||
'text-blue-500',
|
||||
'text-green-500',
|
||||
'text-yellow-500',
|
||||
'text-orange-500',
|
||||
'text-red-500',
|
||||
'text-purple-500',
|
||||
'text-pink-500',
|
||||
'text-teal-500',
|
||||
]
|
||||
|
||||
function getBlockClass(block) {
|
||||
if (block.flagged)
|
||||
return 'bg-gray-500-10'
|
||||
if (!block.revealed)
|
||||
return 'bg-gray-500-10 hover:bg-gray-500-20'
|
||||
|
||||
return block.mine
|
||||
? 'bg-red-500-50'
|
||||
: numberColors[block.adjacentMines]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.text-transparent { color: transparent; }
|
||||
.text-blue-500 { color: #3b82f6; }
|
||||
.text-green-500 { color: #10b981; }
|
||||
.text-yellow-500 { color: #f59e0b; }
|
||||
.text-orange-500 { color: #f97316; }
|
||||
.text-red-500 { color: #ef4444; }
|
||||
.text-red { color: rgba(248, 113, 113); }
|
||||
.text-purple-500 { color: #8b5cf6; }
|
||||
.text-pink-500 { color: #ec4899; }
|
||||
.text-teal-500 { color: #14b8a6; }
|
||||
.bg-gray-500-10 { background-color: rgba(107, 114, 128, 0.1); }
|
||||
.bg-gray-500-20 { background-color: rgba(107, 114, 128, 0.2); }
|
||||
.bg-red-500-50 { background-color: rgba(239, 68, 68, 0.5); }
|
||||
.font-600 { font-weight: 600; }
|
||||
.button-block { width: 30px; height: 30px; }
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<button
|
||||
style="display: flex; align-items: center; justify-content: center; min-width: 2rem; min-height: 2rem; margin: 1px; border: 0.5px solid rgba(166, 166, 166, 0.1);"
|
||||
:class="getBlockClass(block)"
|
||||
>
|
||||
<template v-if="block.flagged">
|
||||
<div class="text-red button-block">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24"><path fill="currentColor" d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6h-5.6Z"/></svg>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="block.revealed">
|
||||
<div v-if="block.mine" class="button-block">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24"><path fill="currentColor" d="M23 13v-2h-3.07a7.988 7.988 0 0 0-1.62-3.9l2.19-2.17l-1.43-1.43l-2.17 2.19A7.988 7.988 0 0 0 13 4.07V1h-2v3.07c-1.42.18-2.77.74-3.9 1.62L4.93 3.5L3.5 4.93L5.69 7.1A7.988 7.988 0 0 0 4.07 11H1v2h3.07c.18 1.42.74 2.77 1.62 3.9L3.5 19.07l1.43 1.43l2.17-2.19c1.13.88 2.48 1.44 3.9 1.62V23h2v-3.07c1.42-.18 2.77-.74 3.9-1.62l2.17 2.19l1.43-1.43l-2.19-2.17a7.988 7.988 0 0 0 1.62-3.9H23M12 8a4 4 0 0 0-4 4H6a6 6 0 0 1 6-6v2Z"/></svg>
|
||||
</div>
|
||||
<div v-else class="font-600">
|
||||
{{ block.adjacentMines }}
|
||||
</div>
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
161
.vitepress/components/sweep.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<script setup>
|
||||
import { GamePlay } from '../composables/logic.js'
|
||||
import { useNow, useStorage } from '@vueuse/core'
|
||||
import { watchEffect } from 'vue'
|
||||
import MineBlock from './MineBlock.vue'
|
||||
import Confetti from './Confetti.vue'
|
||||
const play = new GamePlay(9, 9, 10)
|
||||
|
||||
const now = $(useNow())
|
||||
const timerMS = $computed(() => Math.round(((play.state.value.endMS || +now) - play.state.value.startMS) / 1000))
|
||||
|
||||
useStorage('vuesweeper-state', play.state)
|
||||
const state = $computed(() => play.board)
|
||||
|
||||
const mineRest = $computed(() => {
|
||||
if (!play.state.value.mineGenerated)
|
||||
return play.mines
|
||||
return play.blocks.reduce((a, b) => a + (b.mine ? 1 : 0) - (b.flagged ? 1 : 0), 0)
|
||||
})
|
||||
|
||||
function newGame(difficulty) {
|
||||
switch (difficulty) {
|
||||
case 'very easy':
|
||||
play.reset(3, 3, 1)
|
||||
break
|
||||
case 'easy':
|
||||
play.reset(9, 9, 10)
|
||||
break
|
||||
case 'medium':
|
||||
play.reset(16, 16, 40)
|
||||
break
|
||||
case 'hard':
|
||||
play.reset(16, 30, 99)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
play.checkGameState()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex gap1 justify-center p4">
|
||||
<button class="btn" @click="play.reset()">
|
||||
New Game
|
||||
</button>
|
||||
<button class="btn" @click="newGame('very easy')">
|
||||
Very Easy
|
||||
</button>
|
||||
<button class="btn" @click="newGame('easy')">
|
||||
Easy
|
||||
</button>
|
||||
<button class="btn" @click="newGame('medium')">
|
||||
Medium
|
||||
</button>
|
||||
<button class="btn" @click="newGame('hard')">
|
||||
Hard
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-10 justify-center">
|
||||
<div class="text-2xl flex gap-1 items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32"><path fill="currentColor" d="M15 11h2v9h-2zm-2-9h6v2h-6z"/><path fill="currentColor" d="m28 9l-1.42-1.41l-2.25 2.25a10.94 10.94 0 1 0 1.18 1.65ZM16 26a9 9 0 1 1 9-9a9 9 0 0 1-9 9Z"/></svg>
|
||||
{{ timerMS }}
|
||||
</div>
|
||||
<div class="text-2xl flex gap-1 items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24"><path fill="currentColor" d="M23 13v-2h-3.07a7.988 7.988 0 0 0-1.62-3.9l2.19-2.17l-1.43-1.43l-2.17 2.19A7.988 7.988 0 0 0 13 4.07V1h-2v3.07c-1.42.18-2.77.74-3.9 1.62L4.93 3.5L3.5 4.93L5.69 7.1A7.988 7.988 0 0 0 4.07 11H1v2h3.07c.18 1.42.74 2.77 1.62 3.9L3.5 19.07l1.43 1.43l2.17-2.19c1.13.88 2.48 1.44 3.9 1.62V23h2v-3.07c1.42-.18 2.77-.74 3.9-1.62l2.17 2.19l1.43-1.43l-2.19-2.17a7.988 7.988 0 0 0 1.62-3.9H23M12 8a4 4 0 0 0-4 4H6a6 6 0 0 1 6-6v2Z"/></svg>
|
||||
{{ mineRest }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p5 w-full overflow-auto">
|
||||
<div
|
||||
v-for="row, y in state"
|
||||
:key="y"
|
||||
class="flex items-center justify-center w-max ma"
|
||||
>
|
||||
<MineBlock
|
||||
v-for="block, x in row" :key="x"
|
||||
:block="block"
|
||||
@click="play.onClick(block)"
|
||||
@dblclick="play.autoExpand(block)"
|
||||
@contextmenu.prevent="play.onRightClick(block)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div flex="~ gap-1" justify-center>
|
||||
<button btn @click="toggleDev()">
|
||||
{{ isDev ? 'DEV' : 'NORMAL' }}
|
||||
</button>
|
||||
</div> -->
|
||||
|
||||
<Confetti :passed="play.state.value.status === 'won'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.gap1 {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.gap-1 {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.gap-10 {
|
||||
gap: 2.5rem;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.btn {
|
||||
background-color: var(--vp-button-brand-bg);
|
||||
border: 1px solid var(--vp-c-brand-light);
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: var(--vp-c-brand-light);
|
||||
border-color: var(--vp-c-brand-light);
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
.btn:dark {
|
||||
background-color: var(--vp-c-brand-dark);
|
||||
border-color: var(--vp-c-brand-light);
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
.p4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
.p5 {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.w-max {
|
||||
width: max-content;
|
||||
}
|
||||
.overflow-auto {
|
||||
overflow: auto;
|
||||
}
|
||||
.ma {
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
210
.vitepress/composables/logic.js
Normal file
@@ -0,0 +1,210 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const directions = [
|
||||
[1, 1],
|
||||
[1, 0],
|
||||
[1, -1],
|
||||
[0, -1],
|
||||
[-1, -1],
|
||||
[-1, 0],
|
||||
[-1, 1],
|
||||
[0, 1],
|
||||
];
|
||||
|
||||
export class GamePlay {
|
||||
state = ref();
|
||||
|
||||
constructor(width, height, mines) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.mines = mines;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
get board() {
|
||||
return this.state.value.board;
|
||||
}
|
||||
|
||||
get blocks() {
|
||||
return this.state.value.board.flat();
|
||||
}
|
||||
|
||||
reset(width = this.width, height = this.height, mines = this.mines) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.mines = mines;
|
||||
|
||||
this.state.value = {
|
||||
startMS: +Date.now(),
|
||||
endMS: undefined, // 确保结束时间戳被重置
|
||||
mineGenerated: false,
|
||||
status: 'play',
|
||||
board: Array.from({ length: this.height }, (_, y) =>
|
||||
Array.from({ length: this.width }, (_, x) => ({
|
||||
x,
|
||||
y,
|
||||
mine: false, // 初始化 mine 属性
|
||||
flagged: false, // 初始化 flagged 属性
|
||||
adjacentMines: 0,
|
||||
revealed: false,
|
||||
}),
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
randomRange(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
randomInt(min, max) {
|
||||
return Math.round(this.randomRange(min, max));
|
||||
}
|
||||
|
||||
generateMines(state, initial) {
|
||||
const placeRandom = () => {
|
||||
const x = this.randomInt(0, this.width - 1);
|
||||
const y = this.randomInt(0, this.height - 1);
|
||||
const block = state[y][x];
|
||||
if (Math.abs(initial.x - block.x) <= 1 && Math.abs(initial.y - block.y) <= 1)
|
||||
return false;
|
||||
if (block.mine)
|
||||
return false;
|
||||
block.mine = true;
|
||||
return true;
|
||||
};
|
||||
Array.from({ length: this.mines }, () => null)
|
||||
.forEach(() => {
|
||||
let placed = false;
|
||||
let attempts = 0;
|
||||
const maxAttempts = 1000;
|
||||
while (!placed) {
|
||||
if (attempts++ > maxAttempts) {
|
||||
this.reset();
|
||||
break;
|
||||
}
|
||||
placed = placeRandom();
|
||||
}
|
||||
});
|
||||
this.updateNumbers();
|
||||
}
|
||||
|
||||
updateNumbers() {
|
||||
this.board.forEach((raw) => {
|
||||
raw.forEach((block) => {
|
||||
if (block.mine)
|
||||
return;
|
||||
this.getSiblings(block)
|
||||
.forEach((b) => {
|
||||
if (b.mine)
|
||||
block.adjacentMines += 1;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
expendZero(block) {
|
||||
if (block.adjacentMines)
|
||||
return;
|
||||
|
||||
this.getSiblings(block)
|
||||
.forEach((s) => {
|
||||
if (!s.revealed) {
|
||||
s.revealed = true;
|
||||
this.expendZero(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRightClick(block) {
|
||||
if (this.state.value.status !== 'play')
|
||||
return;
|
||||
|
||||
if (block.revealed)
|
||||
return;
|
||||
block.flagged = !block.flagged;
|
||||
}
|
||||
|
||||
onClick(block) {
|
||||
if (this.state.value.status !== 'play')
|
||||
return;
|
||||
|
||||
if (!this.state.value.mineGenerated) {
|
||||
this.generateMines(this.board, block);
|
||||
this.state.value.mineGenerated = true;
|
||||
}
|
||||
|
||||
block.revealed = true;
|
||||
if (block.mine) {
|
||||
this.onGameOver('lost');
|
||||
return;
|
||||
}
|
||||
|
||||
this.expendZero(block);
|
||||
}
|
||||
|
||||
getSiblings(block) {
|
||||
return directions.map(([dx, dy]) => {
|
||||
const x2 = block.x + dx;
|
||||
const y2 = block.y + dy;
|
||||
if (x2 < 0 || x2 >= this.width || y2 < 0 || y2 >= this.height)
|
||||
return undefined;
|
||||
return this.board[y2][x2];
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
showAllMines() {
|
||||
this.board.flat().forEach((i) => {
|
||||
if (i.mine)
|
||||
i.revealed = true;
|
||||
});
|
||||
}
|
||||
|
||||
checkGameState() {
|
||||
if (!this.state.value.mineGenerated)
|
||||
return;
|
||||
const blocks = this.board.flat();
|
||||
|
||||
if (blocks.every(block => block.revealed || block.flagged || block.mine)) {
|
||||
if (blocks.some(block => block.flagged && !block.mine))
|
||||
this.onGameOver('lost');
|
||||
else
|
||||
this.onGameOver('won');
|
||||
}
|
||||
}
|
||||
|
||||
autoExpand(block) {
|
||||
const siblings = this.getSiblings(block);
|
||||
const flags = siblings.reduce((a, b) => a + (b.flagged ? 1 : 0), 0);
|
||||
const notRevealed = siblings.reduce((a, b) => a + (!b.revealed && !b.flagged ? 1 : 0), 0);
|
||||
if (flags === block.adjacentMines) {
|
||||
siblings.forEach((i) => {
|
||||
if (i.revealed || i.flagged)
|
||||
return;
|
||||
i.revealed = true;
|
||||
this.expendZero(i);
|
||||
if (i.mine)
|
||||
this.onGameOver('lost');
|
||||
});
|
||||
}
|
||||
const missingFlags = block.adjacentMines - flags;
|
||||
if (notRevealed === missingFlags) {
|
||||
siblings.forEach((i) => {
|
||||
if (!i.revealed && !i.flagged)
|
||||
i.flagged = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onGameOver(status) {
|
||||
this.state.value.status = status;
|
||||
this.state.value.endMS = +Date.now();
|
||||
if (status === 'lost') {
|
||||
this.showAllMines();
|
||||
setTimeout(() => {
|
||||
alert('lost');
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
// import { defineConfig } from 'vitepress'
|
||||
import { withMermaid } from "vitepress-plugin-mermaid";
|
||||
import { withMermaid } from "vitepress-plugin-mermaid-xyxsw";
|
||||
import mathjax3 from 'markdown-it-mathjax3';
|
||||
import { main_sidebar, chapter2, chapter3, chapter4, chapter5, chapter6, chapter7, chapter8, chapter9 } from './sidebar.js';
|
||||
import { nav } from './nav.js';
|
||||
import PanguPlugin from 'markdown-it-pangu'
|
||||
import { createWriteStream } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { SitemapStream } from 'sitemap'
|
||||
|
||||
const links = []
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
import VueMacros from 'unplugin-vue-macros/vite'
|
||||
import Vue from '@vitejs/plugin-vue'
|
||||
|
||||
const customElements = [
|
||||
'mjx-container',
|
||||
@@ -104,8 +102,9 @@ const customElements = [
|
||||
export default withMermaid({
|
||||
lang: 'zh-CN',
|
||||
title: "HDU-CS-WIKI",
|
||||
description: "HDU计算机科学讲义",
|
||||
description: "HDU 计算机科学讲义",
|
||||
lastUpdated: true,
|
||||
cleanUrls: true,
|
||||
head: [['script', { async: "async", src: 'https://umami.hdu-cs.wiki/script.js', "data-website-id": "3f11687a-faae-463a-b863-6127a8c28301", "data-domains": "wiki.xyxsw.site,hdu-cs.wiki" }]],
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
@@ -156,24 +155,31 @@ export default withMermaid({
|
||||
isCustomElement: (tag) => customElements.includes(tag),
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
transformHtml: (_, id, { pageData }) => {
|
||||
if (!/[\\/]404\.html$/.test(id))
|
||||
links.push({
|
||||
// you might need to change this if not using clean urls mode
|
||||
url: pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'),
|
||||
lastmod: pageData.lastUpdated
|
||||
})
|
||||
},
|
||||
buildEnd: async ({ outDir }) => {
|
||||
const sitemap = new SitemapStream({
|
||||
hostname: 'https://hdu-cs.wiki/'
|
||||
})
|
||||
const writeStream = createWriteStream(resolve(outDir, 'sitemap.xml'))
|
||||
sitemap.pipe(writeStream)
|
||||
links.forEach((link) => sitemap.write(link))
|
||||
sitemap.end()
|
||||
await new Promise((r) => writeStream.on('finish', r))
|
||||
sitemap: {
|
||||
hostname: 'https://hdu-cs.wiki'
|
||||
},
|
||||
vite: {
|
||||
plugins: [
|
||||
VueMacros(),
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^.*\/VPSwitchAppearance\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL('./components/CustomSwitchAppearance.vue', import.meta.url)
|
||||
)
|
||||
},
|
||||
{
|
||||
find: /^.*\/NotFound\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL('./components/CustomNotFound.vue', import.meta.url)
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export function main_sidebar() {
|
||||
{ text: '1.10如何读论文', link: '/1.杭电生存指南/1.10如何读论文' },
|
||||
{ text: '1.11陷入虚无主义?进来看看吧', link: '/1.杭电生存指南/1.11陷入虚无主义?进来看看吧' },
|
||||
{ text: '1.12选课原则与抢课技巧', link: '/1.杭电生存指南/1.12选课原则与抢课技巧' },
|
||||
{ text: '1.13数学学习篇', link: '/1.杭电生存指南/1.13数学学习篇'},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -224,6 +225,14 @@ export function chapter3() {
|
||||
{ text: '3.6.4.5阶段五:迭代生成', link: '/3.编程思维体系构建/3.6.4.5阶段五:迭代生成' },
|
||||
{ text: '3.6.4.6结语', link: '/3.编程思维体系构建/3.6.4.6结语' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '3.6.5CS61A食用指南',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '3.6.5CS61A食用指南', link: '/3.编程思维体系构建/3.6.5CS61A食用指南' },
|
||||
{ text: '3.6.5.1CS61A Sec1', link: '/3.编程思维体系构建/3.6.5.1CS61A Sec1' },
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -424,7 +433,8 @@ export function chapter4() {
|
||||
{ text: '4.9如何做研究', link: '/4.人工智能/4.9如何做研究' },
|
||||
{ text: '4.10科研论文写作', link: '/4.人工智能/4.10科研论文写作' },
|
||||
{ text: '4.11从 AI 到 智能系统 —— 从 LLMs 到 Agents', link: '/4.人工智能/4.11从 AI 到 智能系统 —— 从 LLMs 到 Agents' },
|
||||
{ text: '4.12本章节内容的局限性', link: '/4.人工智能/4.12本章节内容的局限性' },
|
||||
{ text: '4.12LLM Agent之结构化输出', link: '/4.人工智能/4.12LLMAgent之结构化输出' },
|
||||
{ text: '4.13本章节内容的局限性', link: '/4.人工智能/4.13本章节内容的局限性' },
|
||||
{ text: 'SRT社团介绍', link: '/4.人工智能/SRT' },
|
||||
{
|
||||
text: 'FunRec',
|
||||
@@ -583,6 +593,7 @@ export function chapter6() {
|
||||
},
|
||||
{ text: '6.3密码学', link: '/6.计算机安全/6.3密码学' },
|
||||
{ text: '6.4安全杂项', link: '/6.计算机安全/6.4安全杂项' },
|
||||
{ text: '6.5学习资料推荐', link: '/6.计算机安全/6.5学习资料推荐' },
|
||||
]
|
||||
},
|
||||
]
|
||||
@@ -660,4 +671,4 @@ export function chapter9() {
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import './rainbow.css'
|
||||
|
||||
let homePageStyle = undefined
|
||||
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
Layout: () => {
|
||||
@@ -27,7 +26,7 @@ export default {
|
||||
|
||||
watch(
|
||||
() => ctx.router.route.data.relativePath,
|
||||
() => updateHomePageStyle(location.pathname === '/' || location.pathname === '/contributors.html'),
|
||||
() => updateHomePageStyle(location.pathname === '/' || location.pathname === '/contributors'),
|
||||
{ immediate: true },
|
||||
)
|
||||
},
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
/**
|
||||
* Colors
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
@import url('https://fonts.loli.net/css2?family=Noto+Sans+Mono:wght@400&family=Noto+Sans+SC:wght@400;500;700&display=swap');
|
||||
|
||||
@font-face {
|
||||
font-family: 'Noto Color Emoji';
|
||||
font-style: normal;
|
||||
@@ -14,28 +17,19 @@
|
||||
unicode-range: U+200d, U+261d, U+2620, U+2639-263a, U+2665, U+270a-270d, U+2728, U+2763-2764, U+2b50, U+fe0f, U+1f31a-1f31f, U+1f32b, U+1f383, U+1f389, U+1f3fb-1f3ff, U+1f440-1f450, U+1f463-1f465, U+1f479-1f47b, U+1f47d-1f480, U+1f485, U+1f48b-1f48c, U+1f493-1f49f, U+1f4a4-1f4a6, U+1f4a8-1f4ab, U+1f4af, U+1f525, U+1f573, U+1f590, U+1f595-1f596, U+1f5a4, U+1f5e3, U+1f600-1f644, U+1f648-1f64a, U+1f64c, U+1f64f, U+1f90c-1f925, U+1f927-1f92f, U+1f932-1f933, U+1f970-1f976, U+1f978-1f97a, U+1f9a0, U+1f9b4-1f9b7, U+1f9bb, U+1f9be-1f9bf, U+1f9d0, U+1f9e0-1f9e1, U+1fa75-1fa79, U+1fac0-1fac2, U+1fae0-1fae6, U+1fae8, U+1faf0-1faf8;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Noto Sans SC';
|
||||
font-weight: 400;
|
||||
src: url('./font/NotoSansSC-Regular.otf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Noto Sans SC';
|
||||
font-weight: 700;
|
||||
src: url('./font/NotoSansSC-Bold.otf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Noto Sans Mono';
|
||||
font-weight: 400;
|
||||
src: url('./font/NotoSansMono-Regular.ttf');
|
||||
}
|
||||
|
||||
.dark .vp-doc a,#loading, .dark .vp-doc a>code, .dark .VPNavBarMenuLink.VPNavBarMenuLink:hover, .dark .VPNavBarMenuLink.VPNavBarMenuLink.active, .dark .link.link:hover, .dark .link.link.active, .dark .edit-link-button.edit-link-button, .dark .pager-link .title {
|
||||
color: var(--vp-c-brand-lighter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个版本可能有 bug,build 后字体变不了还为 consolas 可能是我自己配的有问题 可能是这个版本拉了 总之就加个临时的
|
||||
* -------------------------------------------------------------------------- */
|
||||
code {
|
||||
font-family: 'Noto Sans Mono', sans-serif, monospace, consolas !important;
|
||||
font-weight: 400 !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
--vp-c-brand: #0dadc4;
|
||||
--vp-c-brand-light: #1A9CED;
|
||||
@@ -50,14 +44,6 @@
|
||||
--vp-font-family-base: 'Noto Sans SC', 'Noto Color Emoji', sans-serif;
|
||||
--vp-font-family-mono: 'Noto Sans Mono', sans-serif, monospace, consolas;
|
||||
}
|
||||
/**
|
||||
* 这个版本可能有 bug,build 后字体变不了还为 consolas 可能是我自己配的有问题 可能是这个版本拉了 总之就加个临时的
|
||||
* -------------------------------------------------------------------------- */
|
||||
code {
|
||||
font-family: 'Noto Sans Mono', sans-serif, monospace, consolas !important;
|
||||
font-weight: 400 !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Button
|
||||
|
||||
@@ -3,22 +3,30 @@
|
||||
> author: ek1ng
|
||||
>
|
||||
> 本文编写自2021.07.31,也许有些内容已经过时,请选择性参考。
|
||||
|
||||
## 选课原则
|
||||
|
||||
### 要选哪些课
|
||||
|
||||
在杭电,学生必须修读完培养计划上所有的课程才能够毕业,因此选课遵循的唯一纲要便是培养计划,一切以培养计划上的要求为准,体育课、通识必修课、通识选修课、专业选修课、专业必修课等等,请注意任何选课尽可能请以培养计划的课程代码为准,若课程代码不同则需要通过课程替代等方式。
|
||||
|
||||
### 为什么要选课
|
||||
|
||||
选课其实是选老师,而选择的选课老师的背后则是课程考核方式、给分高低、成绩占比、课堂签到情况等等。选择正确的老师能够使课堂更加符合你的预期,不论是教学质量还是教学方式亦或期末分数上。
|
||||
|
||||
在讨论之前,必须声明一些学校课程的基本要求,例如学校要求老师采取易班点名的方式,所以除了某些老师以外大多数老师都会采取不定次数的课堂点名以及点教室人头的方式作为考核考勤情况的方式。
|
||||
|
||||
### 学校开什么课
|
||||
|
||||
学校开展体育课(大一大二4学期分别修读不同类别的体育课程4次)、通识选修课 ~~(通常各专业需要修读人文类、国际类、科技类学分4、4、2个)~~
|
||||
:::warning
|
||||
2023年更新:现在是国际类、人文类、艺术类、科技类学分4、2、2、2个(不管怎么变应该加起来都是10分)
|
||||
:::
|
||||
|
||||
### 选什么课好
|
||||
在杭电换客群或者与认识的室友、同学、朋友等等交流开展某课程的某老师教学情况,打听情报以选择合适的老师。此处的情报通常指老师的教学方式如何,采取怎样的签到方式等等。如果曾经上过某老师开展的a课程,那么通常对于他开展的b课程,签到情况和给分情况都仍然能够适用,但是教学情况则未必。
|
||||
|
||||
### 常见问题Q&A
|
||||
|
||||
Q1:我该如何安排我这一学期的课程?
|
||||
|
||||
A1:尽可能按照培养计划给出的每学期修读建议修读,适当先在大一、大二修读通识选修、体育课以及适量的专业必修专业选修,在大三修读更多的专业课程。当然如果完全打乱培养计划在杭电也是完全允许的,你完全可以在大一、大二修读大三的课程,在大三修读大一的课程,这么做的意义也许是你希望通过修读课程顺序的改变来调整你个人的时间安排,不论如何这是符合学校规章制度的操作,只要你想你就可以这么做。
|
||||
@@ -27,7 +35,7 @@ Q2:选课所谓第一轮选课第二轮选课到底是如何进行的?
|
||||
|
||||
A2:第一轮选课可以选择的课为体育课、通识选修课(英语拓展课、大学军事、公选课都包括在内)、推荐课表上的课程。第二轮选课可以跨年级跨学院跨专业选课,只要能搜到的课都是可以上的,不过请注意,思修近代史毛概马原形策等课程并不能提前修读。第二轮选课通常进行到开学后3周,在开学前3周,可以通过签课的方式即在授课老师、学院的批准通过下选上自己没有抢到的课,理论上任何课(需要注意公选课、体育课一般无法签课,老师是否同意签课通常看老师的个人情况,学院通常会在老师批准的情况下通过批准)只要经过批准都可以签课成功后出现在课表上。请注意,学分上限为32.5(不包括暑假短学期课程),转专业后学分上限自动扩为40,若非转专业学生可以在绩点3.0以上的情况下开学前3周内提出扩学分请求。
|
||||
:::warning
|
||||
2023年更新:学分上限好像变高了,但我忘了是多少🤡
|
||||
Update: 新系统学分上限为36,扩学分后上限为40。
|
||||
:::
|
||||
|
||||
Q3:大一上如何选课?
|
||||
@@ -35,20 +43,29 @@ Q3:大一上如何选课?
|
||||
A3:大一上选课的安排在最后时间段,体育被默认选了太极拳,并选不到“好”的公选课专业课等等,不太建议选很多课,选一门新生研讨课或者推荐不选课。
|
||||
|
||||
## 抢课技巧
|
||||
|
||||
### 选课背景
|
||||
|
||||
2021年上学期的选课中,杭电更换了正方全新的选课平台http://newjw.hdu.edu.cn ,目前选课平台的特性为,在选课时间内开放至公网ip可以访问,可以并发请求,~~并不会网页卡崩~~,抢课全靠手速。
|
||||
|
||||
:::tip
|
||||
1. 鉴于杭电复杂的网络环境,在**内网**抢课甚至不如**公网**😅,所以建议在寝室里连上网线(公网)抢课。
|
||||
|
||||
2. **网页会卡崩**,刚开始选课0~5分钟系统会未响应甚至将你踢出登录,还会让你浏览器爆掉显示 *欧呦,崩溃啦* 之类的字样,一切听天由命。
|
||||
:::
|
||||
|
||||
## 具体技巧
|
||||
|
||||
#### 系统开放前
|
||||
|
||||
通常系统开放前可以查询开课情况,那么可以根据开课情况自己提前规划安排想上的课程。
|
||||
|
||||
#### 系统开放时
|
||||
|
||||
##### 第一轮选课、第二轮选课开放系统时
|
||||
|
||||
提前在粘贴板中剪切/复制第一手要抢的课程,并且在选课平台开放时间前几秒,不停点击刷新,直至选课平台显示的内容不为非选课时间而是可以搜索,粘贴课程名并且点击抢课即可第一手抢到最想抢的课程,再依次抢接下来准备上的课。若课表上已经被系统默认选的课占了想要选的课的位置,那么就需要先退课再选课。
|
||||
|
||||
##### 假期以及开学前三周
|
||||
|
||||
这时候会有同学因为不想选某门课程或者通过将课卡在其他人号上想在假期“转移”到自己号上,选课系统中的课程余量就会时不时出现波动,此时可以上去系统看看说不定能捡漏哦。签课以及扩学分在开学前三周进行,请关注学校通知并且通过签课选上自己没能选上的课程。
|
||||
|
||||
263
1.杭电生存指南/1.13数学学习篇.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# 数学学习篇
|
||||
|
||||
> author:张晓鹏
|
||||
>
|
||||
> 本文章仅为导引
|
||||
|
||||
## 认知部分
|
||||
|
||||
其实之前这一部分写了一大堆,但感觉文字实在是太太太多了,又觉得这种认知篇应该在生存指南部分,因此重新修改了一个相对精简版,希望读者能理解我的意思,也容忍我的啰嗦。(实际上还是很多啊啊啊啊)
|
||||
|
||||
在开始正式这一篇章前,我们需要先闲聊几句,这几句无关你的学习,但又可能无时无刻与你的学习相关。
|
||||
|
||||
1. 互联网时代下,学习资源过分的多,但学习资源的**质量参差不齐**。
|
||||
|
||||
- 啰嗦一:国内的教培太过魔怔,各种速成课、考研课,这种总结式的,反刍式的学习资源,在前期学习是极其不推荐的,它很可能会限制你的思维。
|
||||
- 啰嗦二:很多课程不是为你电定制的,你电虽然不算特别好的学校,但也不算差的,按照我老师的说法,根据当年精英教育比例来看,我们虽然算垫底,但还是精英教育。互联网上有非常非常多的课程是为了二三本同学,播放量高也是如此,毕竟市场人数差很大啊!
|
||||
|
||||
2. 资源的**好坏定义取决于你自身的适配度**,适配度越高越好,并不是难度越高越好。
|
||||
|
||||
- 解释:他人认为的极好的书籍或课程,对他来说适配度 100%,对你来说却可能是 0%,因为你可能完全学不懂,或者对你而言,其学习过程完全没有兴趣。
|
||||
|
||||
3. 时间是极其稀贵的物品,不要浪费你的时间!
|
||||
|
||||
- 解释:对于大部分工科学生来说,**数学更多是工具**,需要的是数学应用,而不是像数学系同学一样,理解数学的理论。诚然,能深刻理解数学,是能更好的利用工具,甚至创造工具,但是时间成本太高,大部分情况下性价比过低。
|
||||
|
||||
- 有人会说:专业学到很深的时候,后面会涉及大量数学理论,因此他要前期打下极其扎实的数学基础。
|
||||
|
||||
- 笔者的回复是:你不是小说男主,你不需要一开始就选择逆天功法,踏踏实实的努力足以支撑你的小世界。如果为了后续极高的上限,而在前期花费过多时间,其一,你很可能在应用层面被同龄人甩远而承受打击;其二,你很可能学了一堆其他领域侧重的数学理论,这在你实际专业和工作中可能完全用不到;其三,数学学不完的,他不像高中有考纲,有穷尽。
|
||||
|
||||
4. 高效利用时间的前提就是**明确自我需求**,针对需求进行**合理取舍**,建议设计一个尽可能适合自己的学习计划,过程中可以询问学长学姐一些建议,但仅是建议,最终决定权在你!
|
||||
|
||||
- 解释:比如你未来的梦想工作是算法工程师,那么你在对应算法领域的数学要多尝试些,而其他部分可以省略些;如果你的未来工作和数学没有关系!那么别学了(开个玩笑,但是要求就变成了能过就行)!
|
||||
- 啰嗦:学长学姐仅比你早一些来到学校,与你也没有责任与义务,其所表达观点也不一定对的,甚至大部分情况下,很多人的观念就是很被动的,千万千万不要被限制了,请主动思考什么是合适你的,什么是你想要的!
|
||||
|
||||
5. 学习资源不代表视频资源,公共性课程视频资源很多,但随着专业深入,视频会越来越少,书籍和文献会成为主要学习路径,特别是外文文献。因此在能继续学习的前提下,**多尝试直接啃书**,而不是看视频。
|
||||
|
||||
- 啰嗦:国内的一些论文和书籍真的是依托答辩!原因没法告诉,这是不能碰的滑梯,但理工科文献尽可能就别看国内的了,说不定里面就有错误,然后影响你很久!(不代表国外就没错误,但是国内的这个整体问题比例太离谱了)
|
||||
|
||||
6. 相信国外热门的资源都会有本土化,但是本土化的水平可能参差不齐,当自己**过于难理解时**,**请看一下英文原著**,因为可能问题不在于你,而在于翻译。
|
||||
|
||||
- 啰嗦:有些书籍的翻译是依托答辩!甚至题目都会给你抄错!
|
||||
|
||||
7. **不要拒绝英文**,尝试拥抱,即便这看起来很困难,但慢慢会好起来的。(这与你的高考英语一点关系没有,别担心,你可以的!)
|
||||
|
||||
8. 国内和国外的教材区别比较大,有一个恰当又不恰当的比喻:**国内的多数教材就像讲义**,看似清清楚楚,实则云里雾里,需要老师来带才能理解;**国外的经典教材如同仙人指路**,带你拨开迷雾。
|
||||
|
||||
- 啰嗦:这里并不代表国外就好,国内就差,前提也是国外经典教材,其次国情不同,国内中学阶段都是阅读讲义,因此大学这样编写也是正常的,默认需要老师来讲。(即便笔者觉得这很不合适)
|
||||
|
||||
## 推荐学习线路篇
|
||||
|
||||
### 高等数学
|
||||
|
||||
#### 整体框架介绍
|
||||
|
||||
这里我对高等数学做一些简单的个人视角的介绍
|
||||
|
||||
其实高等数学/微积分,**全篇内容都在讲极限**,而这也是大学数学与高中数学最大的不同,高等数学的一切都**建立于无穷之上**,而极限是无穷的一种极好的表达方式。如果你在学习的过程中,全篇以极限的角度去审视这些内容,你就会发现,这里面所有的运算都是极限运算,包括导数、积分,甚至级数部分,前期可能感受不深,越到后面越发会感受到,一切的运算都是极限运算,那些所谓的性质和一些不能使用的特殊情况,往往也都来源于这个最本质的家伙,极限!就这样,是不是感觉讲了没讲,没错,还真是!因为数学太深了,再细节下去就不是一篇文章所讲的完了。还想知道更多吗?那就去看下面的**3Blue1Brown 的微积分本质**!
|
||||
|
||||
下面到这个文章没啥意思的推荐部分了。不过还是强调一句:**根据你的需求进行选择**!!!
|
||||
|
||||
#### 系统性的网课推荐
|
||||
|
||||
这里没有放速成课和考研课,因为笔者认为这两类课程的功利性过强,并且对知识的总结过多,不利于未来深入学习其他内容,因此不算传统学习路线,将速成课放在了最后应试技巧部分(考研课没推荐,因为笔者是数学系同学,这些不清楚,是一点也没看过)
|
||||
|
||||
个人推荐优先级(以难度和深度划分):上交大乐经良>国防科大版本>宋浩
|
||||
|
||||
风格上前两者更传统,内容也更系统深刻,个人更喜欢乐老师一些;第三个宋浩老师课堂相对更活跃些,会讲比较多的段子,难度也更低一些。
|
||||
|
||||
系统性的网课选其一能完整跟下来即可(跟不下来也正常,坚持挺难的)
|
||||
|
||||
如果你只是为了考试,选择宋浩版本就足够了;如果为了未来工作,工程应用方面会涉及比较多,请选择前两个更系统的课程。如果觉得这些不合适,那么可以自行搜索选择其他课程。
|
||||
|
||||
##### 《高等数学》上交大乐经良老师
|
||||
|
||||
- 链接:[【高等数学 - 上海交通大学 - 乐经良老师 高清修复 1080p(全集)】](https://www.bilibili.com/video/BV1aY4y137fr/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
|
||||
<Bilibili bvid='BV1aY4y137fr'/>
|
||||
|
||||
##### 国防科大《高等数学》
|
||||
|
||||
- 链接:[【国防科大高等数学【全网最佳】](https://www.bilibili.com/video/BV1F7411B7ep/?p=291&share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
|
||||
<Bilibili bvid='BV1F7411B7ep'/>
|
||||
|
||||
##### 宋浩《高等数学》
|
||||
|
||||
- 链接:[【《高等数学》同济版 全程教学视频(宋浩老师)】](https://www.bilibili.com/video/BV1Eb411u7Fw/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
|
||||
<Bilibili bvid='BV1Eb411u7Fw'/>
|
||||
|
||||
##### 《微积分》苏德矿
|
||||
|
||||
这个版本比较特别,比较**偏向经管类**的同学,因此没有放在前面比较。
|
||||
|
||||
- 【[微积分(浙江大学 苏德矿/矿爷)】](https://www.bilibili.com/video/BV1Lt411r7NQ/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
|
||||
<Bilibili bvid='BV1Lt411r7NQ'/>
|
||||
|
||||
- 如果苏德矿老师版本不太能接受的话,请看宋浩老师版本的微积分,但需要注意的是**宋浩老师版本与杭电考试范围有所不同**,可能需要缺失部分补齐一下。
|
||||
- 因为笔者对经管类数学了解远不如理工科数学,因此这里不做过多阐述,同学可以自行搜索了解。
|
||||
|
||||
#### 教材推荐
|
||||
|
||||
这一块相对简略一些,主要是几个点
|
||||
|
||||
1. 如果是自学,不推荐看同济版教材,和前面说的一样,那个像讲义,并且难度也不够,太浅了,需要老师来给你上课做额外注记。
|
||||
2. 图灵系列的书籍都是很不错的,并且套系的书很多,具体的可以先不着急买,可以先自行搜索电子书(前面章节应该有教的)
|
||||
3. 书单链接:[数学经典教材有什么? - 人民邮电出版社的回答 - 知乎](https://www.zhihu.com/question/22302252/answer/1733795665)
|
||||
4. 自行选择,能读完一个本就足够了,而且读不完正常,根据以往经验的不科学推断,大部分人最多看完网课,书本草草翻过。
|
||||
|
||||
#### 辅助工具推荐
|
||||
|
||||
这部分内容与传统的网课不同,更倾向于**知识体系的辅助构建和补充**,某种程度上你可以认为是精华内容。
|
||||
|
||||
##### 3Blue1Brown 的微积分本质
|
||||
|
||||
这个名字听起来就很不错,对吧!
|
||||
|
||||
1. 链接:[3Blue1Brown 的个人空间_哔哩哔哩_bilibili](https://space.bilibili.com/88461692/channel/seriesdetail?sid=1528931)
|
||||
2. 内容是英文的,但实际上不会怎么影响,若是过于难接受英文,可以自行搜索汉语翻译版。
|
||||
3. 这部分内容和传统的高数教学不同,从直观的**图形角度**,讲解各个微积分中的重要概念由来和应用。
|
||||
4. 学习时间:与传统网课无任何冲突,可以在**任何时间观看**,即传统网课的前、中、后三个阶段均可看,并且不同阶段看感受不同,**建议反复观看**。
|
||||
5. 学习难度:简单又不简单,简单在于讲解的方式非常通俗,图形化知识非常直观,不简单在于其本身内容是深刻的,彻底理解这些内容或许需要不断反复的看以及配合传统网课的学习,搭建完知识体系后顿悟。
|
||||
|
||||
##### Brilliant
|
||||
|
||||
1. 链接:[https://brilliant.org/](https://brilliant.org/)。
|
||||
2. 国外非常火的直观学习数学网站,国内本土化产物是马同学图解数学,个人不推荐购买马同学图解数学,因为据个人了解,里面错误似乎比较多,但可以看其知乎上的好文章。
|
||||
3. 这个网站不做过多介绍,自己打开玩一玩就明白了。
|
||||
4. 额外补充一:这是一个付费内容,前几天是免费的,如果你觉得他值得,可以进行购买,购买途径可以官网,但第三方价格会便宜些,比如淘宝、闲鱼、PDD。
|
||||
5. 额外补充二:一个好用的翻译浏览器插件:沉浸式翻译,可以更好的体验。
|
||||
|
||||
##### MIT-微积分重点
|
||||
|
||||
1. 链接:[【我在 B 站上大学!【完整版 - 麻省理工 - 微积分重点】全 18 讲!学数学不看的微积分课程,看完顺滑一整年。_人工智能数学基础/机器学习/微积分/麻省理工/高等数学】](https://www.bilibili.com/video/BV1rY4y1P7er/?p=2&share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
<Bilibili bvid='BV1rY4y1P7er'/>
|
||||
2. 非常**优雅的入门课**,同前面 3Blue1Brown 的微积分本质一样的使用方法。
|
||||
3. 注意,这个是**入门课**,不算系统课程!
|
||||
4. **建议看完!**
|
||||
|
||||
### 线性代数
|
||||
|
||||
#### 整体框架介绍
|
||||
|
||||
下面又是我一点点个人理解,好吧,其实就一句话,别担心!线性代数是一个很特别的学科,刚开始很难,中间很混乱,最后很通透,因为全篇都在以不同的角度阐述相同又不同的内容,所以某种意义上,线性代数可以从任何一章节开始学习(当然,实际上要根据教材来,不然很怪啦)
|
||||
|
||||
而关于线性代数整体研究什么,我极力推荐下面辅助部分的丘维声先生高等代数第一节高等代数研究对象(就第一节哦!因为这是数学系课程,偏理论,非数同学看多了不合适)
|
||||
|
||||
#### 系统性的网课推荐
|
||||
|
||||
与高等数学模块相同,这里没有放速成课和考研课,因为笔者认为这两类课程的功利性过强,并且对知识的总结过多,不利于未来深入学习其他内容,因此不算传统学习路线,将速成课放在了最后应试技巧部分(考研课没推荐,因为笔者是数学系同学,这些不清楚,是一点也没看过)
|
||||
|
||||
这里只推荐两个网课,如果觉得自身不合适,可以自行去寻找其他更合适的。
|
||||
|
||||
##### MIT 版的线性代数
|
||||
|
||||
1. 链接:[【麻省理工学院 - MIT - 线性代数(我愿称之为线性代数教程天花板)】](https://www.bilibili.com/video/BV16Z4y1U7oU/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
<Bilibili bvid='BV16Z4y1U7oU'/>
|
||||
2. 评价:神中神!未来做工程应用的学生很推荐看这个!
|
||||
|
||||
##### 宋浩线性代数
|
||||
|
||||
1. 链接:[【《线性代数》高清教学视频“惊叹号”系列 宋浩老师】](https://www.bilibili.com/video/BV1aW411Q7x1/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
<Bilibili bvid='BV1aW411Q7x1'/>
|
||||
2. 评价:MIT 系列过于强大,宋浩版本显得有些暗淡无光,但实际上宋浩老师版本完全是可以应对考试,如果接受不了 MIT 的版本,还是可以考虑有趣的宋浩老师。
|
||||
|
||||
#### 教材推荐
|
||||
|
||||
与前面高等数学部分相同,这里只做几个点说明
|
||||
|
||||
1. 有一些线性代数的图解书籍或者讲几何意义的,可以看,但这里没做推荐,因为很多书不太严谨,可以自行搜索。
|
||||
2. 书单链接:[有没有讲线性代数比较好的教材? - 如何表达的回答 - 知乎](https://www.zhihu.com/question/586392502/answer/2913648674)
|
||||
3. 主推荐还是 MIT 网课老爷子的配套书籍,
|
||||
4. 尽可能看英文原版,不要害怕。
|
||||
5. 《线性代数及其应用》千万别看翻译版,有很大问题,要看只看原版!
|
||||
|
||||
#### 辅助部分
|
||||
|
||||
跟上面的高数部分一样,不过对于线性代数来说,可视化的理解会更有效果
|
||||
|
||||
##### 3Blue1Brown 的线性代数本质
|
||||
|
||||
1. 链接:[【官方双语/合集】线性代数的本质 - 系列合集】](https://www.bilibili.com/video/BV1ys411472E/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
<Bilibili bvid='BV1ys411472E'/>
|
||||
2. 神中神!多看,反复看!不允许学线性代数的人不看这个视频!
|
||||
3. 其他不多说,和前面高数一样
|
||||
|
||||
##### Brilliant
|
||||
|
||||
1. 链接:[https://brilliant.org/](https://brilliant.org/)
|
||||
2. 和高数部分一样,不多说了
|
||||
|
||||
##### 丘维声《高等代数》第一节课--高等代数研究对象
|
||||
|
||||
这个课程是数学系同学学习的,也是笔者学习的课程,本来不该给非数学系的同学推荐,但是这里面的第一节讲的太好了,能让你很快的构建起一个**大概的框架**,并且能很好的避免传统无脑填鸭式的上来就给你讲行列式的课程体系(**点名批评线性代数紫皮书**,也就是杭电教材,同济大学版的线代)
|
||||
|
||||
1. 链接:[【北大丘维声教授清华高等代数课程 1080P 高清修复版 (全 151 集)】](https://www.bilibili.com/video/BV1jR4y1M78W/?share_source=copy_web&vd_source=1958d03181be22cf265b18eeec1314ff)
|
||||
<Bilibili bvid='BV1jR4y1M78W'/>
|
||||
2. 再强调一下嗷,就**只看第一节**,也就是 001 和 002 两个视频。因为后续整个内容偏理论,应用层太少,不适合传统工科,但这第一节,绝对能让你搭建一个大概的**框架**,助力后续学习不晕眩!
|
||||
|
||||
##### 线性代数可视化手册
|
||||
|
||||
- 一个非常好的笔记,总结的很不错,但刚开始看会看不明白(毕竟是总结),建议学完一遍再看
|
||||
|
||||
- 链接:[kenjihiranabe/The-Art-of-Linear-Algebra: Graphic notes on Gilbert Strang's "Linear Algebra for Everyone" (github.com)](https://github.com/kenjihiranabe/The-Art-of-Linear-Algebra)
|
||||
- 别告诉我不会在 GitHub 上下载文件哈(不会就去学下,利用 AI 工具
|
||||
|
||||
## 其他部分
|
||||
|
||||
### 新生先修课
|
||||
|
||||
【Warning】这里别的不能多说,只能简略提几点,希望你能懂。
|
||||
|
||||
1. 这是自愿内容,不是必须的。
|
||||
2. 合理利用机制,可以考虑替代期中成绩,为部分同学刷分需要(注!不是所有人都要刷分的!具体看 1.6 节正确解读 GPA)。
|
||||
3. 这份网课面向的是全体学子,其不一定适合你,或过于简单,或效率不高,但你应该尝试作为独立个体将其与其他课程进行对比,选择合适你自己的,为自己负责。
|
||||
4. 相信互联网资源的筛选法则。
|
||||
|
||||
### 未央学社数学答疑(应试部分)
|
||||
|
||||
是不是有点同学会觉得很奇怪,未央学社不是搞技术的吗,好像 java 后端很厉害,实际上这里打个小广告,未央学社除了技术部,还有讲师团和运营部,而讲师团专门负责给大家数学答疑和整理资料,我们构建了**HDU 数学营**,里面有很多资料,比如**往年卷**,比如我们专属出品**复习提纲**等,还有**非常多的同学互帮互助**,不说了,直接来看吧!
|
||||
|
||||
#### 未央学社 HDU 数学营
|
||||
|
||||

|
||||
|
||||
#### 钉钉答疑
|
||||
|
||||
我们还提供答疑服务,详细内容可以看下面的推文哈
|
||||
|
||||
[未央学社学业答疑服务来啦 | 助力解决学业问题](https://mp.weixin.qq.com/s/FmwT_V8j4we22KzJiHWmtQ)
|
||||
|
||||
#### 应试技巧
|
||||
|
||||
##### 往年卷
|
||||
|
||||
因为知识点不会变化,每年只是侧重有些区别,并且大部分题型还是一样的,因此往年卷有助于快速提分,做 3 份往年卷,你就会发现,欸,好像每年差不多。
|
||||
|
||||
##### 速成视频
|
||||
|
||||
个人不推荐猴博士,不适合杭电,看了容易挂科。
|
||||
|
||||
为了不挂科,速成方面,微信公众号:蜂考,相对合适一些,但仅用于速成!
|
||||
|
||||
关于视频资源:可以支持购买正版,也可以 PDD,TB 等地方获取。
|
||||
|
||||
##### 高分复习顺序(个人推荐版)
|
||||
|
||||
这部分内容仅为个人看法,不代表一定能高分,也不代表不这样做拿不到高分。
|
||||
|
||||
最完整的复习顺序:知识点→书本例题→课堂例题(一般有 PPT)→作业题→书本课后题→往年卷真题
|
||||
|
||||
考试题型来源:作业题和例题,因此当回顾作业题完成后,做往年卷会有特别感受。
|
||||
|
||||
随时间可不做的优先级(若时间来不及,先砍)
|
||||
|
||||
1. 书本课后题
|
||||
2. 作业题
|
||||
3. 知识点(这里值得是细看知识点,不是粗看,粗看都不看,直接跳转第 7 步)
|
||||
4. 课堂例题
|
||||
5. 书本例题
|
||||
6. 往年卷真题
|
||||
7. 如果平时没努力的话,这时候可以开始准备补考了
|
||||
@@ -60,7 +60,7 @@
|
||||
计算机信息安全竞赛在社会上的知名度并不高,但它和 ACM 一样是强技术竞赛,无任何 PPT 环节以及展示环节(除了一些少数比赛如国赛省赛有知识竞赛这一环节/赛道),技术就是唯一取胜的关键。CTF 是计算机安全竞赛的一种赛制,全称“Capture The Flag”,即夺旗赛,比赛选手需利用自己的计算机安全技术完成挑战,成功拿下题目后会获得一个使用`flag{}`包裹的字符串,这个字符串就是flag,提交到平台上就可以获得一定的分数。
|
||||
|
||||
杭电的 CTF 竞赛历史悠久,Vidar-Team 信息安全协会的主打比赛就是 CTF,据本文档编写之时算起已有 15 年的历史,大大小小国内国外获奖无数,本 wiki 的计算机安全部分也由信息安全协会编写。
|
||||

|
||||

|
||||
|
||||
### 优点
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
CTF 在计算机安全类招聘,以及计算机安全类研究生招生中占有很大作用,安全类企业在招聘时更喜欢 CTF 选手,就好像算法岗更喜欢 ACM 选手一样(虽然现在两者的就业都没有以前好了)。
|
||||
|
||||
计算机安全也是一个挑战性很强的领域,在国外有很高的研究热情,如今在国内也受政府大力支持,比如强网杯由河南省政府主办,奖金高达 500 万元,西湖论剑由浙江省政府主办,奖金也很丰厚。除此之外,在计算机安全领域还有漏洞赏金这一说,大型企业都会有 SRC(Security Response Center,安全应急响应中心)这种平台,上交该企业的漏洞就可以获得赏金,比如微软 RDP 远程代码执行漏洞(通过一定手段使得另一台电脑执行任意代码)赏金高达 10 万刀。VMware 虚拟机逃逸(在虚拟机内通过一定的手段可以在主机上执行任意代码)20 万刀等,越有挑战性的领域赏金越高。国内的SRC平台:https://www.anquanke.com/src
|
||||

|
||||

|
||||
|
||||
### 缺点
|
||||
|
||||
@@ -81,7 +81,7 @@ CTF还有的缺点也是目前热门领域的通病:发展速度过快,后
|
||||
|
||||
更多可以参考计算安全章节
|
||||
|
||||
[传送门](/6.%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8/6.%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8.html)
|
||||
[传送门](/6.%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8/6.%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8)
|
||||
|
||||
## 数据科学竞赛
|
||||
|
||||
@@ -123,4 +123,4 @@ CTF还有的缺点也是目前热门领域的通病:发展速度过快,后
|
||||
|
||||
由于笔者是创意组的,对其他组别不是很了解,就来介绍一下我们组别的情况。我们组别全称是智能车百度创意组,是由百度与鲸鱼机器人赞助的一个比较新的组别(目前是第三年,不过也开始卷起来了),该组别与传统组别的一个比较大的不同是对于硬件方面的要求并不高(但也是需要了解的,像 arduino ,stm32 这些稍微都得玩的起来),侧重会偏向软件算法方向。百度创意组总体分为两个阶段,线上赛和线下赛,线上赛的任务有点像是 kaggle 打榜,不过是百度官方命题,并且在飞浆平台上进行测试的,本质考验的就是你对神经网络的理解和调参(炼丹),如下图所示
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 小心项目陷阱
|
||||
|
||||

|
||||

|
||||
|
||||
## 辨别项目质量
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
但是大家参与之前你一定要想你的能力是否等于这个项目的量级,一般情况下,要么你技术特别强,要么可能会存在一定的问题。
|
||||
|
||||

|
||||

|
||||
|
||||
所以说,还是那句话,小心甄别。
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
## 序言:一组数据看出国
|
||||
|
||||

|
||||

|
||||
|
||||
点开图片可以看到左边是西交利物浦的出国数据,中间是杭电的数据,右边是成都电子科技大学的数据,可以看出西交利物浦每年去名校的数量大概是杭电一年的 20-40 倍,成电是杭电的一年的 10 倍左右,杭电出国数据实际上只有寥寥几个名校,剩下的则是一些英国院校。
|
||||
|
||||
这其中原因除了杭电高性价比的就业环境,双非院校选择出国深造的人数较低,我认为比较还有一点则是信息差。优秀的大学都有他们自己的飞跃手册,其中会介绍每个国家的优劣,以及申请的一些注意事项,详见下图。
|
||||
|
||||

|
||||

|
||||
|
||||
优越的校友资源和前人留下的数据和方案,加下信息差的叠加,就像符合幂律分布的无标度网络一样,只会让我们的差距越来越大,在感叹其他学校飞跃手册的优越性的同时也发现了一些不可参考性,因为在其中也存在着很多问题,在这些学校中,可能名校读博就是老师写个推荐信就有了面试的机会,可能去港大 / G5 就是大学四年均分 80 分的难度。有很多资料在不同的角度,我们很难参考,所以我不经会想问?那我们呢,我们考多少多少分能够去什么学校,我们想要直博应该怎么准备,所以我打算完成这份出国留学的手册,能够为学弟学妹们铺路,同样希望后面也能够有学弟学妹做完善和补充。
|
||||
|
||||
|
||||
BIN
1.杭电生存指南/static/image-20230801062631288.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
1.杭电生存指南/static/v2-704a5d77d767493bada1feccadcd6c4c_720w.webp
Normal file
|
After Width: | Height: | Size: 54 KiB |
@@ -32,4 +32,4 @@
|
||||
|
||||
如果实在不行,来找 ZZM 聊聊吧。
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
|
||||
然后你要慢慢询问他的问题,接着你要问各种问题各种检查然后去看,如果有十个人一百个人都这么问,你肯定会受不了的吧。
|
||||
|
||||
事实上, 如果希望能提高得到回答的概率, 提问者应该学会如何更好地提问. 换句话说, 提问者应该去积极思考 "我可以主动做些什么来让对方更方便地帮助我诊断问题".
|
||||
事实上,如果希望能提高得到回答的概率,提问者应该学会如何更好地提问。换句话说,提问者应该去积极思考 "我可以主动做些什么来让对方更方便地帮助我诊断问题".
|
||||
|
||||
如果你的提问方式非常不专业, 很可能没有人愿意关注你的问题, 因为这不仅让人觉得你随便提的问题没那么重要, 而且大家也不愿意花费大量的时间向你来回地咨询.
|
||||
如果你的提问方式非常不专业,很可能没有人愿意关注你的问题,因为这不仅让人觉得你随便提的问题没那么重要,而且大家也不愿意花费大量的时间向你来回地咨询。
|
||||
|
||||
<Bilibili bvid='BV1om4y1H71S'/>
|
||||
|
||||
## 解决
|
||||
|
||||
@@ -28,13 +30,37 @@
|
||||
|
||||
问题还是没有解决,现在我该怎么做?
|
||||
|
||||

|
||||

|
||||
|
||||
欢迎大家阅读
|
||||
### 关于截图
|
||||
|
||||
[Stop-Ask-Questions-The-Stupid-Ways/README.md at master · tangx/Stop-Ask-Questions-The-Stupid-Ways](https://github.com/tangx/Stop-Ask-Questions-The-Stupid-Ways/blob/master/README.md) 别像弱智一样提问
|
||||
如果你在问问题的时候掏出一张手机拍电脑🤳🖥️的图片,那么大概率会让想要帮助你解决问题的学长血压升高然后放弃回答你的问题。
|
||||
|
||||
[https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) 提问的智慧
|
||||
除非遇到一些特殊情况(例如你的电脑进 BIOS 了),只能手机拍照,也请保证图片清晰便于识别。
|
||||
|
||||

|
||||
|
||||
在 wiki 的[2.2 优雅的使用工具](2.2优雅的使用工具.md),有推荐一些好用开源的截图工具
|
||||
|
||||
不过一般情况来说,Windows 的组合键`Win+Shift+S`截屏,在任意窗口按下这个组合键便可进入截屏模式,按住鼠标左键拖动框选区域即可截屏,使用`Ctrl+V`操作将截屏内容粘贴到想要保存的地方;如果已经登录了 QQ,那么 QQ 的截图快捷键默认是`Ctrl+Alt+A`,同样使用`Ctrl+V`粘贴截屏内容。
|
||||
|
||||
记住这两个快捷键已经足够满足你对截图的 90% 的需求了
|
||||
|
||||
### 橡皮鸭
|
||||
|
||||
> 来自伯克利大学的学习建议
|
||||
|
||||
当遇到问题时,除了截图外,试着组织语言来解释你遇到困难的地方。
|
||||
|
||||

|
||||
|
||||
<strong>这并不需要一个找到懂得如何解决问题的人 (或者甚至是一个人 —— 这种做法通常被称为橡皮鸭,因为你可以把一只橡皮鸭当作你的练习对象) ,因为主要目标是让你弄清楚你自己的想法,弄清楚你的理解和代码到底在哪里卡住了。这样你可以知道应该专注于哪一部分,以便更好地理解。</strong>
|
||||
|
||||
### 欢迎大家阅读
|
||||
|
||||
[Stop-Ask-Questions-The-Stupid-Ways](https://github.com/tangx/Stop-Ask-Questions-The-Stupid-Ways/blob/master/README.md) 别像弱智一样提问
|
||||
|
||||
[How-To-Ask-Questions-The-Smart-Way](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) 提问的智慧
|
||||
|
||||
## 关于如何搜索代码
|
||||
|
||||
@@ -56,11 +82,11 @@ cv.waitKey(0)
|
||||
接下来,我会去搜索每行代码的作用:(以下是搜索后我做的注释)
|
||||
|
||||
```python
|
||||
import cv2 as cv # 调opencv库
|
||||
import cv2 as cv # 调 opencv 库
|
||||
img = cv.imread('lbxx.jpg',1) # 读取图片(“图片路径”)
|
||||
img_1 = cv.cvtColor(img,cv.COLOR_BGR2GRAY) # 转换成灰度图(图片, 颜色模式)
|
||||
cv.imshow('gray',img_1) # 展示图片(展示img_1为灰度图)
|
||||
cv.imshow('colour',img) # 展示图片(展示img为彩色图)
|
||||
img_1 = cv.cvtColor(img,cv.COLOR_BGR2GRAY) # 转换成灰度图(图片,颜色模式)
|
||||
cv.imshow('gray',img_1) # 展示图片(展示 img_1 为灰度图)
|
||||
cv.imshow('colour',img) # 展示图片(展示 img 为彩色图)
|
||||
cv.waitKey(0) # 保存展示窗口打开
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
"我们要学 C 语言,我买一本大黑书看看!"
|
||||
|
||||

|
||||

|
||||
|
||||
诚然,上面的各种书写的非常好,但是我们需要思考的是,阅读这些真的能达到我们想要的目标吗???
|
||||
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
|
||||
在你完成这份讲义的时候,希望你可以有选择的阅览一部分,然后带着问题去看某些课,效率也会高很多。
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
**并且,全部依赖他人给你指明方向的人生已经结束了!**
|
||||
|
||||

|
||||

|
||||
|
||||
你无法依赖大学里的老师还是家里的父母来为你指明所谓的方向,你的人生只属于你自己,你的道路也只能由你自己来思考。
|
||||
|
||||
|
||||
@@ -12,13 +12,17 @@
|
||||
|
||||
- [Everything](https://www.voidtools.com/zh-cn/downloads/) 电脑上的全局文件搜索 方便你找到不知道丢哪的文件
|
||||
- [SpaceSniffer](http://www.uderzo.it/main_products/space_sniffer/download.html) 快速分析硬盘空间占用情况 解放储存,不解放大脑
|
||||
- [Snipaste](https://zh.snipaste.com/) 全局截图工具,按 F1 键截图,F3 键贴图,简单够用
|
||||
- [eSearch](https://esearch.vercel.app/) 全局截图工具,优化了文字识别功能,可个性化,支持全平台
|
||||
- [ShareX](https://esearch.vercel.app/) 全局截图工具,功能非常强大,高度可个性化,仅支持 Win
|
||||
- [IDM](https://www.internetdownloadmanager.com/) :好用的多线程下载器(付费的),想要免费的话可以搜一下绿色版之类的。(推荐设置线程数为 CPU 核心数的 2 倍,比如 8 核心的 CPU 设置线程数为 16)
|
||||
- [XDM](https://github.com/subhra74/xdm) :IDM 的跨平台版本。
|
||||
- [uTools](https://www.u.tools/) :自由组合插件集(最好用的是 Alt+Space 搜索功能)非常强大,比如安装 fileshare 可以在局域网共享超大文件,而且是跨平台的。
|
||||
- [PowerToys](https://github.com/microsoft/PowerToys) :微软官方出品,包含诸多功能,解决 windows 下一些小痛点。
|
||||
- [Connect to Work or Games from Anywhere | Parsec](https://parsec.app/) :串流小工具,简单来说你就是可以在手机上玩电脑了,远程操作,极致体验~~(也可以玩游戏)~~
|
||||
- [VMware workstation](../3.%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E4%BD%93%E7%B3%BB%E6%9E%84%E5%BB%BA/3.Y.1VMware%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E5%AE%89%E8%A3%85Ubuntu22.04%E7%B3%BB%E7%BB%9F.md):虚拟机就用它!但是最好自己找找盗版,正版要钱。
|
||||
- [cloc](https://github.com/AlDanial/cloc): 统计代码行数(空白行,注释行,代码行)的小工具
|
||||
- mv & cp 命令显示进度条: 在复制大文件的时候非常友好,可以通过以下脚本安装(Linux系统)
|
||||
- mv & cp 命令显示进度条:在复制大文件的时候非常友好,可以通过以下脚本安装(Linux 系统)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
@@ -50,13 +54,13 @@ rm coreutils-8.32 coreutils-8.32.tar.xz
|
||||
|
||||
## 笔记工具
|
||||
|
||||
- [Typora](https://typora.io/) 付费的,~~你可以去并夕夕啊淘宝啊花个不多于 5 块钱的钱买盗版 😋~~,( 正版 $14.99 ),真的好用,感觉没有 Markdown 编辑器能好用过 Typora🤥。
|
||||
- [Typora](https://typora.io/) 付费的,~~你可以去并夕夕啊淘宝啊花个不多于 5 块钱的钱买盗版 😋~~,(正版 $14.99),真的好用,感觉没有 Markdown 编辑器能好用过 Typora🤥。
|
||||
- [MarkText](https://github.com/marktext/marktext) 免费的,平替 Typora。
|
||||
- [MiaoYan](https://github.com/tw93/MiaoYan) 仅支持 apple ,界面挺清爽。
|
||||
- [MiaoYan](https://github.com/tw93/MiaoYan) 仅支持 apple,界面挺清爽。
|
||||
- [思源笔记](https://b3log.org/siyuan/) 一个国产开源的笔记/知识库软件,优势是 本地化、双链、Markdown 语法,与 Obsidian 定位相似,但 Geek 成分和自定义空间相对更高
|
||||
- [Zotero](https://www.zotero.org/):协助文献阅读还有写笔记,支持与平板同传(同时他是开源的,所以可以添加一些插件)
|
||||
|
||||

|
||||

|
||||
|
||||
- [Notion](http://notion.so): 笔记终结者,非常强大,(设计理念被钉钉,飞书,我来非常抄袭)。在线就可以使用。
|
||||
|
||||
@@ -67,8 +71,8 @@ rm coreutils-8.32 coreutils-8.32.tar.xz
|
||||
|
||||
## 浏览器插件
|
||||
|
||||
- [沉浸式翻译](https://immersivetranslate.com/docs/installation/):中英文对照翻译,可以给你英文下面写一小行中文翻译(里面免费的 api 只有谷歌,必应,腾讯,不过够了,也可以自行配置其他 api )
|
||||
- (你真的不玩原神吗)来试试这款原神浏览器插件 [派蒙 paimon](https://github.com/daidr/paimon-webext) : 可以实时显示你的树脂,委托,派遣等情况提示。
|
||||
- [沉浸式翻译](https://immersivetranslate.com/docs/installation/):中英文对照翻译,可以给你英文下面写一小行中文翻译(里面免费的 api 只有谷歌,必应,腾讯,不过够了,也可以自行配置其他 api)
|
||||
- (你真的不玩原神吗)来试试这款原神浏览器插件 [派蒙 paimon](https://github.com/daidr/paimon-webext) :可以实时显示你的树脂,委托,派遣等情况提示。
|
||||
- [wappalyzer](https://www.wappalyzer.com/):如果你是个 web 仔,这个插件可以帮你检测网页所用的前后端技术栈。
|
||||
- [FeHelper--Web 前端助手](https://github.com/zxlie/FeHelper):十几个小工具的集合,包括 base64 离线解码等。
|
||||
- [darkreader](https://github.com/darkreader/darkreader):适应网页的暗色模式,夜深人静冲浪更爽
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
作为一名理工科学生,也许英语并不是你的强势,但往往学习又难以避开英语。
|
||||
|
||||

|
||||

|
||||
|
||||
下面提供一些英语阅读的方法:
|
||||
|
||||
1. **学好英语(顺便过四六级)**
|
||||
2. 文档阅读:使用浏览器插件,例如:[沙拉查词](https://saladict.crimx.com/)、[划词翻译](https://hcfy.app/)、[沉浸式翻译](https://immersivetranslate.com/docs/)
|
||||
2. 文档阅读:使用浏览器插件,例如:[沙拉查词](https://saladict.crimx.com/)、[划词翻译](https://hcfy.app/)、[沉浸式翻译](https://immersivetranslate.com/docs/)、[DeepL翻译](https://www.deepl.com/zh/app/)
|
||||
3. Youtube 等视频网站的双语字幕 [languagereactor](https://www.languagereactor.com/)。
|
||||
4. 实用翻译软件[复制即翻译](https://copytranslator.github.io/)。
|
||||
5. ~~Galgame 翻译 [LunaTranslator](https://github.com/HIllya51/LunaTranslator)~~
|
||||
|
||||
@@ -45,7 +45,7 @@ Search the "friendly" website
|
||||
|
||||
## 如果真的不知道怎么解决怎么办?
|
||||
|
||||

|
||||

|
||||
|
||||
来细看看本章节的内容吧!
|
||||
|
||||
|
||||
BIN
2.高效学习/static/01.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
2.高效学习/static/02.jpg
Normal file
|
After Width: | Height: | Size: 149 KiB |
@@ -38,7 +38,7 @@
|
||||
|
||||
这是我的第一个也是最重要的建议。
|
||||
|
||||
无论是学一门语言,还是学一个工具:<strong>尽可能地先用最短的时间搞懂这个东西是做什么的,然后以最快的方式把它 “run”起来。</strong>
|
||||
无论是学一门语言,还是学一个工具:<strong>尽可能地先用最短的时间搞懂这个东西是做什么的,然后以最快的方式把它“run”起来。</strong>
|
||||
|
||||
当你已经能跑起一个语言、一个工具的最简单的示例的时候,再去花时间慢慢了解背后的复杂的内容,再去拓展即可。先用起来,跑起来,带着问题去翻资料。
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
那么该怎么学呢?
|
||||
|
||||
<strong>先简单地会一样东西的最核心的部分,再去找一个实际的编程场景、编程任务、项目。你会在完成这个项目中遇到各种各样的问题,无论是遗漏了知识点还是压根没思路, 这时候不断地用搜索引擎来学习。( </strong>[2.3 高效的信息检索](../2.%E9%AB%98%E6%95%88%E5%AD%A6%E4%B9%A0/2.3%E9%AB%98%E6%95%88%E7%9A%84%E4%BF%A1%E6%81%AF%E6%A3%80%E7%B4%A2.md)<strong>)</strong>
|
||||
<strong>先简单地会一样东西的最核心的部分,再去找一个实际的编程场景、编程任务、项目。你会在完成这个项目中遇到各种各样的问题,无论是遗漏了知识点还是压根没思路,这时候不断地用搜索引擎来学习。( </strong>[2.3 高效的信息检索](../2.%E9%AB%98%E6%95%88%E5%AD%A6%E4%B9%A0/2.3%E9%AB%98%E6%95%88%E7%9A%84%E4%BF%A1%E6%81%AF%E6%A3%80%E7%B4%A2.md)<strong>)</strong>
|
||||
|
||||
举个例子:你想做一个小程序,来检测某电影院的电影预售。程序大概要做到不断刷新网页,一检测到这个电影预售了,就马上发短信给自己手机(或者直接帮你抢)
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
1. 输入决定输出。开源的代码多是经过检验的牛逼的代码,通过多看看优雅的代码来提高编程能力,比自己无中生有简单地多。
|
||||
2. 开源圈牛人多。无论是拓宽视野,还是在 issue 下的交流,还是别人给你的 review 建议,都能学到很多。你会在开源的过程中认识很多的人,很多大厂的人,说不定就是你以后的面试官。
|
||||
3. 参与开源社区能极大地锻炼自己的编程能力,能给简历贴金 。
|
||||
3. 参与开源社区能极大地锻炼自己的编程能力,能给简历贴金。
|
||||
4. 开源是程序员的浪漫。
|
||||
|
||||
对于学生而言,可以参加一些仅面向学生开放的开源活动。一般会有一个主办方,然后有许多知名的开源社区报名。他们会罗列出一些有一定难度的任务,学生可以提交申请书,陈述如何完成这个任务。中选后会分配单独的导师来带你,还会发奖金给你,一般是大几千起步。推荐阅读这个系列的文章:[https://erdengk.github.io/gsoc-analyse/](https://erdengk.github.io/gsoc-analyse/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 3.1 该使用哪个编辑器???
|
||||
|
||||
# 编辑器,编译器,集成开发环境
|
||||
## 编辑器,编译器,集成开发环境
|
||||
|
||||
我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)。
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
(你也不想用一沓纸带写程序吧)
|
||||
|
||||
## 什么是编辑器
|
||||
### 什么是编辑器
|
||||
|
||||
编辑器的概念很简单,百度百科上这么写道:
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
- <em>Vim </em>: Vim 是从 vi 发展出来的一个文本编辑器,在程序员中被广泛使用,运行在 Linux 环境下。
|
||||
- <em>GNU Emacs</em> : Emacs 是一个轻便、可扩展、免费的编辑器,它比其它的编辑器要更强大,是一个整合环境,或可称它为集成开发环境。它可以处理文字,图像,高亮语法,将代码更直观地展现给开发者。
|
||||
|
||||
## 什么是编译器
|
||||
### 什么是编译器
|
||||
|
||||
C 语言代码由固定的词汇按照固定的格式组织起来,简单直观,程序员容易识别和理解,但是对于 CPU,C 语言代码就是天书,根本不认识,CPU 只认识几百个二进制形式的指令。这就需要一个工具,将 C 语言代码转换成 CPU 能够识别的二进制指令,也就是将代码加工成 .exe 程序;这个工具是一个特殊的软件,叫做编译器(Compiler)。
|
||||
编译器能够识别代码中的词汇、句子以及各种特定的格式,并将他们转换成计算机能够识别的二进制形式,这个过程称为编译(Compile)。
|
||||
@@ -56,7 +56,7 @@ C 语言代码由固定的词汇按照固定的格式组织起来,简单直观
|
||||
|
||||
编译器可以 100% 保证你的代码从语法上讲是正确的,因为哪怕有一点小小的错误,编译也不能通过,编译器会告诉你哪里错了,便于你的更改。
|
||||
|
||||
## 什么是集成开发环境
|
||||
### 什么是集成开发环境
|
||||
|
||||
实际开发中,除了编译器是必须的工具,我们往往还需要很多其他辅助软件,例如:
|
||||
|
||||
@@ -72,18 +72,18 @@ C 语言代码由固定的词汇按照固定的格式组织起来,简单直观
|
||||
|
||||
集成开发环境也是这个道理,只有编译器不方便,所以还要增加其他的辅助工具。
|
||||
|
||||
# 我的推荐
|
||||
## 我的推荐
|
||||
|
||||
作为个人使用比较顺手的几款 IDE
|
||||
|
||||
Java: [JetBrains](https://www.jetbrains.com/zh-cn/idea/)[ IntelliJ ](https://www.jetbrains.com/zh-cn/idea/)[IDEA](https://www.jetbrains.com/zh-cn/idea/)
|
||||
Java: [JetBrains](https://www.jetbrains.com/zh-cn/idea/),[IntelliJ](https://www.jetbrains.com/zh-cn/idea/),[IDEA](https://www.jetbrains.com/zh-cn/idea/)
|
||||
|
||||
C: Visual Studio(宇宙第一 IDE), [JetBrains](https://www.jetbrains.com/zh-cn/clion/)[ Clion](https://www.jetbrains.com/zh-cn/clion/), Visual Studio Code(编辑器 IDE 化需要额外配置)
|
||||
C: [Visual Studio(宇宙第一 IDE)](https://visualstudio.microsoft.com/zh-hans/vs/), [JetBrains](https://www.jetbrains.com/zh-cn/clion/),[Clion](https://www.jetbrains.com/zh-cn/clion/), Visual Studio Code(编辑器 IDE 化需要额外配置)
|
||||
|
||||
Python: [JetBrains](https://www.jetbrains.com/zh-cn/pycharm/)[ ](https://www.jetbrains.com/zh-cn/pycharm/)[P](https://www.jetbrains.com/zh-cn/pycharm/)[ycharm](https://www.jetbrains.com/zh-cn/pycharm/)
|
||||
Python: [JetBrains Pycharm](https://www.jetbrains.com/zh-cn/pycharm/)
|
||||
|
||||
Vim 在附加篇章里有额外介绍
|
||||
|
||||
[JetBrains](https://www.cnblogs.com/Coline1/p/15229244.html)[白嫖指南](https://www.cnblogs.com/Coline1/p/15229244.html)
|
||||
[JetBrains](https://www.cnblogs.com/Coline1/p/15229244.html),[白嫖指南](https://www.cnblogs.com/Coline1/p/15229244.html)
|
||||
|
||||
当然,适合你的才是最好的
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
author:wenjing
|
||||
|
||||
先验条件:保证你可以在每天进行练习和学习此方面内容即使是假期也不能超过三天以上休息,如果你想验证一下这件事当然也可以,注意心态的保持很重要
|
||||
先验条件:保证你可以在每天进行练习和学习此方面内容即使是假期也不能超过三天以上休息,如果你想验证一下这件事当然也可以,注意心态的保持很重要
|
||||
|
||||
# 将时间花在 ACM 上值得吗?
|
||||
## 将时间花在 ACM 上值得吗?
|
||||
|
||||
初入大学,摆脱了高中的种种束缚,同学们想必对大学生活有着种种幻想。或许有同学依旧保持着高中的思维,希望刷取高绩点,用好成绩谋求保研。或许也有同学只想将课程草草应付,去探索一些偏实践的方向以谋求一份好工作。
|
||||
|
||||
但无论你渴望从大学生活谋求何物,我认为做为一位计算机专业的学生投身于 ACM 算法竞赛学习都是值得,无论你是否得奖。
|
||||
|
||||
# ACM 能为我带来什么?
|
||||
## ACM 能为我带来什么?
|
||||
|
||||
显然,做为一名计算机专业的学生,编程是一项必须掌握的技能。再次引用 Niklaus Emil Wirth 的一句话:<strong>程序=算法 + 数据结构。</strong>例如在大一开设的程序设计基础中,我们需要重点学习链表这一数据结构,熟悉运用分支与循环结构(勉强也算算法吧)。然而,在 ACM 中,这是基础到不值一提的事物,宛如空气与水一般基础。你们是否想过,花了大量课时学习的这些知识,其实小学生也可以学会(看看远处的小学编程补习班吧,家人们)那做为大学生去学习这些知识,是否应当得到一些不止于考试内容的知识呢?
|
||||
|
||||
@@ -24,7 +24,7 @@ author:wenjing
|
||||
|
||||
③ 假如你有幸活过筛选,并且获得比赛机会,并且得奖,恭喜你,你的绩点将被画上浓墨重彩的一笔。做为大学顶尖赛事,ACM 的奖项可以直接在你的最终绩点上加分(铜 0.5,银 1.0,金 1.5)这意味着你只要主课不要落下太多,奖学金随便拿(比赛获奖本身还有额外奖金)。
|
||||
|
||||
# 零基础学习 ACM 是否过于困难?
|
||||
## 零基础学习 ACM 是否过于困难?
|
||||
|
||||
我并不这么觉得,原因如下
|
||||
|
||||
@@ -40,7 +40,7 @@ UPD at 2023/7/19:从长期来看,这个结论应该是没有错的,但是
|
||||
|
||||
进队的学生零基础偏少,如果你选择这条路你可能需要克服不小的困难
|
||||
|
||||
# 我应该以什么态度学习 ACM?
|
||||
## 我应该以什么态度学习 ACM?
|
||||
|
||||
假如你是一位有信息竞赛基础,且得过省级奖项的前 oier,您也没什么必要看这篇文章,您已经完全熟悉了算法竞赛需要的一切,我希望您不要有太大压力,做最好的自己就行,不要留下遗憾。对于零基础的同学也一样,或许得奖后的绩点加成实在是过于诱人,但竞赛获奖这种事情还是太难强求,让自己压力太大得不偿失。
|
||||
|
||||
@@ -54,7 +54,7 @@ UPD at 2023/7/19:从长期来看,这个结论应该是没有错的,但是
|
||||
|
||||
之后的日子是灰暗的,浑浑噩噩的训练,知难而退放弃最好的高中的特长生名额。故事很长,我只是想说学竞赛不要太功利,竞赛终究是少数高手的游戏,做不到就是做不到,但这也仅仅只代表你的竞赛能力不够,你的人生并不是只有竞赛,大学也不只有 ACM 一条路(这很显然,不然我们社团应该改名为 ACM 社)
|
||||
|
||||
# 再谈 ACM 为我带来什么
|
||||
## 再谈 ACM 为我带来什么
|
||||
|
||||
我初中成绩并不差,但发挥失常的话确实上不了我毕业的高中。我高考发挥失常,竞赛通过杭电三一成为保底。
|
||||
|
||||
@@ -66,7 +66,7 @@ UPD at 2023/7/19:从长期来看,这个结论应该是没有错的,但是
|
||||
|
||||
截止完成这篇文章为止,笔者仍在集训队中,我害怕自己被淘汰,不是因为我害怕自己失去参赛资格,而是我很难想象自己失去 ACM 的生活,我需要一个学习 ACM 的理由。给诸位讲个笑话,某一天我与朋友出门游玩,想不到话题,于是就开始讨论算法题的做法,从正午到日落。
|
||||
|
||||
# 算法思维与应试思维
|
||||
## 算法思维与应试思维
|
||||
|
||||
众所周知,ACM 是开卷竞赛,你可以携带纸质资料进入考场。
|
||||
|
||||
@@ -88,7 +88,7 @@ UPD at 2023/7/19:从长期来看,这个结论应该是没有错的,但是
|
||||
|
||||
也许在一次次陈旧腐朽的选拔性考试中,应试思维取得了压倒性的胜利。但在 ACM 中,算法思维依旧有一片净土。
|
||||
|
||||
# 数学与算法思维
|
||||
## 数学与算法思维
|
||||
|
||||
那么,如何培养算法思维呢?我认为首先我们得学好数学。然而,我最总是在大一中听到这样的声音:“哎呀,烦死了,我们是学编程的,为什么要花那么多精力学数学,还占那么多学分,真讨厌。“然而,比起枯燥乏味的编程课,我最喜欢的还是数学课。数学在算法中无处不体现,可以说学好算法就是要学好数学,我现在复盘我初中 OI 生涯的失败,很大程度归因于数学基础的薄弱。以下为几个体现数学在算法中重要性的例子。
|
||||
|
||||
@@ -106,6 +106,6 @@ UPD at 2023/7/19:从长期来看,这个结论应该是没有错的,但是
|
||||
|
||||
优秀的数学思维能使你在理解算法的路上事半功倍,当然,算法的学习也能加深你对数学的理解。
|
||||
|
||||
# 结论
|
||||
## 结论
|
||||
|
||||
大胆去学 ACM 吧,大一的空闲时间真的很多,去探索新事物,不试试怎么知道自己行不行。
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
|
||||
在之前的篇章中,我们向新手 acmer 推荐了两个编程网站——Luogu 与 Codeforces,下面由笔者向各位介绍一下网站的详细用法。
|
||||
|
||||
# Luogu
|
||||
## Luogu
|
||||
|
||||
进入 [https://www.luogu.com.cn/](https://www.luogu.com.cn/)
|
||||
|
||||

|
||||

|
||||
|
||||
## 社交模块
|
||||
### 社交模块
|
||||
|
||||
做为一个刷题网站,Luogu 提供了符合中文用户习惯的社交模块。体现于左侧边栏的讨论及主页的最近讨论,以及底部的“发射犇犇”系统。但是我并不建议 Acmer 使用该功能,因为 Luogu 主要面向初高中生甚至小学生等参加 NOIP 系列竞赛的用户,讨论不可避免存在一些低龄化现象。对于社交模块的使用,我推荐当且仅当一种做不出题的求助手段,这点放在之后题目模块讲解。
|
||||
|
||||
## 题目模块
|
||||
### 题目模块
|
||||
|
||||
点开题库,我们看见以下界面
|
||||
|
||||

|
||||

|
||||
|
||||
在上方我们可以筛选我们想要的题目,接下来我们点开 P1000 为例
|
||||
|
||||

|
||||

|
||||
|
||||
右侧三个模块为折叠状态,下面介绍他们的作用
|
||||
|
||||
@@ -34,17 +34,17 @@
|
||||
|
||||
点击提交答案
|
||||
|
||||

|
||||

|
||||
|
||||
左侧可以选择语言类型,C++ 用户建议选择 C++14。
|
||||
|
||||
O2 优化是一种优化(废话)假如您的代码复杂度正确但 TLE,可以尝试该选项。
|
||||
|
||||
## 记录模块
|
||||
### 记录模块
|
||||
|
||||
怎么知道自己代码的问题出在哪里呢?记录模块是帮助你的好工具。
|
||||
|
||||

|
||||

|
||||
|
||||
AC:通过该数据点
|
||||
|
||||
@@ -60,47 +60,47 @@ MLE:空间超限 请检查是否递归爆栈、数组过大
|
||||
|
||||
OLE:输出超限 放心你见不到的
|
||||
|
||||
## 题单模块
|
||||
### 题单模块
|
||||
|
||||
点开侧栏题单
|
||||
|
||||

|
||||

|
||||
|
||||
建议新手从官方精选题单开始,由浅入深,由简到难。等到对算法形成概念,针对漏洞补习时可以尝试用户分享题单(到那个阶段已经有很多手段去找题了,刘教练的题单就够你做了)
|
||||
|
||||
## 比赛模块
|
||||
### 比赛模块
|
||||
|
||||
点开侧栏就能看见准备举办和已结束的比赛。笔者不建议大家在 Luogu 打比赛,首先赛制不一样,其次出题风格不一样,最后对于初学者 Luogu 比赛的难度曲线过大。
|
||||
|
||||
# Codeforces
|
||||
## Codeforces
|
||||
|
||||
进入 [https://codeforces.com/?locale=en](https://codeforces.com/?locale=en)
|
||||
|
||||

|
||||

|
||||
|
||||
比起 Luogu,这样的 UI 设计离 CN 互联网已经很远了(然而比起更硬核的一些做题网站,CF 的 UI 真是越看越顺眼)
|
||||
|
||||
右上角注册登录切语言(哇塞,可以选俄语,你说的对,但是 CF 是一款由俄罗斯开发的多人在线竞技游戏)
|
||||
|
||||
## HOME 模块
|
||||
### HOME 模块
|
||||
|
||||
主页显示各种数据,主要为近期比赛的一些公告。
|
||||
|
||||
## TOP 模块
|
||||
### TOP 模块
|
||||
|
||||
热帖,如果擅长英语的话,CF 的交流氛围还是不错的,做为一个答疑解惑的论坛肯定比国内强。
|
||||
|
||||
## CATALOG 模块
|
||||
### CATALOG 模块
|
||||
|
||||
文档目录,你可以在这学习算法竞赛入门,体系化学习算法,只要你会英语
|
||||
|
||||
## CONTESTS
|
||||
### CONTESTS
|
||||
|
||||
重中之重!CF 的比赛系统可以说是我们选择这个网站的最大原因!
|
||||
|
||||
进入比赛页面
|
||||
|
||||

|
||||

|
||||
|
||||
上方为将举办比赛,显示开始时间(UTC+8 也就是我们时区的时间)和持续时间大多都开始的比较晚,例如笔者就没有这么晚学习的习惯,所以一般赛后写题。比赛分为以下几种类型(例如写在括号里的 Div.2)
|
||||
|
||||
@@ -112,22 +112,22 @@ Div.1、Div.2、Div.3、Div.4 数字越小难度越大。
|
||||
|
||||
下面以一场 Div.2 比赛为例,展示我们该如何打一场 CF。
|
||||
|
||||
## VP
|
||||
### VP
|
||||
|
||||

|
||||

|
||||
|
||||
这是一场笔者之前赛后补过的 Div.2,画面右下角分别为赛后公告和题解,右侧便是开启 VP 的按钮。
|
||||

|
||||

|
||||
|
||||
<em>VP</em><em>模拟赛时的好处就是在虚拟参赛中获得真实比赛才能积累的经验,比如这里笔者发现通过前三题后,我应该先去看看 F 题,因为做出来的人更多,我有更大的可能性做出来,ACM 中题目并不是 100% 按难度排序。</em>
|
||||
|
||||

|
||||

|
||||
|
||||
进入 VP 后,我们可以发现比起正常赛后补题有了明显不同。
|
||||
|
||||
首先我们可以看见赛时某道题的通过人数,随比赛时间流逝 100% 仿真变化。而且也可以与当时的“虚拟选手”同步竞争,例如笔者这里就复制之前写过的代码荣登榜三(乐)
|
||||
|
||||
对于大多数比赛,采用 ICPC 赛制,解决某题得到的分数由该题当前的分数减去(不成功的提交次数)*50,这里某道题的分数是由比赛开始时的分数随时间线性减少得到的。
|
||||
对于大多数比赛,采用 ICPC 赛制,解决某题得到的分数由该题当前的分数减去 (不成功的提交次数)*50,这里某道题的分数是由比赛开始时的分数随时间线性减少得到的。
|
||||
|
||||
也就是做题越快,错误次数越少,分数和排名就越高,这点大体是与 ACM 赛制相同的。
|
||||
|
||||
@@ -135,30 +135,30 @@ Div.1、Div.2、Div.3、Div.4 数字越小难度越大。
|
||||
|
||||
让我们点开 A 题,来看看如何提交答案
|
||||
|
||||

|
||||

|
||||
|
||||
可以看见,右侧有一个 submit,与 luogu 不同的是,你需要上传源代码文件(如 cpp)然后选择 G++17 为语言,提交。
|
||||
|
||||
当然,你也可以点开上侧的 submit code
|
||||
|
||||

|
||||

|
||||
|
||||
选择题目、语言,填写代码后提交,就和 Luogu 的方式一样了。
|
||||
|
||||
同样,在上侧 MY SUBMISSIONS 处可以查看已提交的代码和状态
|
||||
|
||||

|
||||

|
||||
|
||||
## PROBLEMSET
|
||||
### PROBLEMSET
|
||||
|
||||
同样,CF 也有题库
|
||||
|
||||

|
||||

|
||||
|
||||
如果你只想做某道题而不是某场比赛,这里也许更适合你。
|
||||
|
||||
不过 CF 的题库比较鸡肋,标签筛选也不是很方便(大概是把想要的标签在右上角分隔好)
|
||||
|
||||
# 总结
|
||||
## 总结
|
||||
|
||||
笔者向读者详细介绍了两个 OJ,至于如何让 OJ 更好的辅助你的 ACM 学习,我应该在什么时间节点或训练阶段,出于什么训练目的选择哪个网站,笔者留到下一个篇章继续介绍。
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
打完比赛,建议钻研一下自己没做出的前一两题,写个题解。为什么要写题解呢,一个是方便以后来回顾,一个是加深印象,一个是把自己的思维用文字表达出来,这样能发现思维的漏洞(比如证明不严谨之类的)。题解写出来发不发博客就看个人喜好吧。作者以前也是坚持写博客写了很久。
|
||||
|
||||

|
||||

|
||||
|
||||
为什么要打 Codeforces 比赛呢?主要原因是打比赛有计时,有压力(怕掉分的压力),能让人提升更快。不要因为怕掉分就不参加了,你要相信只要你一直打比赛,你的 rating 曲线一定是波动上升的。
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 3.2 算法杂谈
|
||||
|
||||
# 学计算机要先学算法吗?
|
||||
## 学计算机要先学算法吗?
|
||||
|
||||
也许有的同学在高中阶段接触过信息学奥赛,那么也许你已经对基础的算法知识已经有了一定的了解。
|
||||
|
||||
@@ -10,19 +10,19 @@
|
||||
|
||||
学习算法的基础是拥有最基本的计算机素养,你需要优先学习一些基本的计算机概念、编程语言、简单的数据结构(数组、链表等),这些基本知识是你能够灵活利用算法的基础。
|
||||
|
||||
# 学了算法就相当于学好了计算机吗?
|
||||
## 学了算法就相当于学好了计算机吗?
|
||||
|
||||
学好了算法当然不等于学好了计算机科学。计算机科学是一个非常庞大的知识体系,包括更为底层的计算机组成原理、编译原理等,更为表层的 AI,开发等,是一门综合性学科。总的来说,算法是计算机科学中较为重要的一部分,但<strong>远远</strong>不是全部。
|
||||
|
||||
# 学算法就要用《算法导论》一类的书吗?
|
||||
## 学算法就要用《算法导论》一类的书吗?
|
||||
|
||||
我的答案是否定的。它更适合作为“工具书”(就像你英语的词典那样),而不是一本适合新生入门学习的书。可以使用《我的第一本算法书》一类的更为基础更为有趣的算法内容。相比于完全严谨的逻辑推导, 初学者的诉求是在"看得见, 摸得着的例子和环境下探索和吸收新概念". 像这样的大部头可以在之后进行阅读.
|
||||
我的答案是否定的。它更适合作为“工具书”(就像你英语的词典那样),而不是一本适合新生入门学习的书。可以使用《我的第一本算法书》一类的更为基础更为有趣的算法内容。相比于完全严谨的逻辑推导,初学者的诉求是在"看得见,摸得着的例子和环境下探索和吸收新概念". 像这样的大部头可以在之后进行阅读。
|
||||
|
||||
# 学算法一定要用 C 语言吗?不用 C 语言可以吗?
|
||||
## 学算法一定要用 C 语言吗?不用 C 语言可以吗?
|
||||
|
||||
不一定要用 C 语言。但是 C 语言作为一种贴近底层面向过程语言,对日后学习其他的语言会有较大的帮助。你也可以先学习 Python、JAVA 等等。学校的课程仅仅是教授一些比较基础的知识,如果想要真正掌握一门语言,需要在学校课程的基础上更进一大大大步。
|
||||
|
||||
# ACM 怎么说?
|
||||
## ACM 怎么说?
|
||||
|
||||
前情提要,请尽量不要以功利的心态去参加 ACM,你想要的与你能得到的可能存在过大落差
|
||||
|
||||
@@ -32,6 +32,6 @@ ACM 是美国计算机协会(Association for Computing Machinery)的缩写
|
||||
|
||||
在我校,参加 ACM 社团(姑且叫做社团)并不代表能够参加有含金量的团体赛(ICPC、CCPC 等)。你需要先参加由我校教练刘春英老师组织的各种比赛,有资格进入集训队后,才有机会代表学校参加比赛(当然不限名额的个人赛想参加就参加)。
|
||||
|
||||
进入集训队后采取末位淘汰制度(最后留下来的人在 20 人左右),最后留下来的人才有机会参加比赛。<strong>因此个人并不推荐 0 基础的同学对于 ACM 过于执着</strong>,有 0 基础的同学最后进入校队的例子,不过这通常意味着你一天至少得刷一道算法题。如果还是想尝试的同学,可以去洛谷(www.luogu.com.cn)、Codeforces(www.codeforces.com)、Atcoder(atcoder.jp)等平台上注册账号,练习题目,参加这些网站定期组织的一些比赛。
|
||||
进入集训队后采取末位淘汰制度(最后留下来的人在 20 人左右),最后留下来的人才有机会参加比赛。<strong>因此个人并不推荐 0 基础的同学对于 ACM 过于执着</strong>,有 0 基础的同学最后进入校队的例子,不过这通常意味着你一天至少得刷一道算法题。如果还是想尝试的同学,可以去洛谷 ([www.luogu.com.cn](http://www.luogu.com.cn))、Codeforces([www.codeforces.com](http://www.codeforces.com))、Atcoder([atcoder.jp](https://atcoder.jp/)) 等平台上注册账号,练习题目,参加这些网站定期组织的一些比赛。
|
||||
|
||||
如果经过一段时间的练习能够在 Codefoces([www.codeforces.com](http://www.codeforces.com))上达到 1400 以上的 Rating,那么可以再观望观望参与 ACM。
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# 3.3 如何选择编程语言
|
||||
|
||||
# 编程语言的工具属性
|
||||
## 编程语言的工具属性
|
||||
|
||||
在回答这个问题之前,需要各位同学明确的一点是,编程并不是一个独立的学科,像数学那样做题是学不好的。
|
||||
|
||||
编程语言的选择更像是锤子与扳手之间的选择,更大程度上看的是你需要解决什么样的问题。当你需要砸钉子的时候,使用螺丝刀总归是不顺手的,因此了解不同语言的特性,针对任务进行选择是非常有必要的。
|
||||
|
||||
# 编程语言特性
|
||||
## 编程语言特性
|
||||
|
||||
首先附上一张经典老图
|
||||
|
||||

|
||||

|
||||
|
||||
## C 语言/C++
|
||||
### C 语言/C++
|
||||
|
||||
C 语言/C 艹一脉同源,从图中来看,C 和 C 艹都像多功能瑞士军刀,说明其是用来做细活的工具,C 上面的优盘说明其可以进行硬件开发的相关工作。
|
||||
|
||||
@@ -26,7 +26,7 @@ C 语言其实是一门优秀的承上启下的语言,既具有高级语言的
|
||||
|
||||
<strong>我们的任务一部分会使用 C 语言,一方面培养大家编程能力,一方面辅助大家期末考试。</strong>
|
||||
|
||||
## C++
|
||||
### C++
|
||||
|
||||
现代 C++ 程序可看成以下三部分组成。
|
||||
|
||||
@@ -56,6 +56,6 @@ Python 在图里是电锯,适合干比较“狂野”的任务,也是深度
|
||||
|
||||
<strong>频繁应用于</strong><strong>W</strong><strong>eb 开发,安卓应用等等。</strong>
|
||||
|
||||

|
||||

|
||||
|
||||
当然还有各种形形色色的编程语言等着同学们去探索。
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
# FAQ:常见问题
|
||||
|
||||
# 我完全没基础觉得好难呜呜
|
||||
## 我完全没基础觉得好难呜呜
|
||||
|
||||
教育除了知识的记忆之外, 更本质的是能力的训练, 即所谓的 training. 而但凡 training 就必须克服一定的难度, 否则你就是在做重复劳动, 能力也不会有改变. 如果遇到难度就选择退缩, 或者让别人来替你克服本该由你自己克服的难度, 等于是自动放弃了获得 training 的机会
|
||||
教育除了知识的记忆之外,更本质的是能力的训练,即所谓的 training. 而但凡 training 就必须克服一定的难度,否则你就是在做重复劳动,能力也不会有改变。如果遇到难度就选择退缩,或者让别人来替你克服本该由你自己克服的难度,等于是自动放弃了获得 training 的机会
|
||||
|
||||
# 我觉得无从下手
|
||||
## 我觉得无从下手
|
||||
|
||||
尝试借鉴他人的代码也未尝不可,但是要保证每一行都看懂哦
|
||||
|
||||

|
||||

|
||||
|
||||
# 我感觉讲义写的不够细
|
||||
## 我感觉讲义写的不够细
|
||||
|
||||
首先,我无法照顾到每一个人的情况,保证你每一个地方都看懂
|
||||
|
||||
其次,很多地方的坑是故意留给你让你尝试独立解决问题的。
|
||||
|
||||
# 我觉得我以后不会从事 C 相关的工作
|
||||
## 我觉得我以后不会从事 C 相关的工作
|
||||
|
||||
这种"只要不影响我现在 survive, 就不要紧"的想法其实非常的利己和短视: 你在专业上的技不如人, 迟早有一天会找上来, 会影响到你个人职业生涯的长远的发展
|
||||
这种"只要不影响我现在 survive, 就不要紧"的想法其实非常的利己和短视:你在专业上的技不如人,迟早有一天会找上来,会影响到你个人职业生涯的长远的发展
|
||||
|
||||
更严重的是,他可能会透支学校的信誉。
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
3. 能够理解其他语言:C 语言是很多编程语言的基础,如 C++、Java、Python 等语言都从 C 语言继承了很多特性。因此,学好 C 语言可以帮助你更好地理解其他编程语言的设计思路和工作原理。
|
||||
4. 开发底层软件:由于 C 语言具有高效、灵活、可移植等特点,因此它被广泛用于开发操作系统、嵌入式系统、网络协议、游戏引擎等底层软件。学习好 C 语言可以为你将来从事底层软件开发提供必要的基础知识。
|
||||
|
||||
# 我感觉我写了也不会学到啥
|
||||
## 我感觉我写了也不会学到啥
|
||||
|
||||
复杂的问题总是存在简单的解释,C 语言虽然不擅长带 GUI 界面的编写,但是我们每日在用的都和他息息相关,那些庞大的系统也无非就是由这些简单的东西搭建而成的
|
||||
|
||||
# 我觉得我没有学懂 C 语言就开始别的合适吗
|
||||
## 我觉得我没有学懂 C 语言就开始别的合适吗
|
||||
|
||||
学习本章内容更大程度上是为了让你搞清楚编程世界运行的基本原理
|
||||
|
||||
@@ -45,19 +45,19 @@ NJU-ICS-PA 南京大学计算机系统基础
|
||||
|
||||
但是建议大家大二再进行尝试,非常难
|
||||
|
||||
# 我总觉得文章没写清楚
|
||||
## 我总觉得文章没写清楚
|
||||
|
||||
你毕业后进入公司/课题组, 不会再有讲义具体地告诉你应该做什么, 总有一天你需要在脱离讲义的情况下完成任务. 我们希望你现在就放弃"讲义和框架代码会把我应该做的一切细节清楚地告诉我"的幻想, 为自己的成长负起责任:
|
||||
你毕业后进入公司/课题组, 不会再有讲义具体地告诉你应该做什么,总有一天你需要在脱离讲义的情况下完成任务。我们希望你现在就放弃"讲义和框架代码会把我应该做的一切细节清楚地告诉我"的幻想,为自己的成长负起责任:
|
||||
|
||||
- 不知道在说什么, 说明你对知识点的理解还不够清楚, 这时候你应该去看书/看手册
|
||||
- 不知道要做什么/怎么做, 说明你的系统观好是零碎的, 理解不了系统中各个模块之间的联系, 这时候你应该 RTFSC, 尽自己最大努力梳理并理解系统中的一切细节
|
||||
- bug 调不出来, 说明你不清楚程序正确的预期行为, 你需要 RTFSC 理解程序应该如何运行; 此外也说明你不重视工具和方法的使用, 你需要花时间去体验和总结它们
|
||||
- 不知道在说什么,说明你对知识点的理解还不够清楚,这时候你应该去看书/看手册
|
||||
- 不知道要做什么/怎么做, 说明你的系统观好是零碎的,理解不了系统中各个模块之间的联系,这时候你应该 RTFSC, 尽自己最大努力梳理并理解系统中的一切细节
|
||||
- bug 调不出来,说明你不清楚程序正确的预期行为,你需要 RTFSC 理解程序应该如何运行; 此外也说明你不重视工具和方法的使用,你需要花时间去体验和总结它们
|
||||
|
||||
如果你发现自己有以上情况, 你还是少抱怨, 多吃苦吧.
|
||||
如果你发现自己有以上情况,你还是少抱怨,多吃苦吧。
|
||||
|
||||
当然,如果你发现有更好的想法欢迎联系我
|
||||
|
||||
# 这些对我太简单了
|
||||
## 这些对我太简单了
|
||||
|
||||
你可以从广度和深度两个角度对自己进行拔高
|
||||
|
||||
@@ -65,8 +65,8 @@ NJU-ICS-PA 南京大学计算机系统基础
|
||||
|
||||
有且仅有大学有这样好的资源帮助你了
|
||||
|
||||
# <strong>坚持了好久还是搞不定, 我想放弃了</strong>
|
||||
## <strong>坚持了好久还是搞不定,我想放弃了</strong>
|
||||
|
||||

|
||||

|
||||
|
||||
也许是你坚持的姿势不对,来和 ZZM 聊聊吧
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
## Windows-Visual Studio
|
||||
|
||||
[vs2022(Visual Studio 2022)指南&&技巧要领](https://www.bilibili.com/video/BV1Xt411g7jT)
|
||||
[vs2022(Visual Studio 2022) 指南&&技巧要领](https://www.bilibili.com/video/BV1Xt411g7jT)
|
||||
|
||||
<Bilibili bvid='BV1Xt411g7jT'/>
|
||||
|
||||
Visual Studio (以下简称 VS )是 Windows 下最完美的 C/C++ 等语言的开发平台,有“宇宙第一 IDE”之称,功能丰富,开箱即用。目前更新到 2022 版。
|
||||
Visual Studio(以下简称 VS)是 Windows 下最完美的 C/C++ 等语言的开发平台,有“宇宙第一 IDE”之称,功能丰富,开箱即用。目前更新到 2022 版。
|
||||
|
||||
什么是 IDE,什么是代码编辑器,什么是编译器等等细碎问题参考文档 [3.1 该使用哪个编辑器???](3.1%E8%AF%A5%E4%BD%BF%E7%94%A8%E5%93%AA%E4%B8%AA%E7%BC%96%E8%BE%91%E5%99%A8%EF%BC%9F%EF%BC%9F%EF%BC%9F.md) 看不懂的话直接无脑装
|
||||
|
||||
@@ -18,15 +18,15 @@ Visual Studio (以下简称 VS )是 Windows 下最完美的 C/C++ 等语言
|
||||
|
||||
选择社区版
|
||||
|
||||

|
||||

|
||||
|
||||
社区版和专业版等的区别:社区版免费,功能上几乎无差别
|
||||
|
||||
### VS安装
|
||||
### VS 安装
|
||||
|
||||
选择 C++ 桌面开发,其他不用选,有需要了再说。另外,Python 开发不好使,不要像我一样选 Python 开发。
|
||||
|
||||

|
||||

|
||||
|
||||
安装完成后,一般来说 VS 不会自动创建桌面快捷方式,你需要到开始菜单中启动 VS。
|
||||
|
||||
@@ -40,19 +40,19 @@ VS 是项目制,你需要创建一个项目才能开始编写代码并运行
|
||||
|
||||
打开 VS,会打开如下界面(我使用深色主题),在此处单击“创建新项目”
|
||||
|
||||

|
||||

|
||||
|
||||
在创建新项目页面中选择项目模板为控制台应用(空项目亦可,后续手动添加.c 源文件),并单击下一步
|
||||
|
||||

|
||||

|
||||
|
||||
为你的项目起一个名字,以及选择项目的位置,一般默认即可,如果你有强迫症,C 盘一定不能放个人数据,请自行修改。完成后单击“创建”
|
||||
|
||||

|
||||

|
||||
|
||||
自此就创建了一个项目了,你将会到达如下界面:
|
||||
|
||||

|
||||

|
||||
|
||||
其中,左侧(如果在一开始没有选择 C++ 开发环境的话可能在右侧)为资源管理器,列出了本项目所用到的所有文件,包括代码(外部依赖项、源文件、头文件),以及将来开发图形化界面所需的资源文件;最中间占据面积最多的是代码编辑器窗口,你以后将会在这里编写你的 C 语言代码。最下面是输出窗口,源代码进行编译时,会在此处给出编译进度以及可能的代码中的错误。
|
||||
|
||||
@@ -68,7 +68,7 @@ C 语言是编译型语言,因此说“运行”代码其实并不是十分合
|
||||
|
||||
当你编写完自己的代码后,即可单击“本地 Windows 调试器”(或者使用快捷键 F5)进行“运行”。
|
||||
|
||||

|
||||

|
||||
|
||||
你可能会发现在“本地 Windows 调试器”右侧还有一个绿色三角形,并且单击这个也可以“运行”,这两个的区别在于“本地 Windows 调试器”是调试运行,右侧那个是不调试直接运行。
|
||||
|
||||
@@ -76,35 +76,35 @@ C 语言是编译型语言,因此说“运行”代码其实并不是十分合
|
||||
|
||||
如果你的代码被 VS 提示“This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.”
|
||||
|
||||

|
||||

|
||||
|
||||
需要你在项目-xxx 属性(xxx 是你的项目名)-C/C++-代码生成-安全检查里将安全检查禁用
|
||||
需要你在项目-xxx 属性(xxx 是你的项目名)-C/C++-代码生成 - 安全检查里将安全检查禁用
|
||||
|
||||

|
||||

|
||||
|
||||
### 调试
|
||||
|
||||
IDE 相比于代码编辑器,最强大的一点莫过于成熟的调试系统。通过调试,可以快速定位代码中没有被编译器检查出来的逻辑错误。如果需要调试,则可以在这个位置单击,打下断点,并且运行程序,程序运行时,就会在此处暂停下来,暂停时就可以查看各个变量的值了。
|
||||
|
||||

|
||||

|
||||
|
||||
### <strong>深色主题</strong>
|
||||
|
||||
需要深色主题请在工具-主题里更改为深色
|
||||
需要深色主题请在工具 - 主题里更改为深色
|
||||
|
||||
### Tips
|
||||
|
||||
#### 仔细查看报错
|
||||
|
||||

|
||||

|
||||
|
||||
如果程序代码中出现红色波浪线,则表示该处代码有“错误”,并且该处的错误会同步显示在下面的这个位置,单击即可看到错误详情。如果代码中出现绿色波浪线,则表示该处代码中有警告。警告和错误的区别是警告可以通过编译运行,但编译器认为你这里可能写错了;错误是完全不可以通过编译。
|
||||
|
||||

|
||||

|
||||
|
||||
#### 善用提示
|
||||
|
||||

|
||||

|
||||
|
||||
当你打一些函数名或者关键字时,VS 会给出你语法提示,如果这个提示正确,按下 Tab 键即可将这个提示补全到你的代码里;或者你也可以跟着这个提示打一遍,防止打错关键字。
|
||||
|
||||
@@ -114,16 +114,12 @@ IDE 相比于代码编辑器,最强大的一点莫过于成熟的调试系统
|
||||
|
||||
## Windows-Visual Studio Code
|
||||
|
||||
Visual Studio Code(以下简称 vscode) 和 Visual Studio 都是微软开发的软件,区别在于 Visual Studio Code 是一个比较轻量的代码编辑器,在没有经过配置的情况下一般只能编写和查看代码,而不能运行,并且 Visual Studio Code 跨平台,在安装了丰富的插件后体验不输于一众 IDE。
|
||||
|
||||
|
||||
Visual Studio Code(以下简称 vscode)和 Visual Studio 都是微软开发的软件,区别在于 Visual Studio Code 是一个比较轻量的代码编辑器,在没有经过配置的情况下一般只能编写和查看代码,而不能运行,并且 Visual Studio Code 跨平台,在安装了丰富的插件后体验不输于一众 IDE。
|
||||
|
||||
> NX 的留言:
|
||||
> 鄙人认为 C 的初学者应该使用 VSCode 更佳,环境准备可见鄙人博客 [『C/C++』VScode 环境配置](https://nickxu.me/2021/12/31/cc-vscode-huan-jing-pei-zhi/)
|
||||
|
||||
|
||||
### vscode安装
|
||||
|
||||
### vscode 安装
|
||||
|
||||
#### 安装软件本体
|
||||
|
||||
@@ -139,13 +135,13 @@ Visual Studio Code(以下简称 vscode) 和 Visual Studio 都是微软开发
|
||||
|
||||
vscode 的项目和 VS 不同,vscode 的项目比较松散,并没有 VS 那样是一套非常完善的项目系统。
|
||||
|
||||
首先需要一个空文件夹,并在 vscode 里打开这个文件夹。然后点击文件-新建文本文件,并选择语言为 C 语言。此时如果你是第一次创建 C 语言文件,那么右下角会弹出提示,提示你安装 C/C++ 插件,安装即可。
|
||||
首先需要一个空文件夹,并在 vscode 里打开这个文件夹。然后点击文件 - 新建文本文件,并选择语言为 C 语言。此时如果你是第一次创建 C 语言文件,那么右下角会弹出提示,提示你安装 C/C++ 插件,安装即可。
|
||||
|
||||
### 编写代码并运行
|
||||
|
||||
编写完代码后,保存文件,并点击运行-启动调试
|
||||
编写完代码后,保存文件,并点击运行 - 启动调试
|
||||
|
||||

|
||||

|
||||
|
||||
此时会弹出如下选择框,我的电脑上同时安装有 VS 和 gcc 编译器,因此有两个,大部分的电脑上应该只有一个“C++ (Windows)”,选择你电脑上的编译器并运行即可。
|
||||
|
||||
@@ -163,12 +159,9 @@ CLion 是 jetbrains 家族的 C 语言 IDE
|
||||
|
||||
用法和 Windows 的差不多,但由于 Mac OS 自带 clang 编译器,所以无需额外安装编译器。
|
||||
|
||||
|
||||
|
||||
> NX 的留言:
|
||||
> 使用自带的 clang 的确没问题,但是如果你想在 macOS 上使用 gcc/g++ ,[可参考鄙人的博客 在 Mac 的 VSC 中使用 g++ 编译器](https://nickxu.me/2023/04/04/%E5%9C%A8Mac%E7%9A%84VSCode%E4%B8%AD%E4%BD%BF%E7%94%A8g-%E7%BC%96%E8%AF%91%E5%99%A8)
|
||||
|
||||
|
||||
## Mac OS-CLion
|
||||
|
||||
同样和 Windows 的差不多。
|
||||
@@ -177,27 +170,26 @@ CLion 是 jetbrains 家族的 C 语言 IDE
|
||||
|
||||
XCode 是 mac 官方的 IDE,能编写所有 mac 家族设备的软件。但缺点是没有中文。
|
||||
|
||||

|
||||

|
||||
|
||||
打开以后选择 Create a new Xcode project,选择 macOS-Command Line Tool
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
两个空里第一个填项目名,第二个随便填就行
|
||||
|
||||
next 后选择项目保存的位置,之后即可到达以下界面:
|
||||
|
||||

|
||||

|
||||
|
||||
点左上方小三角即可运行
|
||||
|
||||
在行号上点击并运行即可调试
|
||||
|
||||

|
||||

|
||||
|
||||
## Linux
|
||||
|
||||
### 你都用 Linux 了你还来问我?一边玩去
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- 本篇不需要任何前置知识,推荐在学习 C 语言和学完 C 语言后各看一遍。
|
||||
- 我们鼓励你在解决问题的时候进行思考,锻炼解决问题的能力,而不只是成为一个做代码翻译工作的“码农”。
|
||||
|
||||

|
||||

|
||||
|
||||
解决编程问题的常见误区:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
如果你计划得足够好并且代码编写得正确,你的代码将在第一次工作。即便它第一次不起作用,那么你至少有一个对于代码如何调试的可靠计划。
|
||||
|
||||

|
||||

|
||||
|
||||
## Work an Example Yourself
|
||||
|
||||
@@ -65,4 +65,3 @@
|
||||
## Debug Program
|
||||
|
||||
一旦在代码中发现了问题,就需要修复它,这个过程称为调试。许多新手程序员(甚至一些经验丰富的程序员)以临时方式调试,试图更改代码中的某些内容,并希望它能解决他们的问题。这样的方法很少有效,常常会导致很多挫折。
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
以下方式难度由易到难,但并不意味着收获由小到大:
|
||||
|
||||
1.B 站翁恺的 C 语言课程(非常基础,缺点是只看视频学的过浅)
|
||||
1.Video:[B 站翁恺的 C 语言课程](https://www.bilibili.com/video/BV1dr4y1n7vA)(非常基础,缺点是只看视频学的过浅)
|
||||
|
||||
<Bilibili bvid='BV1dr4y1n7vA'/>
|
||||
|
||||
2.MOOC:[翁凯 C 课程的 MOOC 慕课](https://www.icourse163.org/course/ZJU-9001)(同上,慕课的习题和 Projects 性价比不高,几乎没有差别)
|
||||
|
||||
@@ -18,7 +20,7 @@
|
||||
|
||||
7.Web:[LinuxC 一站式编程](https://akaedu.github.io/book/)(难度大,枯燥硬核,收获多,基于 linux)
|
||||
|
||||
### 学习建议:可以选择其一或多种学习。
|
||||
## 学习建议:可以选择其一或多种学习
|
||||
|
||||
- 对于缺乏计算机基础(这里的基础指的是计算机的日常使用)的同学,(1、2)是不错的选择,但在学完后要选择 4、5、6 进行补充巩固提高。
|
||||
- 对于有一定计算机基础的同学,直接上手 4、5、6 都是很不错的选择。
|
||||
@@ -34,6 +36,6 @@
|
||||
|
||||
计算机思维与计算机科学与编码能力
|
||||
|
||||

|
||||

|
||||
|
||||
### <strong>CS education is more than just “learning how to code”!</strong>
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。例如,使用链表存储 {1,2,3},各个元素在内存中的存储状态可能是:
|
||||
|
||||

|
||||

|
||||
|
||||
可以看到,数据不仅没有集中存放,在内存中的存储次序也是混乱的。那么,链表是如何存储数据间逻辑关系的呢?
|
||||
|
||||
链表存储数据间逻辑关系的实现方案是:为每一个元素配置一个指针,每个元素的指针都指向自己的直接后继元素,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
显然,我们只需要记住元素 1 的存储位置,通过它的指针就可以找到元素 2,通过元素 2 的指针就可以找到元素 3,以此类推,各个元素的先后次序一目了然。像图 2 这样,数据元素随机存储在内存中,通过指针维系数据之间“一对一”的逻辑关系,这样的存储结构就是链表。
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
|
||||
在链表中,每个数据元素都配有一个指针,这意味着,链表上的每个“元素”都长下图这个样子:
|
||||
|
||||

|
||||

|
||||
|
||||
数据域用来存储元素的值,指针域用来存放指针。数据结构中,通常将这样的整体称为结点。
|
||||
|
||||
也就是说,链表中实际存放的是一个一个的结点,数据元素存放在各个结点的数据域中。举个简单的例子,图 3 中 {1,2,3} 的存储状态用链表表示,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
在 C 语言中,可以用结构体表示链表中的结点,例如:
|
||||
|
||||
@@ -42,13 +42,13 @@ typedef struct Node* Link;
|
||||
|
||||
图 4 所示的链表并不完整,一个完整的链表应该由以下几部分构成:
|
||||
|
||||
头指针:是指向链表中一个结点所在存储位置的指针。如果链表中有头结点,则头指针指向头结点;若链表中没有头结点,则头指针指向链表中第一个数据结点(也叫首元结点)。
|
||||
头指针:是指向链表中一个结点所在存储位置的指针。如果链表中有头结点,则头指针指向头结点;若链表中没有头结点,则头指针指向链表中第一个数据结点(也叫首元结点)。
|
||||
|
||||
链表有头指针,当我们需要使用链表中的数据时,我们可以使用遍历查找等方法,从头指针指向的结点开始,依次搜索,直到找到需要的数据;反之,若没有头指针,则链表中的数据根本无法使用,也就失去了存储数据的意义。
|
||||
|
||||
结点:链表中的节点又细分为头结点、首元结点和其它结点:
|
||||
|
||||
头结点:位于链表的表头,即链表中第一个结点,其一般不存储任何数据,特殊情况可存储表示链表信息(表的长度等)的数据。
|
||||
头结点:位于链表的表头,即链表中第一个结点,其一般不存储任何数据,特殊情况可存储表示链表信息(表的长度等)的数据。
|
||||
|
||||
头结点的存在,其本身没有任何作用,就是一个空结点,但是在对链表的某些操作中,链表有无头结点,可以直接影响编程实现的难易程度。
|
||||
|
||||
@@ -66,7 +66,7 @@ typedef struct Node* Link;
|
||||
|
||||
例如,创建一个包含头结点的链表存储 {1,2,3},如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
## 链表的创建
|
||||
|
||||
@@ -95,7 +95,7 @@ Link* head = (Link*)malloc(sizeof(Link)); //创建头指针
|
||||
|
||||
```c
|
||||
Link p;
|
||||
while (Judgement) //for同理
|
||||
while (Judgement) //for 同理
|
||||
{
|
||||
p = (Link)malloc(sizeof(Node));
|
||||
p->elem = element;
|
||||
@@ -104,14 +104,14 @@ while (Judgement)
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
### 创建结点——尾插法
|
||||
|
||||
```c
|
||||
Link p;
|
||||
Link r = (*head); //临时中间结构指针,在尾插法中始终指向最后一个结点
|
||||
while (Judgement) //for同理
|
||||
while (Judgement) //for 同理
|
||||
{
|
||||
p = (Link)malloc(sizeof(Node));
|
||||
p->elem = element;
|
||||
@@ -121,7 +121,7 @@ while (Judgement) //for同理
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
## 链表的基本操作
|
||||
|
||||
@@ -141,20 +141,20 @@ while (Judgement) //for同理
|
||||
```c
|
||||
#define error 0
|
||||
#define ok 1
|
||||
/*用e返回L中第i个数据元素的值*/
|
||||
/*用 e 返回 L 中第 i 个数据元素的值*/
|
||||
int GetElem(Link *L, int i; int *e)
|
||||
{
|
||||
Link p;
|
||||
p = (*L)->next; //p指向第一个结点
|
||||
p = (*L)->next; //p 指向第一个结点
|
||||
int j = 1;
|
||||
while (p && j < i) //p不为空或者计数器j还没有等于i时,循环继续
|
||||
while (p && j < i) //p 不为空或者计数器 j 还没有等于 i 时,循环继续
|
||||
{
|
||||
p = p->next; //p指向下一个结点
|
||||
p = p->next; //p 指向下一个结点
|
||||
j++;
|
||||
}
|
||||
if (!p) //第i个元素不存在
|
||||
if (!p) //第 i 个元素不存在
|
||||
return error;
|
||||
*e = p->elem; //取第i个元素的数据
|
||||
*e = p->elem; //取第 i 个元素的数据
|
||||
return ok;
|
||||
}
|
||||
```
|
||||
@@ -176,19 +176,19 @@ int GetElem(Link *L, int i; int *e)
|
||||
|
||||
例如,在链表 `{1,2,3,4}` 的基础上分别实现在头部、中间、尾部插入新元素 5,其实现过程如图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
从图中可以看出,虽然新元素的插入位置不同,但实现插入操作的方法是一致的,都是先执行步骤 1 ,再执行步骤 2。实现代码如下:
|
||||
从图中可以看出,虽然新元素的插入位置不同,但实现插入操作的方法是一致的,都是先执行步骤 1,再执行步骤 2。实现代码如下:
|
||||
|
||||
```c
|
||||
/*在L中第i个位置(注意链表中的位置不一定为结点的个数)之前插入新的数据元素e,
|
||||
L的长度加一(可以用头结点存储链表长度)*/
|
||||
/*在 L 中第 i 个位置(注意链表中的位置不一定为结点的个数)之前插入新的数据元素 e,
|
||||
L 的长度加一(可以用头结点存储链表长度)*/
|
||||
int ListInsert(Link *L, int i, int e)
|
||||
{
|
||||
Link p, r; //r为临时中间结构指针,用于实现插入
|
||||
p = *L; //p指向头结点
|
||||
Link p, r; //r 为临时中间结构指针,用于实现插入
|
||||
p = *L; //p 指向头结点
|
||||
int j = 1;
|
||||
while (p && j < i) //寻找第i个结点,
|
||||
while (p && j < i) //寻找第 i 个结点,
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
@@ -207,7 +207,7 @@ int ListInsert(Link *L, int i, int e)
|
||||
|
||||
对于没有头结点的链表,在头部插入结点比较特殊,需要单独实现。
|
||||
|
||||

|
||||

|
||||
|
||||
和 2)、3) 种情况相比,由于链表没有头结点,在头部插入新结点,此结点之前没有任何结点,实现的步骤如下:
|
||||
|
||||
@@ -217,8 +217,8 @@ int ListInsert(Link *L, int i, int e)
|
||||
实现代码如下:
|
||||
|
||||
```c
|
||||
/*在L中第i个位置(注意链表中的位置不一定为结点的个数)之前插入新的数据元素e,
|
||||
L的长度加一(可以用头结点存储链表长度)*/
|
||||
/*在 L 中第 i 个位置(注意链表中的位置不一定为结点的个数)之前插入新的数据元素 e,
|
||||
L 的长度加一(可以用头结点存储链表长度)*/
|
||||
int ListInsert(Link *L, int i, int e)
|
||||
{
|
||||
if (i == 1)
|
||||
@@ -253,12 +253,12 @@ temp->next=temp->next->next;
|
||||
|
||||
例如,从存有 `{1,2,3,4}` 的链表中删除存储元素 3 的结点,则此代码的执行效果如图 3 所示:
|
||||
|
||||

|
||||

|
||||
|
||||
实现代码如下:
|
||||
|
||||
```c
|
||||
/*删除L中的第i个数据元素,并用e返回其值,L的长度减一
|
||||
/*删除 L 中的第 i 个数据元素,并用 e 返回其值,L 的长度减一
|
||||
(可以用头结点存储链表长度)*/
|
||||
int ListDelete(Link *L, int i, int* e)
|
||||
{
|
||||
@@ -271,7 +271,7 @@ int ListDelete(Link *L, int i, int* e)
|
||||
j++;
|
||||
}
|
||||
if (!(p->next))
|
||||
return error; //L中不存在第i个元素
|
||||
return error; //L 中不存在第 i 个元素
|
||||
r = p->next; //标记要删除的结点
|
||||
p->next = r->next; //移除结点
|
||||
*e = r->elem; //返回结点所存数据
|
||||
@@ -282,12 +282,12 @@ int ListDelete(Link *L, int i, int* e)
|
||||
|
||||
对于不带头结点的链表,需要单独考虑删除首元结点的情况,删除其它结点的方式和图 3 完全相同,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
实现代码如下:
|
||||
|
||||
```c
|
||||
/*删除L中的第i个数据元素,并用e返回其值,L的长度减一
|
||||
/*删除 L 中的第 i 个数据元素,并用 e 返回其值,L 的长度减一
|
||||
(可以用头结点存储链表长度)*/
|
||||
int ListDelete(Link *L, int i, int* e)
|
||||
{
|
||||
@@ -319,7 +319,7 @@ int ListDelete(Link *L, int i, int* e)
|
||||
|
||||
如图所示,假设此时圆周周围有 5 个人,要求从编号为 3 的人开始顺时针数数,数到 2 的那个人出列:
|
||||
|
||||

|
||||

|
||||
|
||||
出列顺序依次为:
|
||||
|
||||
@@ -339,10 +339,10 @@ int ListDelete(Link *L, int i, int* e)
|
||||
|
||||
为了使空链表和非空链表处理一致,我们通常设一个头结点,当然,并不是说,循环链表一定要头结点,这需要注意。循环链表带有头结点的空链表如图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
对于非空的循环链表如图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断 p->next 是否为空,现在则是 p->next 不等于头结点,则循环未结束。
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
# 阶段一:编程属性
|
||||
|
||||
# [C 语言任务模块](https://github.com/E1PsyCongroo/HDU_C_Assignments/)
|
||||
## [C 语言任务模块](https://github.com/E1PsyCongroo/HDU_C_Assignments/)
|
||||
|
||||
作为一名合格的大学生,更应深谙“纸上得来终觉浅,绝知此事要躬行”的道理,编程语言就像是一个工具,无论你如何熟读说明书(语法、特性),未经实践终究是靠不住的。
|
||||
|
||||
本模块将以有趣的任务的形式替你检测是否你已经达到了基本掌握C语言语法和一些特性的目的
|
||||
本模块将以有趣的任务的形式替你检测是否你已经达到了基本掌握 C 语言语法和一些特性的目的
|
||||
|
||||
- 该任务模块旨在帮助巩固 C 语言基础知识,传递一些编程思维,入门学习请看 [3.4.4C 语言前置概念学习](3.4.4C%E8%AF%AD%E8%A8%80%E5%89%8D%E7%BD%AE%E6%A6%82%E5%BF%B5%E5%AD%A6%E4%B9%A0.md)
|
||||
- 你可以通过使用 git `git clone ``https://github.com/E1PsyCongroo/HDU_C_Assignments.git` 获取任务
|
||||
- 或者访问https://github.com/E1PsyCongroo/HDU_C_Assignments 学习
|
||||
- 你可以通过使用 git 工具 `git clone https://github.com/E1PsyCongroo/HDU_C_Assignments.git` 获取任务
|
||||
- 或者访问 [https://github.com/E1PsyCongroo/HDU_C_Assignments](https://github.com/E1PsyCongroo/HDU_C_Assignments) 学习
|
||||
|
||||
# 任务一做前必查
|
||||
## 任务一做前必查
|
||||
|
||||
1. 理解[3.4.3解决编程问题的普适性过程](3.4.3%E8%A7%A3%E5%86%B3%E7%BC%96%E7%A8%8B%E9%97%AE%E9%A2%98%E7%9A%84%E6%99%AE%E9%80%82%E6%80%A7%E8%BF%87%E7%A8%8B.md) 。
|
||||
1. 理解[3.4.3 解决编程问题的普适性过程](3.4.3%E8%A7%A3%E5%86%B3%E7%BC%96%E7%A8%8B%E9%97%AE%E9%A2%98%E7%9A%84%E6%99%AE%E9%80%82%E6%80%A7%E8%BF%87%E7%A8%8B.md) 。
|
||||
2. 理解 C 语言语法基础:变量、表达式、函数、判断、循环、常用标准库函数。
|
||||
3. 理解 C 语言中的一切都是数字。
|
||||
4. 初步理解 C 语言各类数据类型:基本数据类型和复杂自定义数据类型。
|
||||
5. 初步理解 C 语言数组及字符串。
|
||||
|
||||
# 任务二做前必查
|
||||
## 任务二做前必查
|
||||
|
||||
1. 深入理解 C 语言指针、数组和字符串。
|
||||
2. 理解递归思想。
|
||||
3. 理解复杂自定义数据类型。
|
||||
|
||||
### 请阅读各个任务的 README.md,了解完成任务所需的前置知识。
|
||||
## 请阅读各个任务的 README.md,了解完成任务所需的前置知识
|
||||
|
||||
进阶:评价一个程序,大体分为以下四个层次。
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Bye!
|
||||
|
||||
尽管可能微不足道,但该程序确实展示 <em>了</em>任何文本冒险中最重要的方面:描述性文本。一个好的故事是制作一款好的冒险游戏的要素之一。
|
||||
|
||||
# 为什么要用英文?
|
||||
## 为什么要用英文?
|
||||
|
||||
因为中文的编码模式可能会带来奇怪的影响。
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
|
||||
假设我们的洞口被警卫挡住了。玩家就过不去,我们可以简单地将通道的<em>目的地</em>更改为终点位置(或 <em>NULL</em>),但这会导致对<em>诸如 go cave 和 look cave</em> 这样的命令做出不正确的回应:“你在这里看不到任何洞穴。我们需要一个将通道的实际终点和虚假终点分开的单独属性。为此,我们将引入一个属性 prospect 来表示后者。
|
||||
|
||||
1. 在许多冒险中,玩家以及游戏中的 NPC 在携带量方面受到限制。给每件物品一个重量,角色库存中所有物品的总重量不应超过该角色所能承载的最大重量。当然,我们也可以给一个物体一个非常高的重量,使它不可移动(一棵树,一座房子,一座山)。
|
||||
2. RPG 式的冒险游戏需要角色的整个属性范围( 玩家与非玩家 ),例如 HP。HP 为零的对象要么死了,要么根本不是角色。
|
||||
1. 在许多冒险中,玩家以及游戏中的 NPC 在携带量方面受到限制。给每件物品一个重量,角色库存中所有物品的总重量不应超过该角色所能承载的最大重量。当然,我们也可以给一个物体一个非常高的重量,使它不可移动(一棵树,一座房子,一座山)。
|
||||
2. RPG 式的冒险游戏需要角色的整个属性范围 ( 玩家与非玩家 ),例如 HP。HP 为零的对象要么死了,要么根本不是角色。
|
||||
|
||||
我们在 object.txt 中定义了七个新属性:
|
||||
|
||||
```
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
|
||||
@@ -115,13 +115,14 @@ extern OBJECT objs[];
|
||||
textGo "Solid rock is blocking the way."
|
||||
```
|
||||
|
||||
注意:textGo 不仅对通道对象有用,而且对非通道对象也有用( 在这种情况下,以后我们将介绍“墙”这个概念)
|
||||
注意:textGo 不仅对通道对象有用,而且对非通道对象也有用 ( 在这种情况下,以后我们将介绍“墙”这个概念)
|
||||
|
||||
思考题:你能否自行实现上述伪代码?
|
||||
::: warning 🤔 思考题:你能否自行实现上述伪代码?
|
||||
:::
|
||||
|
||||
现在,我们已经可以使用新属性(如果你完成了上面的思考题),<strong>details</strong> 用于新识别的命令<em>外观`<object>`</em>,<strong>textGo</strong> 在我们的命令 <em>go</em> 实现中替换固定文本<em>“OK</em>”。
|
||||
现在,我们已经可以使用新属性 (如果你完成了上面的思考题),<strong>details</strong> 用于新识别的命令<em>外观`<object>`</em>,<strong>textGo</strong> 在我们的命令 <em>go</em> 实现中替换固定文本<em>“OK</em>”。
|
||||
|
||||
# location.c
|
||||
## location.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -195,7 +196,7 @@ void executeGo(const char *noun)
|
||||
|
||||
属性权重和容量一起成为不能将某些对象移动到周围的可能原因。而 HP 检查代替了角色的硬编码白名单。
|
||||
|
||||
# move.c
|
||||
## move.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -264,7 +265,7 @@ void moveObject(OBJECT *obj, OBJECT *to)
|
||||
|
||||
这里还有一个模块可以使用 HP 来识别角色。
|
||||
|
||||
# inventory.c
|
||||
## inventory.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -327,7 +328,8 @@ void executeInventory(void)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:仔细观察这段代码,看看与你写的有何不同?
|
||||
::: warning 🤔 思考题:仔细观察这段代码,看看与你写的有何不同?
|
||||
:::
|
||||
|
||||
权重检查利用了新功能 <em>weightOfContents,</em>它将在<em>misc.c</em>中实现。在同一模块中,我们还对一些现有函数进行了修改,以支持最后几个属性。
|
||||
|
||||
@@ -335,7 +337,7 @@ void executeInventory(void)
|
||||
|
||||
在函数 <em>getPassage</em> 中我们将属性<em>目标</em>替换为 prospect,并改进<em>对所有</em>命令(而不仅仅是 <em>go</em> and <em>look</em>)的响应,这些命令应用于位于“隐藏通道”另一端的位置。
|
||||
|
||||
# misc.h
|
||||
## misc.h
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
@@ -357,7 +359,7 @@ extern OBJECT *actorHere(void);
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -432,15 +434,15 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:为什么上面的 getPassage 函数使用了函数指针这种语法?
|
||||
::: warning 🤔 思考题:
|
||||
为什么上面的 getPassage 函数使用了函数指针这种语法?
|
||||
|
||||
```
|
||||
函数指针和指针函数有什么区别?
|
||||
```
|
||||
函数指针和指针函数有什么区别?
|
||||
:::
|
||||
|
||||
为了使整个画面完整,最好扩展前面生成的地图,我们可以用虚线表示“明显”的通道。
|
||||
|
||||
```c
|
||||
```awk
|
||||
BEGIN { print "digraph map {"; }
|
||||
/^- / { outputEdges(); delete a; }
|
||||
/^[ \t]/ { a[$1] = $2; }
|
||||
@@ -462,7 +464,7 @@ function outputEdge(from, to, style)
|
||||
|
||||
- 尽量不要太担心浪费仅在某些类型的对象中使用的属性上的内存空间(例如,<em>textGo</em>仅用于通道),或者许多重复的字符串文本。
|
||||
- 为了演示属性 prospect 的使用,我们使洞穴无法访问。当您查看新<em>地图时,</em>这一点立即变得很明显。进入洞穴的箭头是虚线的,这意味着这是一个虚假的通道,但不是实际的通道。请放心,洞穴将在下一章重新开放。
|
||||
- 请注意,更详细的描述往往需要一个更大的字典(更多的对象,更多的标签)。例如,命令 look silver coin 现在返回 "该硬币的正面有一只鹰"。玩家通过输入一个命令 look eagle 来查看银币,但程序并不知道鹰是什么意思(显然这样子是不行的)。
|
||||
- 请注意,更详细的描述往往需要一个更大的字典(更多的对象,更多的标签)。例如,命令 look silver coin 现在返回 "该硬币的正面有一只鹰"。玩家通过输入一个命令 look eagle 来查看银币,但程序并不知道鹰是什么意思 (显然这样子是不行的)。
|
||||
|
||||
输出样例
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
让我们举一个简单的例子。为了越过守卫进入山洞,玩家必须杀死或贿赂守卫(或两者兼而有之,这很有价值)。换句话说:
|
||||
|
||||
- 当警卫死亡时(HP=0),入口开放
|
||||
- 当警卫拿着银币(贿赂警卫)时,入口开放
|
||||
- 当警卫拿着银币 (贿赂警卫) 时,入口开放
|
||||
- 两者都不是,入口关闭
|
||||
|
||||
打开一个封闭的通道(在这里是进入洞穴)涉及到改变一些属性值:
|
||||
|
||||
- 目的地从 NULL(空地点)变为洞穴
|
||||
- 目的地从 NULL(空地点) 变为洞穴
|
||||
- <strong>textGo</strong>从 "警卫阻止你...... "改为 "你走进山洞"
|
||||
- 在一些特殊情况下,描述和细节不需要改变。但对于一个门洞或栅栏,其中之一(或两者)通常会包含一些从 "开放 "到 "关闭 "的文字。
|
||||
|
||||
@@ -36,7 +36,8 @@ bool intoCaveIsClosed(void)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:你能仿照上面例子自己写一些条件函数吗?
|
||||
::: warning 🤔 思考题:你能仿照上面例子自己写一些条件函数吗?
|
||||
:::
|
||||
|
||||
新的属性条件是一个指向这样一个函数的指针。
|
||||
|
||||
@@ -46,9 +47,9 @@ bool (*condition)(void);
|
||||
|
||||
接下来,我们可以立即开始为 object.txt 中的新属性分配函数。
|
||||
|
||||
# object.txt
|
||||
## object.txt
|
||||
|
||||
```
|
||||
```txt
|
||||
- intoCave
|
||||
condition intoCaveIsOpen
|
||||
description "a cave entrance to the east"
|
||||
@@ -68,11 +69,12 @@ bool (*condition)(void);
|
||||
textGo "The guard stops you from walking into the cave.\n"
|
||||
```
|
||||
|
||||
思考题:尝试自己实现上面的伪代码
|
||||
::: warning 🤔 思考题:尝试自己实现上面的伪代码
|
||||
:::
|
||||
|
||||
这两个 "条件 "函数是如此具体,每一个条件函数都只用这一次。现在,我们可以在我们需要的地方定义这些函数。许多编程语言都支持匿名函数,像这样:
|
||||
|
||||
```
|
||||
```txt
|
||||
- intoCave
|
||||
condition { return guard->health == 0 || silver->location == guard; }
|
||||
...
|
||||
@@ -84,9 +86,9 @@ bool (*condition)(void);
|
||||
|
||||
所以现在我们可以把额外的段落和条件添加到 object.txt 中,就像前面解释的那样。
|
||||
|
||||
# object.txt
|
||||
## new object.txt
|
||||
|
||||
```
|
||||
```txt
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
@@ -193,11 +195,12 @@ extern OBJECT objs[];
|
||||
textGo "Solid rock is blocking the way."
|
||||
```
|
||||
|
||||
思考题:尝试自己实现这些功能,并看看与你之前设计的有何不同
|
||||
::: warning 🤔 思考题:尝试自己实现这些功能,并看看与你之前设计的有何不同
|
||||
:::
|
||||
|
||||
为了使这些条件发挥作用,我们需要调整函数 isHolding 和 getDistance。
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -273,7 +276,8 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:想想我们调整了什么
|
||||
::: warning 🤔 思考题:想想我们调整了什么
|
||||
:::
|
||||
|
||||
注意:
|
||||
|
||||
@@ -282,7 +286,8 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
3. 为了简单起见,条件函数没有参数。实际上,传递一个参数 OBJECT *obj 可能更好;这使得编写更多的通用条件函数成为可能,可以在多个对象中重复使用。
|
||||
4. 在理论上,任何对象都可以成为 "条件"。在下一章,你可以看到一个类似的技术被应用于此。
|
||||
|
||||
思考题:想一想上面第二点要怎么用 C 来实现?
|
||||
::: warning 🤔 思考题:想一想上面第二点要怎么用 C 来实现?
|
||||
:::
|
||||
|
||||
输出样例
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 12.开启关闭
|
||||
|
||||
在上一章中,我们使用 "条件 "函数来使对象消失。当然,还有一个更简单的方法来实现同样的目的:只要清除对象的位置属性就可以了!
|
||||
在上一章中,我们使用 "条件 "函数来使对象消失。当然,还有一个更简单的方法来实现同样的目的:只要清除对象的位置属性就可以了!
|
||||
|
||||
洞口是一个典型的例子,条件函数在那里工作得特别好。这是因为入口受到其他对象(守卫和银币)中的属性的影响;我们可以使用函数使得所有的逻辑都能保持一致。
|
||||
|
||||
让我们举一个更直接的例子。假设山洞有一扇门通向一个密室。只是一个简单的门洞,玩家可以打开和关闭。就像前一章一样,我们将使用两个对象来表示这个通道;一个表示打开的门,另一个表示门关闭时。
|
||||
|
||||
```
|
||||
```txt
|
||||
- backroom
|
||||
description "a backroom"
|
||||
tags "backroom"
|
||||
@@ -28,11 +28,12 @@
|
||||
textGo "The door is closed.\n"
|
||||
```
|
||||
|
||||
思考题:尝试自己用 C 语言实现
|
||||
::: warning 🤔 思考题:尝试自己用 C 语言实现
|
||||
:::
|
||||
|
||||
自然,门也应该能从另一侧进入。
|
||||
|
||||
```
|
||||
```txt
|
||||
- openDoorToCave
|
||||
description "an open door to the north"
|
||||
tags "north", "door", "doorway"
|
||||
@@ -111,7 +112,8 @@ else
|
||||
}
|
||||
```
|
||||
|
||||
思考题:你能不能仿照上面的代码实现 close 功能?
|
||||
::: warning 🤔 思考题:你能不能仿照上面的代码实现 close 功能?
|
||||
:::
|
||||
|
||||
为了使事情稍微复杂一些,我们可以在门上或盒子上加一把锁。这需要(至少)三个相互排斥的对象;每个可能的状态都有一个:打开、关闭和锁定。但是我们仍然可以使用同一个函数来交换对象的位置。例如,这里是如何解锁一个上锁的盒子;反之亦然。
|
||||
|
||||
@@ -132,15 +134,17 @@ else if (obj == lockedBox)
|
||||
|
||||
显然,代码的行数与游戏中的门(以及盒子和其他可以打开的物体)的数量成正比。因此,如果你的游戏有不止几扇门,那么选择一个更通用的解决方案是个好主意。顺便说一下,这对每一个命令都是适用的:当它涉及到许多物体时,尽量写通用代码;但当你处理一两个特殊情况时,就坚持使用直接的、专门的代码。
|
||||
|
||||
思考题:我们可以使用什么方法来解决这个问题?
|
||||
::: warning 🤔 思考题:
|
||||
我们可以使用什么方法来解决这个问题?
|
||||
|
||||
提示:C++ 中的模板功能(这只是一种选择)
|
||||
|
||||
下面我们将揭晓答案
|
||||
:::
|
||||
|
||||
通用代码通常带有数据驱动的方法。换句话说,我们需要向我们的对象结构添加一个或多个属性。在这种特殊情况下,我们将为我们希望支持的每个命令添加一个函数指针:打开、关闭、锁定和解锁。
|
||||
|
||||
# object.txt
|
||||
## object.txt
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -354,7 +358,7 @@ extern OBJECT objs[];
|
||||
|
||||
为了避免重复的代码,我们这次特意没有使用匿名函数。相反,我们将在一个单独的模块中实现必要的逻辑。函数 swapLocations 也在其中,这不过是一个稍微扩展的版本,它也会向用户输出反馈。
|
||||
|
||||
# toggle.h
|
||||
## toggle.h
|
||||
|
||||
```c
|
||||
extern void cannotBeOpened(void);
|
||||
@@ -376,7 +380,7 @@ extern void toggleBox(void);
|
||||
extern void toggleBoxLock(void);
|
||||
```
|
||||
|
||||
# toggle.c
|
||||
## toggle.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -439,7 +443,7 @@ void toggleBoxLock(void)
|
||||
|
||||
正如前面所宣布的,打开、关闭、锁定和解锁这四个命令的实现是完全通用的。
|
||||
|
||||
# openclose.h
|
||||
## openclose.h
|
||||
|
||||
```c
|
||||
extern void executeOpen(const char *noun);
|
||||
@@ -448,7 +452,7 @@ extern void executeLock(const char *noun);
|
||||
extern void executeUnlock(const char *noun);
|
||||
```
|
||||
|
||||
# openclose.c
|
||||
## openclose.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -478,17 +482,18 @@ void executeUnlock(const char *noun)
|
||||
{
|
||||
OBJECT *obj = reachableObject("what you want to unlock", noun);
|
||||
if (obj != NULL) (*obj->unlock)();
|
||||
}
|
||||
```
|
||||
|
||||
上面,我们使用了一个通用函数 reachableObject 来处理不在这里的对象;其实现见下文。这样,我们就不必把同样的代码写四遍(每个执行函数写一遍)。更多的命令将在第 15 章中加入;这些命令将受益于同样的函数。
|
||||
|
||||
# reach.h
|
||||
## reach.h
|
||||
|
||||
```c
|
||||
extern OBJECT *reachableObject(const char *intention, const char *noun);
|
||||
```
|
||||
|
||||
# reach.c
|
||||
## reach.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -526,7 +531,7 @@ OBJECT *reachableObject(const char *intention, const char *noun)
|
||||
|
||||
同样,我们也要对 parsexec.c 进行补充
|
||||
|
||||
# parsexec.c
|
||||
## parsexec.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 13.编写解析器
|
||||
|
||||
每个文本冒险都有一个解析器,但是解析器也有高下之分。一个简单的 "动词-名词 "解析器(就像我们从第二章开始一直使用的那个)对于一个精心设计的冒险游戏来说可能已经足够了。
|
||||
每个文本冒险都有一个解析器,但是解析器也有高下之分。一个简单的 "动词 - 名词 "解析器(就像我们从第二章开始一直使用的那个)对于一个精心设计的冒险游戏来说可能已经足够了。
|
||||
|
||||
然而,Infocom 已经证明,一个更高级的解析器确实有助于制作一个令人愉快的游戏。它不一定要通过图灵测试。
|
||||
|
||||
@@ -19,7 +19,8 @@ char *noun = strtok(NULL, "\n");
|
||||
- 它确实接受多字对象(如银币),但字与字之间的空格必须准确无误。我们的游戏拒绝银币和硬币之间的双空格。
|
||||
- 它是区分大小写的;"向北走 "的命令因为大写的 "G "而不被识别。
|
||||
|
||||
思考题:你能想到有什么办法解决这些问题吗?
|
||||
::: warning 🤔 思考题:你能想到有什么办法解决这些问题吗?
|
||||
:::
|
||||
|
||||
编写一个好的分析器并不是一件小事,但在这里我将给你一个相对简单的方法,我们将定义一个由模式列表组成的语法,类似于(但比)正则表达式要简单得多。
|
||||
|
||||
@@ -30,7 +31,8 @@ char *noun = strtok(NULL, "\n");
|
||||
|
||||
为了解析用户的输入,我们将从上到下遍历模式列表,依次尝试将用户的输入与每个模式匹配。我们将在发现第一个匹配时停止。为了简单起见,我们将不使用回溯,尽管这可以在以后添加。
|
||||
|
||||
思考题:如果我们使用回溯,那该怎么编写代码?
|
||||
::: warning 🤔 思考题:如果我们使用回溯,那该怎么编写代码?
|
||||
:::
|
||||
|
||||
大写字母是我们语法中的非终端符号,它们可以匹配任何标签(任何对象)。当解析器在两个不同的标签(例如 "银币 "和 "银")之间进行选择时,较长的标签将被优先考虑。
|
||||
|
||||
@@ -40,7 +42,7 @@ char *noun = strtok(NULL, "\n");
|
||||
const char *params[26];
|
||||
```
|
||||
|
||||
该数组有 26 个元素;字母表中的每个(大写)字母都有一个,这足以满足一个模式中多达 26 个不同的非终端。对于一个(匹配的)模式中的每个非终端,将通过在非终端的数组元素中填充一个指向该特定标签的指针来 "捕获 "一个匹配的标签。 params[0](第一个数组元素)捕获非终端 "A",params[1]捕获 "B",以此类推。一个简单的宏定义可以用来找到属于某个非终端的数组元素。
|
||||
该数组有 26 个元素;字母表中的每个(大写)字母都有一个,这足以满足一个模式中多达 26 个不同的非终端。对于一个(匹配的)模式中的每个非终端,将通过在非终端的数组元素中填充一个指向该特定标签的指针来 "捕获 "一个匹配的标签。params[0](第一个数组元素)捕获非终端 "A",params[1]捕获 "B",以此类推。一个简单的宏定义可以用来找到属于某个非终端的数组元素。
|
||||
|
||||
```c
|
||||
#define paramByLetter(letter) (params + (letter) - 'A')
|
||||
@@ -52,7 +54,8 @@ const char *params[26];
|
||||
|
||||
假设用户打错了字,输入了 "go kave"。问题是,这个命令到底应不应该匹配 "go A "这个命令?如果我们不想出解决办法,那么这个命令将无法匹配任何其他命令,并最终进入一个通用的错误处理程序,它可能会回答 "我不知道如何去 kave "这样的话。这就失去了改进这些 "消极反应 "的所有机会;回答 "我不知道你要去哪里 "已经感觉更自然了。最好的办法是将所有关于命令去向的答复保持在函数 executeGo 内。
|
||||
|
||||
停下来想一想,可以怎么解决这个问题?
|
||||
::: warning 🤔 停下来想一想,可以怎么解决这个问题?
|
||||
:::
|
||||
|
||||
有几种方法可以实现这一点,但最简单的方法是允许非终止符匹配任何东西。所以不仅仅是一个有效的标签,也包括完全的胡言乱语、空白或什么都没有这种语句。这种 "无效 "的输入将被捕获为一个空字符串("")。
|
||||
|
||||
@@ -73,17 +76,18 @@ const char *params[26];
|
||||
|
||||
前两个命令形式可以为任何顺序,但第三个必须在最后。
|
||||
|
||||
思考题:你是否有办法解决这个问题?
|
||||
::: warning 🤔 思考题:你是否有办法解决这个问题?
|
||||
:::
|
||||
|
||||
是时候将其付诸行动了。我们将抛弃模块 parsexec.c 的现有内容,用一个新的函数 parseAndExecute 的实现来取代它,该函数使用一个模式列表,应该能够匹配我们到目前为止实现的每一条命令。每个模式都与一个执行相应命令的函数相联系。
|
||||
|
||||
# parsexec.c
|
||||
## parsexec.c
|
||||
|
||||
```c
|
||||
extern bool parseAndExecute(const char *input);
|
||||
```
|
||||
|
||||
# parsexec.h
|
||||
## parsexec.h
|
||||
|
||||
```c
|
||||
#include <ctype.h>
|
||||
@@ -149,7 +153,7 @@ bool parseAndExecute(const char *input)
|
||||
|
||||
最难的部分是函数 matchCommand 的实现。但正如你在下面看到的,这也可以在不到 100 行的代码中完成。
|
||||
|
||||
# match.h
|
||||
## match.h
|
||||
|
||||
```c
|
||||
#define MAX_PARAMS 26
|
||||
@@ -161,7 +165,7 @@ extern const char *params[];
|
||||
extern bool matchCommand(const char *src, const char *pattern);
|
||||
```
|
||||
|
||||
# match.c
|
||||
## match.c
|
||||
|
||||
```c
|
||||
#include <ctype.h>
|
||||
@@ -242,7 +246,7 @@ bool matchCommand(const char *src, const char *pattern)
|
||||
|
||||
我们调整各种命令的实现,以利用新的数组参数。
|
||||
|
||||
# <strong>inventory.h</strong>
|
||||
## <strong>inventory.h</strong>
|
||||
|
||||
```c
|
||||
extern bool executeGet(void);
|
||||
@@ -252,7 +256,7 @@ extern bool executeGive(void);
|
||||
extern bool executeInventory(void);
|
||||
```
|
||||
|
||||
# <strong>inventory.c</strong>
|
||||
## <strong>inventory.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -323,7 +327,7 @@ bool executeInventory(void)
|
||||
|
||||
我们在上一章中添加的模块也是如此。
|
||||
|
||||
# opemclose.h
|
||||
## opemclose.h
|
||||
|
||||
```c
|
||||
extern bool executeOpen(void);
|
||||
@@ -332,7 +336,7 @@ extern bool executeLock(void);
|
||||
extern bool executeUnlock(void);
|
||||
```
|
||||
|
||||
# openclose.c
|
||||
## openclose.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -372,7 +376,7 @@ bool executeUnlock(void)
|
||||
|
||||
在 location.c 中,look around 命令被赋予了自己的功能,与检查特定对象的 look 命令分开(见第 7-12 行)。
|
||||
|
||||
# location.h
|
||||
## location.h
|
||||
|
||||
```c
|
||||
extern bool executeLookAround(void);
|
||||
@@ -380,7 +384,7 @@ extern bool executeLook(void);
|
||||
extern bool executeGo(void);
|
||||
```
|
||||
|
||||
# location.c
|
||||
## location.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -453,7 +457,7 @@ bool executeGo(void)
|
||||
}
|
||||
```
|
||||
|
||||
我们的游戏仍然只接受简单的动词-名词命令,但新的解析器确实有可能接受有多个名词的命令,如 "put coin in box",这将在下一章中演示。
|
||||
我们的游戏仍然只接受简单的动词 - 名词命令,但新的解析器确实有可能接受有多个名词的命令,如 "put coin in box",这将在下一章中演示。
|
||||
|
||||
新的解析器比原来的解析器有了很大的改进,但以今天的标准来看,它仍然远远不够完美。例如,没有结构性的方法可以用一个命令操纵多个对象("获得硬币、钥匙和灯")或连续执行两个或多个命令("获得钥匙然后向东走")。这对于一个 RPG 游戏来说限制实在太大了!
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
是时候证明新的解析器确实有能力解释更复杂的命令了。
|
||||
|
||||
到目前为止,我们的解析器只接受简单的动词-名词命令。让我们试着解析一些更复杂的命令,比如:
|
||||
到目前为止,我们的解析器只接受简单的动词 - 名词命令。让我们试着解析一些更复杂的命令,比如:
|
||||
|
||||
- get coin from box
|
||||
- put coin in box
|
||||
@@ -16,7 +16,8 @@
|
||||
- ask A from B
|
||||
- give A to B
|
||||
|
||||
思考题:你能否自行实现这些命令
|
||||
::: warning 🤔 思考题:你能否自行实现这些命令
|
||||
:::
|
||||
|
||||
但是正如前一章所解释的,类似的命令(比如 "从 B 中获取 A "和已经存在的 "获取 A")必须以正确的顺序排列。如果'get A'首先出现,那么它将消耗任何以'get'开头的命令,包括所有应该被新模式匹配的命令。简而言之,"从 B 获取 A "必须出现在 "获取 A "之前。
|
||||
|
||||
@@ -32,13 +33,13 @@
|
||||
|
||||
目前,我们不需要有两个以上非终结点的模式。
|
||||
|
||||
# parsexec.h
|
||||
## parsexec.h
|
||||
|
||||
```c
|
||||
extern bool parseAndExecute(const char *input);
|
||||
```
|
||||
|
||||
# parsexec.c
|
||||
## parsexec.c
|
||||
|
||||
```c
|
||||
#include <ctype.h>
|
||||
@@ -106,9 +107,9 @@ bool parseAndExecute(const char *input)
|
||||
}
|
||||
```
|
||||
|
||||
在一个新的模块 inventory2.c 中,我们实现了新的多名词命令 get、drop/put、ask 和 give。现在我们终于可以把金币放回盒子里了(可喜可贺,可喜可贺)。
|
||||
在一个新的模块 inventory2.c 中,我们实现了新的多名词命令 get、drop/put、ask 和 give。现在我们终于可以把金币放回盒子里了 (可喜可贺,可喜可贺)。
|
||||
|
||||
# inventory2.h
|
||||
## inventory2.h
|
||||
|
||||
```c
|
||||
extern bool executeGetFrom(void);
|
||||
@@ -117,7 +118,7 @@ extern bool executeAskFrom(void);
|
||||
extern bool executeGiveTo(void);
|
||||
```
|
||||
|
||||
# inventory2.c
|
||||
## inventory2.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -210,11 +211,12 @@ bool executeGiveTo(void)
|
||||
|
||||
仔细观察上面的代码,你可能会注意到,像 "把硬币放进森林 "和 "把硬币放进盒子 "这样的命令(当盒子被关闭时)会得到以下奇怪的回答:"这太重了。" 这是因为大多数物体(包括封闭的盒子,以及像森林这样的场景)容纳其他物体的能力为零。这是正确的,但这个信息是很不恰当的。为了避免这种情况,我们将为那些容量为零的物体引入一个单独的信息:"这样做似乎不太适合。"
|
||||
|
||||
先想想有没有什么办法解决?
|
||||
::: warning 🤔 先想想有没有什么办法解决?
|
||||
:::
|
||||
|
||||
然而,当它作为对 "把钥匙放进盒子里 "的回应时,这个特殊的信息是完全误导的。所以我们将为这种特殊的对象组合做一个特殊的例外。
|
||||
|
||||
# move.c
|
||||
## move.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
在黑暗中的效果因游戏而异。通常情况下,它使命令 "look "没有效果。在一些游戏中(如 Zork),黑暗是致命的。在其他游戏中,只要你画出了黑暗区域的详细地图,在没有光源的情况下,你仍然可以取得进展。
|
||||
|
||||
我们的游戏将保持在这两者之间;在黑暗中不会让你被杀,但你也不能进入任何通道(具有光亮的通道将是一个例外)。对我们来说,让玩家跑进一个黑暗的区域,而没有不让他机会回到他来的地方,似乎是不公平的。
|
||||
我们的游戏将保持在这两者之间;在黑暗中不会让你被杀,但你也不能进入任何通道 (具有光亮的通道将是一个例外)。对我们来说,让玩家跑进一个黑暗的区域,而没有不让他机会回到他来的地方,似乎是不公平的。
|
||||
|
||||
好吧,所以首先,我们来设计在黑暗中玩家无法看到周围环境。
|
||||
|
||||
# location.c
|
||||
## location.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -90,7 +90,7 @@ bool executeGo(void)
|
||||
|
||||
其次,在黑暗中玩家无法看到或使用附近的物体。
|
||||
|
||||
# noun.c
|
||||
## noun.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -193,7 +193,7 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
|
||||
在这两种情况下,我们都使用了一个函数 isLit。它在 misc.c 中被定义(并且使用量增多了)。
|
||||
|
||||
# misc.h
|
||||
## misc.h
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
@@ -216,7 +216,7 @@ extern OBJECT *actorHere(void);
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -318,6 +318,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
注意:
|
||||
@@ -326,13 +327,14 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
- isNoticeable 函数相比 isLit 更进一步。它对每个物体都有效,而不仅仅是地点和灯。它还考虑到玩家库存中的物体总是可以被使用,即使是在黑暗中。
|
||||
- 第 60 行:附近的物体仍然隐藏在黑暗中,被视为'不在这里'。这自然可以防止游戏泄露玩家不应该知道的物体的信息。
|
||||
|
||||
思考题:你还能想到那哪些可以改进的地方吗?
|
||||
::: warning 🤔 思考题:你还能想到那哪些可以改进的地方吗?
|
||||
:::
|
||||
|
||||
我们为实现 isLit 函数的功能从而使用了一个新的属性 light。
|
||||
|
||||
# object.awk
|
||||
## object.awk
|
||||
|
||||
```
|
||||
```awk
|
||||
BEGIN {
|
||||
count = 0;
|
||||
obj = "";
|
||||
@@ -428,11 +430,11 @@ function outputRecord(separator)
|
||||
}
|
||||
```
|
||||
|
||||
默认情况下,亮度为零意味着一个物体不发光。在大白天的每一个地点(通常是除了地下的所有地点)都会被赋予一个正(大于 0)的光线值。其实是什么值并不重要,只要它不是零就可以了。我们还将添加一盏灯,玩家可以携带它来穿越黑暗区域。
|
||||
默认情况下,亮度为零意味着一个物体不发光。在大白天的每一个地点(通常是除了地下的所有地点)都会被赋予一个正 (大于 0) 的光线值。其实是什么值并不重要,只要它不是零就可以了。我们还将添加一盏灯,玩家可以携带它来穿越黑暗区域。
|
||||
|
||||
# object.txt
|
||||
## object.txt
|
||||
|
||||
```
|
||||
```txt
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
@@ -658,7 +660,7 @@ extern OBJECT objs[];
|
||||
|
||||
我们将添加一些命令,我们可以用来打开和关闭灯。
|
||||
|
||||
# parsexec.c
|
||||
## parsexec.c
|
||||
|
||||
```c
|
||||
#include <ctype.h>
|
||||
@@ -733,14 +735,14 @@ bool parseAndExecute(const char *input)
|
||||
|
||||
下面是这些命令的实现。
|
||||
|
||||
# onoff.h
|
||||
## onoff.h
|
||||
|
||||
```c
|
||||
extern bool executeTurnOn(void);
|
||||
extern bool executeTurnOff(void);
|
||||
```
|
||||
|
||||
# onoff.c
|
||||
## onoff.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -789,7 +791,7 @@ bool executeTurnOff(void)
|
||||
|
||||
为了打开和关闭灯,我们将使用我们用来打开和关闭门和盒子的相同技巧(见第 13 章)。
|
||||
|
||||
# toggle.h
|
||||
## toggle.h
|
||||
|
||||
```c
|
||||
extern void cannotBeOpened(void);
|
||||
@@ -813,7 +815,7 @@ extern void toggleBoxLock(void);
|
||||
extern void toggleLamp(void);
|
||||
```
|
||||
|
||||
# toggle.c
|
||||
## toggle.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -895,9 +897,9 @@ void toggleLamp(void)
|
||||
|
||||
最后,我们将在我们生成的地图上标记出黑暗的位置。
|
||||
|
||||
# map.awk
|
||||
## map.awk
|
||||
|
||||
```c
|
||||
```awk
|
||||
BEGIN { print "digraph map {\n\tnode [style=filled]"; }
|
||||
/^- / { outputEdges(); obj = $2; delete a; }
|
||||
/^[ \t]/ { a[$1] = $2; }
|
||||
@@ -929,9 +931,10 @@ function outputNodes()
|
||||
}
|
||||
```
|
||||
|
||||
思考题:尝试将上面的伪代码用 C 语言实现其功能
|
||||
::: warning 🤔 思考题:尝试将上面的伪代码用 C 语言实现其功能
|
||||
:::
|
||||
|
||||
玩家们,请注意不要把灯关掉,也不要把它丢掉。如果这样做,那么在黑暗中你将永远无法找回它。换言之,你会被困在黑暗之中! 幸运的是,下一章将提供一个撤销笨拙行动的方法。
|
||||
玩家们,请注意不要把灯关掉,也不要把它丢掉。如果这样做,那么在黑暗中你将永远无法找回它。换言之,你会被困在黑暗之中!幸运的是,下一章将提供一个撤销笨拙行动的方法。
|
||||
|
||||
输出样例:Welcome to Little Cave Adventure.
|
||||
You are in an open field.
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
# 2.探索未知
|
||||
|
||||
::: tip 提醒
|
||||
::: tip <font size=5><strong> 驾驭项目,而不是被项目驾驭</strong></font>
|
||||
|
||||
# <strong> 驾驭项目, 而不是被项目驾驭</strong>
|
||||
你和一个项目的关系会经历 4 个阶段:
|
||||
|
||||
你和一个项目的关系会经历 4 个阶段:
|
||||
1. 被驾驭:你对它一无所知
|
||||
2. 一知半解:你对其中的主要模块和功能有了基本的了解
|
||||
3. 驾轻就熟:你对整个项目的细节都了如指掌
|
||||
4. 为你所用:你可以随心所欲地在项目中添加你认为有用的功能
|
||||
|
||||
1. 被驾驭: 你对它一无所知
|
||||
2. 一知半解: 你对其中的主要模块和功能有了基本的了解
|
||||
3. 驾轻就熟: 你对整个项目的细节都了如指掌
|
||||
4. 为你所用: 你可以随心所欲地在项目中添加你认为有用的功能
|
||||
如果你想要达成第二个阶段,你需要仔细学习不断探索更新的内容,达到第三个阶段的主要手段是独立完成实验内容和独立调试。至于要达到第四个阶段,就要靠你的主观能动性了:代码还有哪里做得不够好?怎么样才算是够好?应该怎么做才能达到这个目标?
|
||||
|
||||
如果你想要达成第二个阶段,你需要仔细学习不断探索更新的内容, 达到第三个阶段的主要手段是独立完成实验内容和独立调试. 至于要达到第四个阶段, 就要靠你的主观能动性了: 代码还有哪里做得不够好? 怎么样才算是够好? 应该怎么做才能达到这个目标?
|
||||
你毕业后到了工业界或学术界,就会发现真实的项目也都是这样:
|
||||
|
||||
你毕业后到了工业界或学术界, 就会发现真实的项目也都是这样:
|
||||
|
||||
1. 刚接触一个新项目, 不知道如何下手
|
||||
1. 刚接触一个新项目,不知道如何下手
|
||||
2. RTFM, RTFSC, 大致明白项目组织结构和基本的工作流程
|
||||
3. 运行项目的时候发现有非预期行为(可能是配置错误或环境错误, 可能是和已有项目对接出错, 也可能是项目自身的 bug), 然后调试. 在调试过程中, 对这些模块的理解会逐渐变得清晰.
|
||||
4. 哪天需要你在项目中添加一个新功能, 你会发现自己其实可以胜任.
|
||||
3. 运行项目的时候发现有非预期行为 (可能是配置错误或环境错误,可能是和已有项目对接出错,也可能是项目自身的 bug), 然后调试。在调试过程中,对这些模块的理解会逐渐变得清晰。
|
||||
4. 哪天需要你在项目中添加一个新功能,你会发现自己其实可以胜任。
|
||||
|
||||
这说明了: 如果你一遇到 bug 就找大神帮你调试, 你失去的机会和能力会比你想象的多得多
|
||||
这说明了:如果你一遇到 bug 就找大神帮你调试,你失去的机会和能力会比你想象的多得多
|
||||
|
||||
:::
|
||||
文字冒险游戏的基本交互很简单
|
||||
@@ -31,17 +29,17 @@
|
||||
|
||||
那么,当命令很多的时候,如果你将他写在一起,一个文件有五六千行,我相信这样的情况你是不愿意去看的,因此,我们引入了函数的概念。
|
||||
|
||||
自行了解函数的概念,同时去了解当我需要引用别的文件的函数时该怎么办?
|
||||
|
||||
了解一下什么是“驼峰原则”,我们为什么要依据它命名函数?
|
||||
::: warning 🤔 自行了解函数的概念,同时去了解当我需要引用别的文件的函数时该怎么办?
|
||||
|
||||
了解一下什么是“驼峰原则”,我们为什么要依据它命名函数?
|
||||
:::
|
||||
下面的代码示例包含三个函数,每个步骤一个函数:
|
||||
|
||||
1. 函数<em>getInput</em>。
|
||||
2. 函数<em>parseAndExecute</em>。
|
||||
3. 函数<em>main</em>,负责重复调用其他两个函数。
|
||||
|
||||
# main.c
|
||||
## main.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -55,7 +53,7 @@ static bool getInput(void)
|
||||
printf("\n--> ");
|
||||
//你可以将他改成你喜欢的内容
|
||||
return fgets(input, sizeof input, stdin) != NULL;
|
||||
//fgets用于收集键盘的输入
|
||||
//fgets 用于收集键盘的输入
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -69,23 +67,26 @@ int main()
|
||||
|
||||
注意:某些老版本的 C 语言不支持 bool 选项,你将他改为 int 是一样的。
|
||||
|
||||
思考题:static 是什么意思?我为什么要用他?
|
||||
::: warning 🤔 思考题:static 是什么意思?我为什么要用他?
|
||||
:::
|
||||
|
||||
# <strong>parsexec.h</strong>
|
||||
## <strong>parsexec.h</strong>
|
||||
|
||||
```c
|
||||
extern bool parseAndExecute(char *input);
|
||||
```
|
||||
|
||||
思考题:extern 是干什么的?.h 文件又在干嘛?
|
||||
::: warning 🤔 思考题:
|
||||
extern 是干什么的?.h 文件又在干嘛?
|
||||
|
||||
哇,我用了一个指针!input 前面是个指针!!!
|
||||
|
||||
指针是啥?[C 指针详解](https://www.runoob.com/w3cnote/c-pointer-detail.html) STFW(虽然都给你了)
|
||||
|
||||
在这里用指针是为了传参的时候可以传字符串哦
|
||||
:::
|
||||
|
||||
# <strong>parsexec.c</strong>
|
||||
## <strong>parsexec.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -97,11 +98,11 @@ bool parseAndExecute(char *input)
|
||||
{
|
||||
char *verb = strtok(input, " \n");
|
||||
char *noun = strtok(NULL, " \n");
|
||||
//strtok是string库下的一个函数
|
||||
//strtok 是 string 库下的一个函数
|
||||
if (verb != NULL)
|
||||
{
|
||||
if (strcmp(verb, "quit") == 0)
|
||||
//strcmp也是
|
||||
//strcmp 也是
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -116,18 +117,20 @@ bool parseAndExecute(char *input)
|
||||
else
|
||||
{
|
||||
printf("I don't know how to '%s'.\n", verb);
|
||||
//%s是verb附加参数的占位符
|
||||
//%s是 verb 附加参数的占位符
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
你的编译器可能会给出警告 the unused variable ‘noun’,这些不用担心,将会在下一章解决。
|
||||
你的编译器可能会给出警告 the unused variable‘noun’,这些不用担心,将会在下一章解决。
|
||||
|
||||
返回<em>false</em>将导致主循环结束。
|
||||
|
||||
RTFM&&STFW 搞懂 strtok 和 strcmp 的用法
|
||||
::: warning <font size=5><strong>RTFM&&STFW</strong></font>
|
||||
搞懂 strtok 和 strcmp 的用法
|
||||
:::
|
||||
|
||||
考虑一下 NULL 是干什么的
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 3.指明地点
|
||||
|
||||
某种极其糟糕的编程习惯
|
||||
|
||||
# Copy-paste
|
||||
::: warning <font size=5>某种极其糟糕的编程习惯</font>
|
||||
<font size=5><strong>Copy-paste</strong></font>
|
||||
|
||||
我们很多同学在编程的过程中,可能会写出一大堆重复性很强的代码,在最近看的 pa 中,举了这样一个例子,你不需要看懂只需要感受到就可:
|
||||
:::
|
||||
|
||||
```
|
||||
```c
|
||||
if (strcmp(s, "$0") == 0)
|
||||
return cpu.gpr[0]._64;
|
||||
else if (strcmp(s, "ra") == 0)
|
||||
@@ -86,17 +86,18 @@ bx = torch.cat((xs[0], bs[0], xs[1], bs[1], xs[2], bs[2], xs[3], bs[3], xs[4], b
|
||||
xs[24], bs[24], xs[25], bs[25], xs[26], bs[26], xs[27], bs[27], xs[28], bs[28], xs[29], bs[29], xs[30], bs[30], xs[31], bs[31]), 1)
|
||||
```
|
||||
|
||||
你想想,你遇到这么长的代码,你愿意看他吗?
|
||||
::: tip <font size=5>你想想,你遇到这么长的代码,你愿意看他吗?</font>
|
||||
|
||||
更可怕的是,这种编码模式可能会导致意想不到的 bug。
|
||||
|
||||
当你发现这些代码有 bug 的时候, 噩梦才刚刚开始. 也许花了好几天你又调出一个 bug 的时候, 才会想起这个 bug 你好像之前在哪里调过. 你也知道代码里面还有类似的 bug, 但你已经分辨不出哪些代码是什么时候从哪个地方复制过来的了.
|
||||
当你发现这些代码有 bug 的时候,噩梦才刚刚开始。也许花了好几天你又调出一个 bug 的时候,才会想起这个 bug 你好像之前在哪里调过。你也知道代码里面还有类似的 bug, 但你已经分辨不出哪些代码是什么时候从哪个地方复制过来的了。
|
||||
|
||||
这种糟糕的编程习惯叫 Copy-Paste, 经过上面的分析, 相信你也已经领略到它的可怕了. 事实上, [周源源教授](https://cseweb.ucsd.edu/~yyzhou/)的团队在 2004 年就设计了一款工具 CP-Miner, 来自动检测操作系统代码中由于 Copy-Paste 造成的 bug. 这个工具还让周源源教授收获了一篇[系统方向顶级会议 OSDI 的论文](http://pages.cs.wisc.edu/~shanlu/paper/OSDI04-CPMiner.pdf), 这也是她当时所在学校 UIUC 史上的第一篇系统方向的顶级会议论文.
|
||||
这种糟糕的编程习惯叫 Copy-Paste, 经过上面的分析,相信你也已经领略到它的可怕了。事实上,[周源源教授](https://cseweb.ucsd.edu/~yyzhou/)的团队在 2004 年就设计了一款工具 CP-Miner, 来自动检测操作系统代码中由于 Copy-Paste 造成的 bug. 这个工具还让周源源教授收获了一篇[系统方向顶级会议 OSDI 的论文](http://pages.cs.wisc.edu/~shanlu/paper/OSDI04-CPMiner.pdf), 这也是她当时所在学校 UIUC 史上的第一篇系统方向的顶级会议论文。
|
||||
|
||||
后来周源源教授发现, 相比于操作系统, 应用程序的源代码中 Copy-Paste 的现象更加普遍. 于是她们团队把 CP-Miner 的技术应用到应用程序的源代码中, 并创办了 PatternInsight 公司. 很多 IT 公司纷纷购买 PatternInsight 的产品, 并要求提供相应的定制服务, 甚至 PatternInsight 公司最后还被 VMWare 收购了.
|
||||
后来周源源教授发现,相比于操作系统,应用程序的源代码中 Copy-Paste 的现象更加普遍。于是她们团队把 CP-Miner 的技术应用到应用程序的源代码中,并创办了 PatternInsight 公司。很多 IT 公司纷纷购买 PatternInsight 的产品,并要求提供相应的定制服务,甚至 PatternInsight 公司最后还被 VMWare 收购了。
|
||||
|
||||
这个故事折射出, 大公司中程序员的编程习惯也许不比你好多少, 他们也会写出 Copy-Paste 这种难以维护的代码. 但反过来说, 重视编码风格这些企业看中的能力, 你从现在就可以开始培养.
|
||||
这个故事折射出,大公司中程序员的编程习惯也许不比你好多少,他们也会写出 Copy-Paste 这种难以维护的代码。但反过来说,重视编码风格这些企业看中的能力,你从现在就可以开始培养。
|
||||
:::
|
||||
|
||||
<em>传统上,文本冒险是由(许多)不同位置组成的虚拟世界。虽然这不是必需的(一些冒险发生在一个房间里!),但这是解释</em><em>数据结构</em><em>使用的好方法。</em>
|
||||
|
||||
@@ -112,13 +113,13 @@ struct location {
|
||||
};
|
||||
```
|
||||
|
||||
思考题:我们为什么要用结构体来保存位置?
|
||||
::: warning 🤔 思考题:
|
||||
我们为什么要用结构体来保存位置?
|
||||
|
||||
```
|
||||
这样子做有什么好处?
|
||||
```
|
||||
这样子做有什么好处?
|
||||
|
||||
const 又是什么?
|
||||
const 又是什么?
|
||||
:::
|
||||
|
||||
接下来,我们定义一个位置数组。目前,我们保持它非常简单:只有两个位置。
|
||||
|
||||
@@ -135,9 +136,9 @@ struct location locs[2] = {
|
||||
};
|
||||
```
|
||||
|
||||
让我们把它付诸实践。在上一章 (<em>parsexec.c)</em> 的代码示例中,我们更改了第 4、18 和 22 行)。
|
||||
让我们把它付诸实践。在上一章(<em>parsexec.c)</em> 的代码示例中,我们更改了第 4、18 和 22 行)。
|
||||
|
||||
# <strong>parsexec.c</strong>
|
||||
## <strong>parsexec.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -174,14 +175,14 @@ bool parseAndExecute(char *input)
|
||||
|
||||
接下来,我们将一个新模块添加到项目中
|
||||
|
||||
# <strong>location.h</strong>
|
||||
## <strong>location.h</strong>
|
||||
|
||||
```c
|
||||
extern void executeLook(const char *noun);
|
||||
extern void executeGo(const char *noun);
|
||||
```
|
||||
|
||||
# <strong>location.c</strong>
|
||||
## <strong>location.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 4.创建对象
|
||||
|
||||
<em>在我们继续之前,我</em><em>们</em><em>在这里使用的是</em><em>哲学意义上</em><em>的“对象”一词。它与</em><em>面向对象编程</em><em>无关,也与</em><em>Java</em><em>,</em><em>C#</em><em>和</em><em>Python</em><em>等编程语言中预定义的“对象”类型没有任何共同之处。下面,我将定义一个名为 object 的</em><em>结构体。。</em>
|
||||
<em>在我们继续之前,我</em><em>们</em><em>在这里使用的是</em><em>[哲学意义上](https://en.wikipedia.org/wiki/Object_(philosophy))</em><em>的“对象”一词。它与</em><em>[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)</em><em>无关,也与</em><em>Java</em><em>,</em><em>C#</em><em>和</em><em>Python</em><em>等编程语言中预定义的“对象”类型没有任何共同之处。下面,我将定义一个名为 object 的</em><em>结构体。</em>
|
||||
|
||||
冒险游戏中的大多数谜题都围绕着<strong>物品</strong>。例子:
|
||||
|
||||
@@ -47,9 +47,11 @@ objs[] = {
|
||||
|
||||
我们发现 OBJECT 的结构体里面有一个指针和自己长得一样,不用担心,这和链表的操作类似。
|
||||
|
||||
思考题:链表是什么,为什么要有这么一个操作指针?
|
||||
::: warning 🤔 思考题:
|
||||
链表是什么,为什么要有这么一个操作指针?
|
||||
|
||||
链表和数组有什么异同点,他们分别在增删改查上有什么优劣?
|
||||
:::
|
||||
|
||||
为了更容易地用那些所谓的物品或者是地点,我们将为每个元素定义一个名字
|
||||
|
||||
@@ -80,7 +82,8 @@ for (obj = objs; obj < objs + 5; obj++)
|
||||
}
|
||||
```
|
||||
|
||||
暂停理解一下吧
|
||||
::: warning 🤔 暂停理解一下吧
|
||||
:::
|
||||
|
||||
那么,我们有合并这个物品(或地点)列表有什么好处呢?答案是这会让我们的代码变得更加简单,因为许多函数(如上面的函数通过这样的列表)只需要扫描单个列表就可以实现,而不是三个列表。有人可能会说没必要,因为每个命令仅适用于一种类型的对象:
|
||||
|
||||
@@ -96,11 +99,12 @@ for (obj = objs; obj < objs + 5; obj++)
|
||||
|
||||
将所有对象放在一个大列表中,很容易添加一个名为“type”的属性来<em>构造对象</em>,以帮助我们区分不同类型的对象。
|
||||
|
||||
怎么做怎么遍历呢?先思考吧
|
||||
::: warning 🤔 怎么做怎么遍历呢?先思考吧
|
||||
:::
|
||||
|
||||
但是,对象通常具有同样有效的其他特征:
|
||||
|
||||
- <strong>Locations: 通过</strong><strong>道路</strong><strong>连接(将在后面介绍)。如果一个物体无法通过一条通道到达,那么它就不是一个位置。就是这么简单。</strong>
|
||||
- <strong>Locations:通过</strong><strong>道路</strong><strong>连接(将在后面介绍)。如果一个物体无法通过一条通道到达,那么它就不是一个位置。就是这么简单。</strong>
|
||||
- <strong>Items:玩家唯一可以捡起的物品;</strong><strong>可以给他们整一个重量的属性</strong>
|
||||
- <strong>Actors:玩家唯一可以与之交谈,交易,战斗的对象;当然,前提是他们还活着!</strong><strong>可以加一个 HP 属性</strong>
|
||||
|
||||
@@ -122,7 +126,7 @@ player->location->description
|
||||
|
||||
是时候把它们放在一起了。我们从对象数组的全新模块开始
|
||||
|
||||
# Object.h
|
||||
## Object.h
|
||||
|
||||
```c
|
||||
typedef struct object {
|
||||
@@ -143,7 +147,7 @@ extern OBJECT objs[];
|
||||
#define endOfObjs (objs + 6)
|
||||
```
|
||||
|
||||
# Object.c
|
||||
## Object.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -163,15 +167,16 @@ OBJECT objs[] = {
|
||||
|
||||
以下模块将帮助我们找到与指定名词匹配的对象。
|
||||
|
||||
# noun.h
|
||||
## noun.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
```
|
||||
|
||||
# 指针?函数?希望你已经掌握这是什么了
|
||||
::: warning <font size=5><strong>🤔 指针?函数?希望你已经掌握这是什么了</strong></font>
|
||||
:::
|
||||
|
||||
# noun.c
|
||||
## noun.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -229,13 +234,13 @@ OBJECT *getVisible(const char *intention, const char *noun)
|
||||
|
||||
这是另一个辅助程序的函数。它打印存在于特定位置的对象(物品,NPC)的列表。它将用于函数 <em>executeLook</em>,在下一章中,我们将介绍另一个需要它的命令。
|
||||
|
||||
# misc.h
|
||||
## misc.h
|
||||
|
||||
```c
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -251,7 +256,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
//排除玩家在玩家的位置这种蠢东西
|
||||
{
|
||||
if (count++ == 0)
|
||||
//我们需要保证找到一个东西之前他不会打印you see
|
||||
//我们需要保证找到一个东西之前他不会打印 you see
|
||||
{
|
||||
printf("You see:\n");
|
||||
}
|
||||
@@ -265,14 +270,14 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
|
||||
在 <em>location.c</em> 中,命令环<em>顾四周的实现</em>,并根据新的数据结构进行调整。旧的位置数组被删除,变量 <em>locationOfPlayer</em> 也是如此。
|
||||
|
||||
# location.h
|
||||
## location.h
|
||||
|
||||
```c
|
||||
extern void executeLook(const char *noun);
|
||||
extern void executeGo(const char *noun);
|
||||
```
|
||||
|
||||
# location.c
|
||||
## location.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -8,19 +8,14 @@
|
||||
|
||||
玩家属性不再需要存储在单独的变量中;我们可以使用与任何其他对象相同的数据结构。所以玩家,作为一个对象必须具有以下特点:
|
||||
|
||||
- 所处位置(我在哪)
|
||||
- 所处位置(我在哪)
|
||||
- 玩家可能持有的任何物品的位置。
|
||||
|
||||
这使得某些常见操作非常容易实现:
|
||||
|
||||
```
|
||||
<strong> Action </strong> <strong>Typical</strong>
|
||||
```
|
||||
|
||||
<strong> command </strong> <strong>Example</strong>
|
||||
|
||||
| 玩家从一个位置移动到另一个位置 | go | player->location = cave; |
|
||||
|**Action**|**Typical Command**|**Example**|
|
||||
| ------------------------------ | --------- | ------------------------------------ |
|
||||
| 玩家从一个位置移动到另一个位置 | go | player->location = cave; |
|
||||
| 列出某个位置存在的项和参与者 | look | listObjectsAtLocation(cave); |
|
||||
| 玩家获取物品 | get | silver->location = player; |
|
||||
| 玩家掉落物品 | drop | silver->location = player->location; |
|
||||
@@ -31,11 +26,13 @@
|
||||
|
||||
你可以尝试去使用这些命令(上面的前两个示例已经在上一章中实现了)。现在,我们将为玩家和非玩家角色介绍一些典型的<strong>物品栏</strong>操作(命令<em>获取</em>、<em>掉落</em>、<em>给予</em>、<em>询问</em>和<em>物品栏</em>)。
|
||||
|
||||
思考题:你能不能尝试自己实现一下上面的命令?
|
||||
::: warning 🤔 思考题:
|
||||
你能不能尝试自己实现一下上面的命令?
|
||||
|
||||
如果你可以在不参考下面内容的情况下就写出基本内容会有很大收获的
|
||||
:::
|
||||
|
||||
# <strong>parsexec.c</strong>
|
||||
## <strong>parsexec.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -48,7 +45,7 @@ bool parseAndExecute(char *input)
|
||||
{
|
||||
char *verb = strtok(input, " \n");
|
||||
char *noun = strtok(NULL, " \n");
|
||||
//第二次使用strtok要用NULL传参
|
||||
//第二次使用 strtok 要用 NULL 传参
|
||||
if (verb != NULL)
|
||||
{
|
||||
if (strcmp(verb, "quit") == 0)
|
||||
@@ -94,7 +91,7 @@ bool parseAndExecute(char *input)
|
||||
|
||||
新命令由以下模块实现。
|
||||
|
||||
# <strong>inventory.h</strong>
|
||||
## <strong>inventory.h</strong>
|
||||
|
||||
```c
|
||||
extern void executeGet(const char *noun);
|
||||
@@ -104,7 +101,7 @@ extern void executeGive(const char *noun);
|
||||
extern void executeInventory(void);
|
||||
```
|
||||
|
||||
# <strong>inventory.c</strong>
|
||||
## <strong>inventory.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -165,19 +162,21 @@ void executeInventory(void)
|
||||
|
||||
注意:由于动词名词比较好弄,命令 <em>ask</em> 和 <em>give</em> 只有一个参数:item。
|
||||
|
||||
思考题:为什么我们要这样设计?
|
||||
::: warning 🤔 思考题:
|
||||
为什么我们要这样设计?
|
||||
|
||||
你能否为这些命令多加几个参数?
|
||||
:::
|
||||
|
||||
从本质上讲,<em>get</em>, <em>drop</em>, <em>give</em> and <em>ask 这些命令</em>除了将项目从一个地方移动到另一个地方之外,什么都不做。单个函数 <em>move 对象</em>可以对所有四个命令执行该操作。
|
||||
|
||||
# move.h
|
||||
## move.h
|
||||
|
||||
```c
|
||||
extern void moveObject(OBJECT *obj, OBJECT *to);
|
||||
```
|
||||
|
||||
# move.c
|
||||
## move.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -227,18 +226,19 @@ void moveObject(OBJECT *obj, OBJECT *to)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:识别一些我们拿不了的物品需要考虑什么因素?
|
||||
::: warning 🤔 思考题:识别一些我们拿不了的物品需要考虑什么因素?
|
||||
:::
|
||||
|
||||
命令“get”使用函数<em>getVisible</em>将名词转换为 object,就像命令“go”一样;请参阅上一章。但是对于对玩家(或其他一些参与者)已经持有的对象进行<em>drop</em>, <em>ask</em>, <em>give 等</em>命令时,我们需要稍微不同的东西。我们将在 <em>noun.c</em> 中添加一个函数 <em>getPossession</em>。
|
||||
|
||||
# noun.h
|
||||
## noun.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
|
||||
```
|
||||
|
||||
# noun.c
|
||||
## noun.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -318,18 +318,18 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
注意:新函数(45-75 行) <em>getPossession</em> 是 <em>getObject</em> 的装饰器(wrapper )(参见第 52 行),它要么返回匹配的对象,要么返回 NULL(如果没有合适的对象与名词匹配)。
|
||||
注意:新函数(45-75 行) <em>getPossession</em> 是 <em>getObject</em> 的装饰器(wrapper)(参见第 52 行),它要么返回匹配的对象,要么返回 NULL(如果没有合适的对象与名词匹配)。
|
||||
|
||||
函数 <em>actor 这里</em>用于命令 <em>give</em> 和 <em>ask</em>,但它也可能由其他命令调用。所以我们在<em>misc.c</em>中定义了它。
|
||||
|
||||
# misc.h
|
||||
## misc.h
|
||||
|
||||
```c
|
||||
extern OBJECT *actorHere(void);
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -367,7 +367,8 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:上面第四行中的函数 actorHere 返回的指针指向什么?
|
||||
::: warning 🤔 思考题:上面第四行中的函数 actorHere 返回的指针指向什么?
|
||||
:::
|
||||
|
||||
在第 9 行中,有一个详尽的,硬编码的非玩家角色列表(到目前为止,只有一个:<em>守卫</em>)。
|
||||
|
||||
|
||||
@@ -28,11 +28,12 @@ struct object {
|
||||
- 通道总是朝一个方向运行;要双向连接两个位置,我们总是必须创建两个单独的通道。乍一看,这似乎很笨拙,但它确实给了我们很大的灵活性来完善命令“go”的行为
|
||||
- 在大地图上,你可能会发现手动创建所有通道很乏味。所以,我强烈建议你使用自定义工具<em>生成</em>地图中重复性更强的部分。这里不会介绍这一点,但您可能会在第 9 章中找到一些灵感,我们将在其中讨论自动胜场。
|
||||
|
||||
思考题:为什么创建两个通道可以使我们的程序更加灵活?
|
||||
::: warning 🤔 思考题:为什么创建两个通道可以使我们的程序更加灵活?
|
||||
:::
|
||||
|
||||
接下来我们将展开对象数组
|
||||
|
||||
# object.h
|
||||
## object.h
|
||||
|
||||
```c
|
||||
typedef struct object {
|
||||
@@ -56,7 +57,7 @@ extern OBJECT objs[];
|
||||
#define endOfObjs (objs + 8)
|
||||
```
|
||||
|
||||
# object.c
|
||||
## object.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -76,7 +77,7 @@ OBJECT objs[] = {
|
||||
|
||||
我们将在 <em>misc.c</em> 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。
|
||||
|
||||
# misc.h
|
||||
## misc.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getPassage(OBJECT *from, OBJECT *to);
|
||||
@@ -84,7 +85,7 @@ extern OBJECT *actorHere(void);
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -140,14 +141,14 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
|
||||
我们将在命令“go”的实现中使用新功能<em>getPassage</em>来确定是否存在可以将玩家带到所需位置的通道。
|
||||
|
||||
# location.h
|
||||
## location.h
|
||||
|
||||
```c
|
||||
extern void executeLook(const char *noun);
|
||||
extern void executeGo(const char *noun);
|
||||
```
|
||||
|
||||
# location.c
|
||||
## location.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -177,7 +178,7 @@ void executeGo(const char *noun)
|
||||
// already handled by getVisible
|
||||
}
|
||||
else if (getPassage(player->location, obj) != NULL)
|
||||
//go只会在有地方的时候才会运行起来
|
||||
//go 只会在有地方的时候才会运行起来
|
||||
{
|
||||
printf("OK.\n");
|
||||
player->location = obj;
|
||||
@@ -202,14 +203,14 @@ void executeGo(const char *noun)
|
||||
|
||||
我们还将使用新功能<em>getPassage</em>来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。
|
||||
|
||||
# noun.h
|
||||
## noun.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
|
||||
```
|
||||
|
||||
# noun.c
|
||||
## noun.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -294,11 +295,12 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
|
||||
显然,此示例中的地图是微不足道的:只有两个位置,并且它们在两个方向上都连接在一起。第 12 章将增加第三个地点。
|
||||
|
||||
思考题:你能否绘制一张更精细的地图,并将其变成对象列表(位置和通道)
|
||||
::: warning 🤔 思考题:
|
||||
你能否绘制一张更精细的地图,并将其变成对象列表(位置和通道)
|
||||
|
||||
```
|
||||
注:不用当成任务,自行实验即可
|
||||
```
|
||||
注:不用当成任务,自行实验即可
|
||||
|
||||
:::
|
||||
|
||||
输出样例
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 7.增大距离
|
||||
|
||||
<em>一个典型的冒险包含许多谜题。</em><em>众所周知,Infocom</em><em>的冒险很难完成。解决每个难题可能需要数周甚至数月的反复试验。</em>
|
||||
<em>一个典型的冒险包含许多谜题。</em><em>众所周知,[Infocom](https://en.wikipedia.org/wiki/Infocom)</em><em>的冒险很难完成。解决每个难题可能需要数周甚至数月的反复试验。</em>
|
||||
|
||||
<em>当玩家操纵角色失败后,如果只是返回“你</em><em>不能这么操作</em><em>”来回应玩家</em><em>,会很 nt,很没意思</em><em>。</em>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<em>冒险游戏至少应该做的是解释为什么玩家的命令无法完成:“你不能这样做,因为......”这有助于使虚拟世界更具说服力,故事更可信,游戏更有趣。</em>
|
||||
|
||||
我们已经付出了相当大的努力让游戏解释<strong>为什么</strong>某些命令是无效的。只需看看<em>名词.c,inventory.c,location.c</em>,<em>move.c</em>中的许多<em>printf</em>调用。 但随着游戏变得越来越复杂,这正成为一个相当大的负担。我们需要一种更结构化的方法来检测和处理错误情况。这就是我们在本章中将要讨论的内容。
|
||||
我们已经付出了相当大的努力让游戏解释<strong>为什么</strong>某些命令是无效的。只需看看<em>名词.c,inventory.c,location.c</em>,<em>move.c</em>中的许多<em>printf</em>调用。但随着游戏变得越来越复杂,这正成为一个相当大的负担。我们需要一种更结构化的方法来检测和处理错误情况。这就是我们在本章中将要讨论的内容。
|
||||
|
||||
大多数命令对一个或多个对象进行操作,例如:
|
||||
|
||||
@@ -64,7 +64,8 @@ typedef enum {
|
||||
} DISTANCE;
|
||||
```
|
||||
|
||||
typedef 以及枚举类 enum 之前有接触过吗?没有接触过的话就去学习一下吧。
|
||||
::: warning 💡 typedef 以及枚举类 enum 之前有接触过吗?没有接触过的话就去学习一下吧。
|
||||
:::
|
||||
|
||||
在最右边的列中,我们为每个情况提出了一个满足条件。通过一些重新洗牌,我们可以很容易地将其转换为计算对象“距离”的函数(从玩家的角度来看):
|
||||
|
||||
@@ -84,9 +85,11 @@ DISTANCE getDistance(OBJECT *from, OBJECT *to)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:你是否有其他方法实现这个功能?
|
||||
::: warning 🤔 思考题:
|
||||
你是否有其他方法实现这个功能?
|
||||
|
||||
注:自行实验即可
|
||||
:::
|
||||
|
||||
就这样!我们可以调用此函数并对其返回值进行比较。例如,我们在 noun<em>.c</em>中有以下代码:
|
||||
|
||||
@@ -110,7 +113,7 @@ else if (!(getDistance(player, obj) == distSelf ||
|
||||
getDistance(player, obj) == distHere ||
|
||||
getDistance(player, obj) == distOverthere ||
|
||||
getDistance(player, obj) == distHeldContained ||
|
||||
getDistance(player, obj) == distHereContained)
|
||||
getDistance(player, obj) == distHereContained))
|
||||
```
|
||||
|
||||
这可以简化为:
|
||||
@@ -119,13 +122,14 @@ else if (!(getDistance(player, obj) == distSelf ||
|
||||
else if (getDistance(player, obj) >= distNotHere)
|
||||
```
|
||||
|
||||
尝试理解一下这样做的意义
|
||||
::: warning 🤔 尝试理解一下这样做的意义
|
||||
:::
|
||||
|
||||
这只是一个例子,让你对这个概念有所了解;您将在下面找到<em>noun.c</em>的实际实现,看起来略有不同。
|
||||
|
||||
是时候把事情落实到位了。枚举 <em>DISTANCE</em> 和函数 <em>getDistance</em> 的定义被添加到 <em>misc.h</em> 和 <em>misc.c</em> 中,因为我们将在多个模块中使用它们。
|
||||
|
||||
# misc.h
|
||||
## misc.h
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
@@ -150,7 +154,7 @@ extern OBJECT *actorHere(void);
|
||||
extern int listObjectsAtLocation(OBJECT *location);
|
||||
```
|
||||
|
||||
# misc.c
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -226,7 +230,7 @@ int listObjectsAtLocation(OBJECT *location)
|
||||
|
||||
注意:isHolding 这个函数之后我们将在各个地方使用
|
||||
|
||||
# location.h
|
||||
## location.h
|
||||
|
||||
```c
|
||||
extern void executeLook(const char *noun);
|
||||
@@ -235,7 +239,7 @@ extern void executeGo(const char *noun);
|
||||
|
||||
在函数 <em>executeGo</em> 中,我们可以用检查距离来替换大多数 <em>if</em> 条件。
|
||||
|
||||
# location.c
|
||||
## location.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -290,11 +294,12 @@ void executeGo(const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:你能否为 switch 函数增加更多 case 来完善判断条件?
|
||||
::: warning 🤔 思考题:你能否为 switch 函数增加更多 case 来完善判断条件?
|
||||
:::
|
||||
|
||||
函数 <em>executeGet</em> 也是如此。
|
||||
|
||||
# <strong>inventory.h</strong>
|
||||
## <strong>inventory.h</strong>
|
||||
|
||||
```c
|
||||
extern void executeGet(const char *noun);
|
||||
@@ -304,7 +309,7 @@ extern void executeGive(const char *noun);
|
||||
extern void executeInventory(void);
|
||||
```
|
||||
|
||||
# <strong>inventory.c</strong>
|
||||
## <strong>inventory.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -369,14 +374,14 @@ void executeInventory(void)
|
||||
|
||||
最后,我们将调整 noun<em>.c</em>中的约束。我们正在向函数<em>getObject</em>添加两个参数,以便找到特定名词的匹配项,同时忽略任何被认为不存在的对象。这将在下一章中得到真正的回报,我们将在下一章中介绍具有相同标签的不同对象。
|
||||
|
||||
# noun.h
|
||||
## noun.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
|
||||
```
|
||||
|
||||
# noun.c
|
||||
## noun.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -455,7 +460,8 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
思考题:你能理解什么时候加 const,什么时候不用吗?
|
||||
::: warning 🤔 思考题:你能理解什么时候加 const,什么时候不用吗?
|
||||
:::
|
||||
|
||||
在本章中,<em>距离</em>的概念主要用于在游戏可以给用户的不同响应之间进行选择。但是,距离的好处并不局限于<strong>输出</strong>端;它可以同样很好地用于在<strong>输入</strong>端进行改进。在下一章中,我们将使用距离来提高对用户输入的名词的识别。
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# 8.移动方向
|
||||
|
||||
<em>传统的文本冒险使用</em><em>指南针方向</em><em>进行导航。</em>
|
||||
<em>传统的文本冒险使用</em><em>[指南针方向](https://en.wikipedia.org/wiki/Cardinal_direction)</em><em>进行导航。</em>
|
||||
|
||||
例如,我们在第 6 章中绘制的地图上,玩家可能想<strong>向东</strong>移动,从田野移动到洞穴。我们可以通过给连接<strong>Cave</strong>的通道标上“east”来实现这一点。但是,我们首先需要解决两个问题。
|
||||
|
||||
1. 我们可能仍然想把这段通道称为“entrance”和“east”。但现在,一个对象只能有一个标签。
|
||||
2. 在更大的地图上,具有更多的位置和道路,标签“east”将被定义多次。到目前为止,标签在我们的游戏中是全球独一无二的,没有重复项。
|
||||
|
||||
思考题:你能否想出解决办法?
|
||||
::: warning 🤔 思考题:你能否想出解决办法?
|
||||
:::
|
||||
|
||||
这些问题同样适用于其他对象,而不仅仅是通道。
|
||||
|
||||
@@ -17,11 +18,11 @@
|
||||
|
||||
这立即将我们带到了解析器的第三个问题:
|
||||
|
||||
1. 一个标签只能是一个单词;“ sliver coin”这是俩单词,他不接受啊
|
||||
1. 一个标签只能是一个单词;“sliver coin”这是俩单词,他不接受啊
|
||||
|
||||
所有三个问题都将在本章中解决,从问题#1 开始。它通过为每个对象提供一个标签列表来解决,而不仅仅是一个标签。
|
||||
|
||||
# object.h
|
||||
## object.h
|
||||
|
||||
```c
|
||||
typedef struct object {
|
||||
@@ -47,7 +48,7 @@ extern OBJECT objs[];
|
||||
#define endOfObjs (objs + 10)
|
||||
```
|
||||
|
||||
# object.c
|
||||
## object.c
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@@ -63,7 +64,7 @@ static const char *tags6[] = {"east", "entrance", NULL};
|
||||
static const char *tags7[] = {"west", "exit", NULL};
|
||||
static const char *tags8[] = {"west", "north", "south", "forest", NULL};
|
||||
static const char *tags9[] = {"east", "north", "south", "rock", NULL};
|
||||
//我们不固定标签长度,在结束的时候用NULL来标记
|
||||
//我们不固定标签长度,在结束的时候用 NULL 来标记
|
||||
OBJECT objs[] = {
|
||||
{"an open field" , tags0, NULL , NULL },
|
||||
{"a little cave" , tags1, NULL , NULL },
|
||||
@@ -75,22 +76,22 @@ OBJECT objs[] = {
|
||||
{"an exit to the west" , tags7, cave , field },
|
||||
{"dense forest all around" , tags8, field, NULL },
|
||||
{"solid rock all around" , tags9, cave , NULL }
|
||||
//我们用NULL来阻绝进入一个你不知道的地方
|
||||
//我们用 NULL 来阻绝进入一个你不知道的地方
|
||||
};
|
||||
```
|
||||
|
||||
当然,要让这个改动生效,我们还需要调整<em>noun.c</em>中的<em>objectHasTag</em>函数。
|
||||
|
||||
<em>同时,我们将让函数 getVisible</em>和<em>getPossession</em> 告知玩家他必须更具体的选择你到底是银币还是金币 ,而不是随机选择任何一个对象。
|
||||
<em>同时,我们将让函数 getVisible</em>和<em>getPossession</em> 告知玩家他必须更具体的选择你到底是银币还是金币,而不是随机选择任何一个对象。
|
||||
|
||||
# noun.h
|
||||
## noun.h
|
||||
|
||||
```c
|
||||
extern OBJECT *getVisible(const char *intention, const char *noun);
|
||||
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
|
||||
```
|
||||
|
||||
# <strong>noun.c</strong>
|
||||
## <strong>noun.c</strong>
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -107,7 +108,7 @@ static bool objectHasTag(OBJECT *obj, const char *noun)
|
||||
for (tag = obj->tags; *tag != NULL; tag++)
|
||||
{
|
||||
if (strcmp(*tag, noun) == 0) return true;
|
||||
}//扫描对象的tag列表
|
||||
}//扫描对象的 tag 列表
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -187,9 +188,9 @@ OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
|
||||
}
|
||||
```
|
||||
|
||||
问题 #3 可以通过从函数<em>parseAndExecute</em>中删除一个 [空格](http://en.wikipedia.org/wiki/Space_(punctuation))字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是大咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。
|
||||
问题 #3 可以通过从函数<em>parseAndExecute</em>中删除一个 [空格](http://en.wikipedia.org/wiki/Space_(punctuation))字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是打咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。
|
||||
|
||||
# parsexec.c
|
||||
## parsexec.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
@@ -249,4 +250,5 @@ bool parseAndExecute(char *input)
|
||||
|
||||
现在对象数组 ( <em>object.c</em> ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。
|
||||
|
||||
猜猜看该怎么办?
|
||||
::: warning 🤔 猜猜看该怎么办?
|
||||
:::
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 9.练习:生成代码
|
||||
# 9.练习:生成代码
|
||||
|
||||
*到目前为止,我们的冒险游戏有10个对象。每个对象由有5 个属性组成。一个真正的文本冒险可能有数百个甚至数千个对象,并且每个对象的属性数量也可能增加(请参阅下一章)。在目前的形式下,维护如此庞大的对象和属性列表将很困难。*
|
||||
*到目前为止,我们的冒险游戏有 10 个对象。每个对象由有 5 个属性组成。一个真正的文本冒险可能有数百个甚至数千个对象,并且每个对象的属性数量也可能增加(请参阅下一章)。在目前的形式下,维护如此庞大的对象和属性列表将很困难。*
|
||||
|
||||
例如,当我们在添加对象 *wallField* 和 *wallCave* 时,我们必须在三个不同的位置执行此操作:一次在 *object.h* 中(作为*#define*),两次在 *object.c* 中(数组 *objs* 中的一个元素,以及一个单独的标签数组)。这显然十分笨拙并且容易出错。
|
||||
例如,当我们在添加对象 *wallField* 和 *wallCave* 时,我们必须在三个不同的位置执行此操作:一次在 *object.h* 中(作为<em>#define</em>),两次在 *object.c* 中(数组 *objs* 中的一个元素,以及一个单独的标签数组)。这显然十分笨拙并且容易出错。
|
||||
|
||||
我们将不再手工维护object. h和object. c,而是从更适合我们需要的单一源开始生成文件。这个新的源文件可以用你喜欢的任何语言( 典型的是某些特定领域的语言 ),只要你有工具把它转换回C。下面是一个简单的例子,考虑下列布局来组织我们的对象:
|
||||
我们将不再手工维护 object. h 和 object. c,而是从更适合我们需要的单一源开始生成文件。这个新的源文件可以用你喜欢的任何语言 ( 典型的是某些特定领域的语言 ),只要你有工具把它转换回 C。下面是一个简单的例子,考虑下列布局来组织我们的对象:
|
||||
|
||||
```txt
|
||||
/* Raw C code (declarations) */
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
根据到目前为止收集的对象,我们可以构造以下源文件。文件名并不重要;我只是简单地将其命名为*object.txt*,以明确它与*object.h*和*object.c*相关。
|
||||
|
||||
# object.txt
|
||||
## object.txt
|
||||
|
||||
```txt
|
||||
#include <stdio.h>
|
||||
@@ -87,4 +87,5 @@ extern OBJECT objs[];
|
||||
location cave
|
||||
```
|
||||
|
||||
思考题:你能否自己用C来实现这段伪代码?
|
||||
::: warning 🤔 思考题:你能否自己用 C 来实现这段伪代码?
|
||||
:::
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 阶段二:文字冒险(cool)
|
||||
|
||||
# 前言
|
||||
## 前言
|
||||
|
||||
本来打算让各位做下面的任务来进行进一步的学习的,但是想了想,实在是,<strong>太无聊啦</strong>!
|
||||
|
||||
@@ -20,24 +20,24 @@
|
||||
|
||||
当然,如果你选择跳过,也不会对 python 开发那里造成非常大的影响但是你会错失一个非常宝贵的学习机会。
|
||||
|
||||

|
||||

|
||||
|
||||
在 1980 年代, [文字冒险](http://en.wikipedia.org/wiki/Text_adventure) 是一种受人尊敬的电脑游戏类型。但是时代已经变了,在 21 世纪,它们与 带有 3D 引擎的现代 [MMORPG 相比显得苍白无力。](http://en.wikipedia.org/wiki/Mmorpg)书籍在电影的兴起中幸存下来,而基于文本的游戏很快就输掉了与图形游戏的战斗。“互动小说”由一个活跃的社区保持活力,但它的商业价值早已不复存在。
|
||||
|
||||
# 系统调试的黄金法则:KISS 原则
|
||||
## 系统调试的黄金法则:KISS 原则
|
||||
|
||||
这里的 `KISS` 是 `Keep It Simple, Stupid` 的缩写, 它的中文翻译是: 不要在一开始追求绝对的完美.
|
||||
这里的 `KISS` 是 `Keep It Simple, Stupid` 的缩写,它的中文翻译是:不要在一开始追求绝对的完美。
|
||||
|
||||
随着以后代码量会越来越多, 各个模块之间的交互也越来越复杂, 工程的维护变得越来越困难, 一个很弱智的 bug 可能需要调好几天.
|
||||
随着以后代码量会越来越多,各个模块之间的交互也越来越复杂,工程的维护变得越来越困难,一个很弱智的 bug 可能需要调好几天。
|
||||
|
||||
在这种情况下, 系统能跑起来才是王道, 跑不起来什么都是浮云, 追求面面俱到只会增加代码维护的难度.
|
||||
在这种情况下,系统能跑起来才是王道,跑不起来什么都是浮云,追求面面俱到只会增加代码维护的难度。
|
||||
|
||||
唯一可以把你从 bug 的混沌中拯救出来的就是 KISS 法则, 它的宗旨是<strong>从易到难, 逐步推进</strong>, 一次只做一件事, 少做无关的事.
|
||||
唯一可以把你从 bug 的混沌中拯救出来的就是 KISS 法则,它的宗旨是<strong>从易到难,逐步推进</strong>, 一次只做一件事,少做无关的事。
|
||||
|
||||
如果你不知道这是什么意思, 我们以可能发生的 `str` 成员缓冲区溢出问题来作为例子. KISS 法则告诉你, 你应该使用 `assert(0)`, 就算不"得体"地处理上述问题, 仍然不会影响表达式求值的核心功能的正确性.
|
||||
如果你不知道这是什么意思,我们以可能发生的 `str` 成员缓冲区溢出问题来作为例子。KISS 法则告诉你,你应该使用 `assert(0)`, 就算不"得体"地处理上述问题,仍然不会影响表达式求值的核心功能的正确性。
|
||||
|
||||
如果你还记得调试公理, 你会发现两者之间是有联系的: 调试公理第二点告诉你, 未测试代码永远是错的. 与其一下子写那么多"错误"的代码, 倒不如使用 `assert(0)` 来有效帮助你减少这些"错误".
|
||||
如果你还记得调试公理,你会发现两者之间是有联系的:调试公理第二点告诉你,未测试代码永远是错的。与其一下子写那么多"错误"的代码,倒不如使用 `assert(0)` 来有效帮助你减少这些"错误".
|
||||
|
||||
如果把 KISS 法则放在软件工程领域来解释, 它强调的就是多做[单元测试](http://en.wikipedia.org/wiki/Unit_testing): 写一个函数, 对它进行测试, 正确之后再写下一个函数, 再对它进行测试... 一种好的测试方式是使用 assertion 进行验证,
|
||||
如果把 KISS 法则放在软件工程领域来解释,它强调的就是多做[单元测试](http://en.wikipedia.org/wiki/Unit_testing): 写一个函数,对它进行测试,正确之后再写下一个函数,再对它进行测试... 一种好的测试方式是使用 assertion 进行验证,
|
||||
|
||||
KISS 法则不但广泛用在计算机领域, 就连其它很多领域也视其为黄金法则, [这里](http://blog.sciencenet.cn/blog-414166-562616.html)有一篇文章举出了很多的例子, 我们强烈建议你阅读它, 体会 KISS 法则的重要性.
|
||||
KISS 法则不但广泛用在计算机领域,就连其它很多领域也视其为黄金法则,[这里](http://blog.sciencenet.cn/blog-414166-562616.html)有一篇文章举出了很多的例子,我们强烈建议你阅读它,体会 KISS 法则的重要性。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 调试理论
|
||||
|
||||
# 调试公理
|
||||
::: warning 🌲 调试公理
|
||||
|
||||
- The machine is always right. (机器永远是对的)
|
||||
|
||||
@@ -9,32 +9,34 @@
|
||||
|
||||
- Corollary: Mistakes are likely to appear in the "must-be-correct" code.
|
||||
|
||||
这两条公理的意思是: 抱怨是没有用的, 接受代码有 bug 的现实, 耐心调试.
|
||||
这两条公理的意思是:抱怨是没有用的,接受代码有 bug 的现实,耐心调试.
|
||||
:::
|
||||
|
||||
# 如何调试
|
||||
::: warning 😋 如何调试
|
||||
|
||||
- 不要使用"目光调试法", 要思考如何用正确的工具和方法帮助调试
|
||||
|
||||
- 程序设计课上盯着几十行的程序, 你或许还能在大脑中像 NEMU 那样模拟程序的执行过程; 但程序规模大了之后, 很快你就会放弃的: 你的大脑不可能模拟得了一个巨大的状态机
|
||||
- 我们学习计算机是为了学习计算机的工作原理, 而不是学习如何像计算机那样机械地工作
|
||||
- 使用 `assert()` 设置检查点, 拦截非预期情况
|
||||
- 程序设计课上盯着几十行的程序,你或许还能在大脑中像 NEMU 那样模拟程序的执行过程; 但程序规模大了之后,很快你就会放弃的:你的大脑不可能模拟得了一个巨大的状态机
|
||||
- 我们学习计算机是为了学习计算机的工作原理,而不是学习如何像计算机那样机械地工作
|
||||
- 使用 `assert()` 设置检查点,拦截非预期情况
|
||||
|
||||
- 例如 `assert(p != NULL)` 就可以拦截由空指针解引用引起的段错误
|
||||
- 结合对程序执行行为的理解, 使用 `printf()` 查看程序执行的情况(注意字符串要换行)
|
||||
- 结合对程序执行行为的理解,使用 `printf()` 查看程序执行的情况 (注意字符串要换行)
|
||||
|
||||
- `printf()` 输出任意信息可以检查代码可达性: 输出了相应信息, 当且仅当相应的代码块被执行
|
||||
- `printf()` 输出变量的值, 可以检查其变化过程与原因
|
||||
- `printf()` 输出任意信息可以检查代码可达性:输出了相应信息,当且仅当相应的代码块被执行
|
||||
- `printf()` 输出变量的值,可以检查其变化过程与原因
|
||||
- 使用 GDB 观察程序的任意状态和行为
|
||||
|
||||
- 打印变量, 断点, 监视点, 函数调用栈...
|
||||
- 打印变量,断点,监视点,函数调用栈...
|
||||
:::
|
||||
|
||||

|
||||

|
||||
|
||||
# 调试理论
|
||||
## 什么是调试理论
|
||||
|
||||
如果我们能判定任意程序状态的正确性,那么给定一个 failure,我们可以通过二分查找定位到第一个 error 的状态,此时的代码就是 fault (bug)。
|
||||
|
||||
# 正确的方法:理解程序的执行过程,弄清楚到底为何导致了 bug
|
||||
## 正确的方法:理解程序的执行过程,弄清楚到底为何导致了 bug
|
||||
|
||||
- `ssh`:使用 `-v` 选项检查日志
|
||||
- `gcc`:使用 `-v` 选项打印各种过程
|
||||
@@ -44,22 +46,22 @@
|
||||
|
||||
各个工具普遍提供调试功能,帮助用户/开发者了解程序的行为
|
||||
|
||||
# 错误概念
|
||||
## 错误概念
|
||||
|
||||
我们来简单梳理一下段错误发生的原因. 首先, 机器永远是对的. 如果程序出了错, 先怀疑自己的代码有 bug . 比如由于你的疏忽, 你编写了 `if (p = NULL)` 这样的代码. 但执行到这行代码的时候, 也只是 `p` 被赋值成 `NULL`, 程序还会往下执行. 然而等到将来对 `p` 进行了解引用的时候, 才会触发段错误, 程序彻底崩溃.
|
||||
我们来简单梳理一下段错误发生的原因。首先,机器永远是对的。如果程序出了错,先怀疑自己的代码有 bug . 比如由于你的疏忽,你编写了 `if (p = NULL)` 这样的代码。但执行到这行代码的时候,也只是 `p` 被赋值成 `NULL`, 程序还会往下执行。然而等到将来对 `p` 进行了解引用的时候,才会触发段错误,程序彻底崩溃。
|
||||
|
||||
我们可以从上面的这个例子中抽象出一些软件工程相关的概念:
|
||||
我们可以从上面的这个例子中抽象出一些软件工程相关的概念:
|
||||
|
||||
- Fault: 实现错误的代码, 例如 `if (p = NULL)`
|
||||
- Error: 程序执行时不符合预期的状态, 例如 `p` 被错误地赋值成 `NULL`
|
||||
- Failure: 能直接观测到的错误, 例如程序触发了段错误
|
||||
- Fault: 实现错误的代码,例如 `if (p = NULL)`
|
||||
- Error: 程序执行时不符合预期的状态,例如 `p` 被错误地赋值成 `NULL`
|
||||
- Failure: 能直接观测到的错误,例如程序触发了段错误
|
||||
|
||||
调试其实就是从观测到的 failure 一步一步回溯寻找 fault 的过程, 找到了 fault 之后, 我们就很快知道应该如何修改错误的代码了. 但从上面的例子也可以看出, 调试之所以不容易, 恰恰是因为:
|
||||
调试其实就是从观测到的 failure 一步一步回溯寻找 fault 的过程,找到了 fault 之后,我们就很快知道应该如何修改错误的代码了。但从上面的例子也可以看出,调试之所以不容易,恰恰是因为:
|
||||
|
||||
- fault 不一定马上触发 error
|
||||
- 触发了 error 也不一定马上转变成可观测的 failure
|
||||
- error 会像滚雪球一般越积越多, 当我们观测到 failure 的时候, 其实已经距离 fault 非常遥远了
|
||||
- error 会像滚雪球一般越积越多,当我们观测到 failure 的时候,其实已经距离 fault 非常遥远了
|
||||
|
||||
理解了这些原因之后, 我们就可以制定相应的策略了:
|
||||
理解了这些原因之后,我们就可以制定相应的策略了:
|
||||
|
||||
- 尽可能把 fault 转变成 error. 这其实就是测试做的事情, 所以我们在上一节中加入了表达式生成器的内容, 来帮助大家进行测试, 后面的实验内容也会提供丰富的测试用例. 但并不是有了测试用例就能把所有 fault 都转变成 error 了, 因为这取决于测试的覆盖度. 要设计出一套全覆盖的测试并不是一件简单的事情, 越是复杂的系统, 全覆盖的测试就越难设计. 但是, 如何提高测试的覆盖度, 是学术界一直以来都在关注的问题.
|
||||
- 尽可能把 fault 转变成 error. 这其实就是测试做的事情,所以我们在上一节中加入了表达式生成器的内容,来帮助大家进行测试,后面的实验内容也会提供丰富的测试用例。但并不是有了测试用例就能把所有 fault 都转变成 error 了,因为这取决于测试的覆盖度。要设计出一套全覆盖的测试并不是一件简单的事情,越是复杂的系统,全覆盖的测试就越难设计。但是,如何提高测试的覆盖度,是学术界一直以来都在关注的问题。
|
||||
|
||||
@@ -2,59 +2,63 @@
|
||||
|
||||
请在开始进行 C 语言编程之后查阅使用
|
||||
|
||||

|
||||

|
||||
|
||||
# GDB 是什么?
|
||||
## GDB 是什么?
|
||||
|
||||
调试器,简单来说就是当你代码跑不通时候修正错误用的
|
||||
|
||||
[GDB's Mascot?](https://sourceware.org/gdb/mascot/)
|
||||
|
||||
可搭配插件 gef pwndbg pwngdb peda
|
||||
|
||||
# 基本操作
|
||||
## 基本操作
|
||||
|
||||
[GDB 快速入门教程](https://www.bilibili.com/video/BV1EK411g7Li/)
|
||||
|
||||
### <strong>GDB 使用表</strong>
|
||||
|
||||
<em>run (r)</em>运行程序
|
||||
`run (r)`运行程序
|
||||
|
||||
<em>b</em>打断点,可以在函数和位置打断点
|
||||
`b`打断点,可以在函数和位置打断点
|
||||
|
||||
<em>info b</em>查看打断点的位置
|
||||
`info b`查看打断点的位置
|
||||
|
||||
<em>n</em>下一步,跳过函数的
|
||||
`n`下一步,跳过函数的
|
||||
|
||||
<em>list</em>查看源代码
|
||||
`list`查看源代码
|
||||
|
||||
<em>-p</em>走 PID 路线
|
||||
`-p`走 PID 路线
|
||||
|
||||
edit [file:]function 看现在停下的函数位置
|
||||
`edit [file:]function` 看现在停下的函数位置
|
||||
|
||||
step 进入任何函数
|
||||
`step` 进入任何函数
|
||||
|
||||
<em>p</em>打印变量
|
||||
`p`打印变量
|
||||
|
||||
<em>shell</em>输入命令
|
||||
`shell`输入命令
|
||||
|
||||
<em>set logging on</em>记录日志
|
||||
`set logging on`记录日志
|
||||
|
||||
<em>watchpoint</em>观察变量是否变化的观察点
|
||||
`watchpoint`观察变量是否变化的观察点
|
||||
|
||||
<em>watch</em>设置观察点位置,watch*(地址)
|
||||
`watch`设置观察点位置,watch*(地址)
|
||||
|
||||
<em>layout split</em>开启 TUI 模式
|
||||
`layout split`开启 TUI 模式
|
||||
|
||||
<em>whatis</em>查看变量类型
|
||||
`whatis`查看变量类型
|
||||
|
||||
<em>ptype</em>查看详细信息
|
||||
`ptype`查看详细信息
|
||||
|
||||
#### <strong>TUI</strong>
|
||||
|
||||
<em>ctrl +x +a</em>开启
|
||||
`ctrl + x + a`开启
|
||||
|
||||
<em>ctrl</em>+<em>p</em>+<em>n</em>往前
|
||||
`ctrl + p + n`往前
|
||||
|
||||
<em>ctrl</em> +<em>l</em>重新整理页面
|
||||
`ctrl + l`重新整理页面
|
||||
|
||||
# 官方手册
|
||||
## 官方手册
|
||||
|
||||
[GDB User Manual](https://sourceware.org/gdb/current/onlinedocs/gdb)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# C 的历史问题:undefined behavior
|
||||
|
||||

|
||||

|
||||
|
||||
简写为 UB
|
||||
|
||||
@@ -22,7 +22,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
```
|
||||
|
||||
讓我們看看不同編譯器的 Debug 模式下執行的結果
|
||||
让我们看看不同编译器的 Debug 模式下执行的结果
|
||||
|
||||
Visual C++ 6.0
|
||||
|
||||
@@ -36,7 +36,7 @@ MinGW(GCC)
|
||||
|
||||
> The answer : 25
|
||||
|
||||
我們試試看在 Release 下執行的結果
|
||||
我们试试看在 Release 下执行的结果
|
||||
|
||||
Visual C++ 6.0
|
||||
|
||||
@@ -50,9 +50,9 @@ MinGW(GCC)
|
||||
|
||||
> The answer : 25
|
||||
|
||||
C 語言最初為了開發 UNIX 和系統軟體而生,本質是低階的程式語言,
|
||||
C 语言最初为了开发 UNIX 和系统软体而生,本质是低阶的程式语言
|
||||
|
||||
在語言規範層級存在 UB,可允許編譯器引入更多最佳化。比方說 `X * 2 / 2` 在沒有 overflow 發生的時候,可最佳化為 `X`。
|
||||
在语言规范层级存在 UB,可允许编译器引入更多最佳化。比方说 `X * 2 / 2` 在沒有 overflow 发生的時候,可最佳化为 `X`。
|
||||
|
||||
而且值得注意的是,在你的程序初始化之前,栈里面塞的一堆东西也是 UB。
|
||||
|
||||
@@ -64,4 +64,4 @@ C 語言最初為了開發 UNIX 和系統軟體而生,本質是低階的程式
|
||||
|
||||
[万恶的未定义行为 | 程式设计 遇上 小提琴](https://blog.ez2learn.com/2008/09/27/evil-undefined-behavior/)
|
||||
|
||||
<del>关键是,老师喜欢出题刁难你啊!真烦啊!</del>
|
||||
<del>关键是,老师喜欢出题刁难你啊!真烦啊!</del>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# C 编译器干了什么
|
||||
|
||||
# 以 gcc 为例
|
||||
## 以 gcc 为例
|
||||
|
||||
1、预处理,生成 .i 的文件[预处理器 cpp]
|
||||
1、预处理,生成 .i 的文件[预处理器 cpp]
|
||||
|
||||
2、将预处理后的文件转换成汇编语言, 生成文件 .s [编译器 egcs]
|
||||
2、将预处理后的文件转换成汇编语言,生成文件 .s [编译器 egcs]
|
||||
|
||||
3、有汇编变为目标代码(机器代码)生成 .o 的文件[汇编器 as]
|
||||
3、有汇编变为目标代码 (机器代码) 生成 .o 的文件[汇编器 as]
|
||||
|
||||
4、连接目标代码, 生成可执行程序 [链接器 ld]
|
||||
4、连接目标代码,生成可执行程序 [链接器 ld]
|
||||
|
||||
# 有啥用
|
||||
## 有啥用
|
||||
|
||||
有一天你发现,某一段 C 语言只有几行,但是用了大量的魔法宏定义以及魔法操作以及神奇的元编程。
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
如果有一天你要学习汇编语言,或者说出现了在代码中看不出的 bug,你可能需要翻译成.s 文件
|
||||
|
||||
# 了解更多
|
||||
## 了解更多
|
||||
|
||||
当然不止如此,编译器还承担了优化的过程,有时候同一份代码,经过 O1 和 O2 不同优化可能最后代码都不一样。
|
||||
|
||||
推荐阅读
|
||||
|
||||
[http://www.ruanyifeng.com/blog/2014/11/compiler.html](http://www.ruanyifeng.com/blog/2014/11/compiler.html)
|
||||
[编译器的工作过程](http://www.ruanyifeng.com/blog/2014/11/compiler.html)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Inline Assembly 与链接加载
|
||||
|
||||
# Inline Assembly
|
||||
## Inline Assembly
|
||||
|
||||
可以在 C 代码里嵌入汇编语言的骚操作。毕竟编译器本身也就是个复制,翻译,,粘贴的过程。
|
||||
|
||||
@@ -14,7 +14,7 @@ C 艹可能没那么容易了····比如说虚函数调用,那你就不太
|
||||
|
||||
当然这里可以参考 c inline Asm 的教程 但是已经脱离了本文的主题了
|
||||
|
||||
这里给指条路 [https://dmalcolm.fedorapeople.org/gcc/2015-08-31/rst-experiment/how-to-use-inline-assembly-language-in-c-code.html](https://dmalcolm.fedorapeople.org/gcc/2015-08-31/rst-experiment/how-to-use-inline-assembly-language-in-c-code.html)
|
||||
这里给指条路 [How to Use Inline Assembly Language in C Code](https://dmalcolm.fedorapeople.org/gcc/2015-08-31/rst-experiment/how-to-use-inline-assembly-language-in-c-code.html)
|
||||
|
||||
> 诸如 Go 的高级语言 也可以通过 inline C 来 内链汇编
|
||||
|
||||
@@ -24,17 +24,17 @@ C 艹可能没那么容易了····比如说虚函数调用,那你就不太
|
||||
|
||||
当然,你可以通过 RTFM 来将 C 语言的变量塞到汇编中处理。
|
||||
|
||||
在 Windows 平台下 VS 没有 Code 可以以 `__asm {}` 代码块来进行实验 但是注意 只能在 x86 模式下使用 x64 不支持 可以参考 [https://docs.microsoft.com/zh-tw/cpp/assembler/inline/asm?view=msvc-170](https://docs.microsoft.com/zh-tw/cpp/assembler/inline/asm?view=msvc-170)
|
||||
在 Windows 平台下 VS 没有 Code 可以以 `__asm {}` 代码块来进行实验 但是注意 只能在 x86 模式下使用 x64 不支持 可以参考 [__asm](https://docs.microsoft.com/zh-tw/cpp/assembler/inline/asm?view=msvc-170)
|
||||
|
||||
以上两种平台的方案都其实本质是编译器特殊宏 并不包括在 C 的标准内 所以要针对不同的编译器 寻找对应的文档
|
||||
|
||||
# 静态链接
|
||||
## 静态链接
|
||||
|
||||
当你使用 GCC 生成可执行文件./a.out 时,到底发生了什么?
|
||||
|
||||
为什么就可以直接执行呢?当你问及这个问题时,那么就大有一番探索的空间了
|
||||
|
||||
# 链接器
|
||||
## 链接器
|
||||
|
||||
链接器的功能:将一个可执行程序所需的目标文件和库最终整合在一起。
|
||||
|
||||
@@ -42,15 +42,13 @@ C 艹可能没那么容易了····比如说虚函数调用,那你就不太
|
||||
|
||||
这个就是帮你和外部库连接起来的重要部分。
|
||||
|
||||
一个程序包含三个段:.text 、.data 和 .bss 段。
|
||||
一个程序包含三个段:.text、.data 和 .bss 段。
|
||||
|
||||
```
|
||||
而各目标文件和库都包含这三段,所以,ld 链接器的功能就是将各个目标文件和库的三段合并在一起。
|
||||
|
||||
当然,链接器所完成的链接工作并非只是简单地将各个目标文件和库的三段内存堆砌在一起,而是还要完成 “重定位” 的工作。
|
||||
```
|
||||
当然,链接器所完成的链接工作并非只是简单地将各个目标文件和库的三段内存堆砌在一起,而是还要完成“重定位”的工作。
|
||||
|
||||
## 查看二进制文件的工具
|
||||
### 查看二进制文件的工具
|
||||
|
||||
使用 objdump 查看 data 节的 x,y
|
||||
|
||||
@@ -60,7 +58,7 @@ C 艹可能没那么容易了····比如说虚函数调用,那你就不太
|
||||
|
||||
使用 IDA BinaryNInja 一类反汇编工具
|
||||
|
||||
# 动态链接
|
||||
## 动态链接
|
||||
|
||||
静态链接一次用那么多,实在是太大太大了
|
||||
|
||||
@@ -74,7 +72,7 @@ Linux 下一般是 .so 如果你看到了 .a 那个一般是 archive 的缩写
|
||||
|
||||
使用动态链接的好处在于 可以热加载和热更新
|
||||
|
||||
# 共享连接的加载
|
||||
## 共享连接的加载
|
||||
|
||||
使用 ldd 来查看 a.out 就可以查看动态链接库
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
值得一提的是,我不会在本教程讲授过于基础的概念,但是会贴出你可能需要学习的内容。
|
||||
|
||||

|
||||

|
||||
|
||||
同时我要说的是:C 语言为了适配多种多样的硬件以及各式各样的操作,他对非常多的 undefined 操作不做太多限制,也就是说你可能会出现各种各样的问题,<del>甚至把你电脑炸了</del>
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
## <strong>光玉</strong>
|
||||
|
||||
想象一下你正在玩 Flappy Bird ,你今晚的目标是拿到 100 分,不然就不睡觉。 经过千辛万苦,你拿到了 99 分,就要看到成功的曙光的时候,你竟然失手了!你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?为什么不让我存档?”
|
||||
想象一下你正在玩 Flappy Bird,你今晚的目标是拿到 100 分,不然就不睡觉。经过千辛万苦,你拿到了 99 分,就要看到成功的曙光的时候,你竟然失手了!你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?为什么不让我存档?”
|
||||
|
||||
[**Play Happy Bird**](https://flappybird.io/)
|
||||
|
||||
想象一下你正在写代码,你今晚的目标是实现某一个新功能,不然就不睡觉。经过千辛万苦,你终于把代码写好了,保存并编译运行,你看到调试信息一行一行地在终端上输出。就要看到成功的曙光的时候,竟然发生了错误!你仔细思考,发现你之前的构思有着致命的错误,但之前正确运行的代码已经永远离你而去了。你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?”你绝望地倒在屏幕前 ... ... 这时,你发现身边渐渐出现无数的光玉,把你包围起来,耀眼的光芒令你无法睁开眼睛 ... ... 等到你回过神来,你发现屏幕上正是那份之前正确运行的代码!但在你的记忆中,你确实经历过那悲痛欲绝的时刻 ... ... 这一切真是不可思议啊 ... ...
|
||||
|
||||
@@ -40,7 +42,7 @@ git config --global user.name "Zhang San" # your name
|
||||
git config --global user.email "zhangsan@foo.com" # your email
|
||||
```
|
||||
|
||||
经过这些配置, 你就可以开始使用 `git` 了。
|
||||
经过这些配置,你就可以开始使用 `git` 了。
|
||||
|
||||
你会通过 `git clone` 命令来拉取远程仓库的代码,里面已经包含一些 `git` 记录,因此不需要额外进行初始化。如果你想在别的实验、项目中使用 `git` ,你首先需要切换到实验、项目的目录中,然后输入
|
||||
|
||||
@@ -68,11 +70,11 @@ git log
|
||||
git status
|
||||
```
|
||||
|
||||
可以得知,与当前存档相比,哪些文件发生了变化.
|
||||
可以得知,与当前存档相比,哪些文件发生了变化。
|
||||
|
||||
### 提交
|
||||
|
||||
你可以像以前一样编写代码。等到你的开发取得了一些阶段性成果,你应该马上进行 “ 提交( commit )”。
|
||||
你可以像以前一样编写代码。等到你的开发取得了一些阶段性成果,你应该马上进行“提交(commit)”。
|
||||
|
||||
首先你需要使用 `git status` 查看是否有新的文件或已修改的文件未被跟踪;若有,则使用 `git add` 将文件加入跟踪列表,例如
|
||||
|
||||
@@ -88,7 +90,7 @@ git add -A
|
||||
git add .
|
||||
```
|
||||
|
||||
但这样可能会跟踪了一些不必要的文件(二进制文件),例如编译产生的 `.o` 文件,和最后产生的可执行文件。事实上,我们只需要跟踪代码源文件即可。为了让 `git` 在添加跟踪文件之前作筛选,你可以编辑 `.gitignore` 文件( 没有的话手动创建 文件名就叫这个 ),在里面给出需要被 `git` 忽略的文件和文件类型。
|
||||
但这样可能会跟踪了一些不必要的文件(二进制文件),例如编译产生的 `.o` 文件,和最后产生的可执行文件。事实上,我们只需要跟踪代码源文件即可。为了让 `git` 在添加跟踪文件之前作筛选,你可以编辑 `.gitignore` 文件(没有的话手动创建 文件名就叫这个),在里面给出需要被 `git` 忽略的文件和文件类型。
|
||||
|
||||
[这个网页](https://www.toptal.com/developers/gitignore) 可以根据你搜索的语言来给你创建必要的 `.gitignore` 文件
|
||||
|
||||
@@ -105,13 +107,13 @@ file.o # 一个编译后的文件
|
||||
|
||||
建议把编译后的文件都加入 `.gitignore` 并在 `README.md` 文件中留下编译的详细操作流程,节省 `.git` 空间、符合提交规范。
|
||||
|
||||
把新文件加入跟踪列表后, 使用 `git status` 再次确认. 确认无误后就可以存档了, 使用
|
||||
把新文件加入跟踪列表后,使用 `git status` 再次确认。确认无误后就可以存档了,使用
|
||||
|
||||
```bash
|
||||
git commit -m "...comment..."
|
||||
```
|
||||
|
||||
提交工程当前的状态(注释)。其中 `...comment...` 是你本次提交的注释( 一般在注释中简略写出本次提交干了什么)以下为注释规范,养成良好习惯请遵守:
|
||||
提交工程当前的状态(注释)。其中 `...comment...` 是你本次提交的注释(一般在注释中简略写出本次提交干了什么)以下为注释规范,养成良好习惯请遵守:
|
||||
|
||||
```bash
|
||||
模板:
|
||||
@@ -145,7 +147,7 @@ subject为commit概述
|
||||
|
||||
你可以使用 `git log` 查看存档记录,你应该能看到刚才编辑的注释。
|
||||
|
||||
### <strong>读档</strong><strong>( 回溯到某一个 commit )</strong>
|
||||
### <strong>读档</strong><strong>(回溯到某一个 commit)</strong>
|
||||
|
||||
如果你遇到了上文提到的让你悲痛欲绝的情况,现在你可以使用光玉来救你一命了。首先使用 `git log` 来查看已有的存档,并决定你需要回到哪个过去。每一份存档都有一个 `hash code`,例如 `b87c512d10348fd8f1e32ddea8ec95f87215aaa5` , 你需要通过 `hash code` 来告诉 `git` 你希望读哪一个档。使用以下命令进行读档:
|
||||
|
||||
@@ -155,7 +157,7 @@ git reset --hard b87c
|
||||
|
||||
其中 `b87c` 是上文 `hash code` 的前缀:你不需要输入整个 hash code. 这时你再看看你的代码,你已经成功地回到了过去!
|
||||
|
||||
但事实上, 在使用 `git reset` 的 `hard` 模式之前, 你需要再三确认选择的存档是不是你的真正目标。 如果你读入了一个较早的存档,那么比这个存档新的所有记录都将被删除!这意味着你不能随便回到“将来”了。
|
||||
但事实上,在使用 `git reset` 的 `hard` 模式之前,你需要再三确认选择的存档是不是你的真正目标。如果你读入了一个较早的存档,那么比这个存档新的所有记录都将被删除!这意味着你不能随便回到“将来”了。
|
||||
|
||||
### 分支
|
||||
|
||||
@@ -165,7 +167,7 @@ git reset --hard b87c
|
||||
git branch
|
||||
```
|
||||
|
||||
查看所有分支. 其中 `master` 是主分支,使用 `git init` 初始化之后会自动建立主分支。
|
||||
查看所有分支。其中 `master` 是主分支,使用 `git init` 初始化之后会自动建立主分支。
|
||||
|
||||
读档的时候使用以下命令:
|
||||
|
||||
@@ -190,13 +192,13 @@ git checkout -B 分支名
|
||||
|
||||
- 把修改结果保存到一个新的分支中,如果分支已存在,其内容将会被覆盖
|
||||
|
||||
不同的分支之间不会相互干扰,这也给项目的分布式开发带来了便利。有了分支功能,你就可以像第三视点那样在一个世界的不同时间 ( 一个分支的多个存档 ),或者是多个平行世界 ( 多个分支 ) 之间来回穿梭了。
|
||||
不同的分支之间不会相互干扰,这也给项目的分布式开发带来了便利。有了分支功能,你就可以像第三视点那样在一个世界的不同时间 ( 一个分支的多个存档 ),或者是多个平行世界(多个分支)之间来回穿梭了。
|
||||
|
||||
### <strong>更多功能</strong>
|
||||
|
||||
以上介绍的是 `git` 的一些基本功能,`git` 还提供很多强大的功能,例如使用 `git diff` 比较同一个文件在不同版本中的区别,使用 `git bisect` 进行二分搜索来寻找一个 bug 在哪次提交中被引入...
|
||||
|
||||
其它功能的使用请参考 `git help` , `man git` , 或者在网上搜索相关资料。
|
||||
其它功能的使用请参考 `git help` , `man git` ,或者在网上搜索相关资料。
|
||||
|
||||
## 全球最大男性交友网站 —— Github
|
||||
|
||||
@@ -205,17 +207,16 @@ git checkout -B 分支名
|
||||
|
||||
你把任务分配好让组员去写代码中的某一个模块。组员写好之后发给你。
|
||||
|
||||
你一看,通过QQ发过来的是一个文件啊文件 比如说 `学生管理模块.c` 你打开一看,我去是**乱码**。
|
||||
你一看,通过 QQ 发过来的是一个文件啊文件 比如说 `学生管理模块.c` 你打开一看,我去是**乱码**。
|
||||
|
||||
你废了九牛二虎之力把他的 GBK 编码改成 UTF8 之后,细细地品鉴这段代码,发现我去有严重逻辑错误,而且代码很不规范。
|
||||
|
||||
你通过QQ告诉他,这一行有问题,能不能改一下。他说,好的我改一下。
|
||||
你通过 QQ 告诉他,这一行有问题,能不能改一下。他说,好的我改一下。
|
||||
|
||||
然后又发了文件啊文件给你,如此反复循环,你俩已经互相传了好几百个源代码文件,很没效率!
|
||||
:::
|
||||
|
||||
|
||||
> 通过Git版本控制管理自己的代码 ,再通过Github来发布、同步互联,是一个很好的解决方案!
|
||||
> 通过 Git 版本控制管理自己的代码,再通过 Github 来发布、同步互联,是一个很好的解决方案!
|
||||
|
||||
简介
|
||||
|
||||
@@ -223,7 +224,7 @@ git checkout -B 分支名
|
||||
|
||||
页面大概是这样(老图):
|
||||
|
||||

|
||||

|
||||
|
||||
### Git 和 Github
|
||||
|
||||
@@ -241,7 +242,7 @@ git checkout -B 分支名
|
||||
|
||||
他会用一个像是命令行交互的方式引导注册,你需要依次填写 `邮箱` 、`密码` 、 `用户名(此为 ID 非昵称)` 、`是否同意邮箱广告推送` 、`机器验证码` 之后创建账户,随后会给你填写的邮箱发送验证码,填写注册。
|
||||
|
||||
随后是一个欢迎问卷😋随便填写、如果他问你什么PRO Plan 选择 free 不付费就好。
|
||||
随后是一个欢迎问卷😋随便填写、如果他问你什么 PRO Plan 选择 free 不付费就好。
|
||||
|
||||
最后你访问[GitHub 官网](https://github.com)应该会显示你的 dashboard 管理台界面
|
||||
|
||||
@@ -251,7 +252,7 @@ git checkout -B 分支名
|
||||
|
||||
如果有就直接跳过这一步
|
||||
|
||||
如果没有,打开 Shell( Windows 下打开 Git Bash <em>前提是你已经安装好了 git 在桌面右键应该会有 Git bash here 选项 </em>),创建 SSH Key:
|
||||
如果没有,打开 Shell(Windows 下打开 Git Bash <em>前提是你已经安装好了 git 在桌面右键应该会有 Git bash here 选项 </em>),创建 SSH Key:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电子邮件地址
|
||||
@@ -263,11 +264,11 @@ ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电
|
||||
|
||||
登陆 `GitHub`,点击右上角自己的头像,打开 `settings`
|
||||
|
||||

|
||||

|
||||
|
||||
然后打开左侧栏 `SSH and GPG`` keys` 页面
|
||||
|
||||

|
||||

|
||||
|
||||
然后,点 `New SSH Key`,填上任意 Title,在 Key 文本框里粘贴 `id_rsa.pub` 文件的内容即可
|
||||
|
||||
@@ -277,11 +278,11 @@ ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电
|
||||
|
||||
首先在 GitHub 主页,找到 `New` 或者 `Create repository` 一个绿色的按钮,创建一个新的仓库
|
||||
|
||||

|
||||

|
||||
|
||||
然后填上这个仓库的大名就可以创建了
|
||||
|
||||

|
||||

|
||||
|
||||
根据之前学习的方法在本地创建完 git 仓库之后
|
||||
|
||||
@@ -302,11 +303,9 @@ git clone git@github.com:yourname/gitexample.git
|
||||
|
||||
> 以上方法是基于 ssh 方式的,下面方法是基于 HTTPS 方式的
|
||||
|
||||
或者你可以跟随新创建之后的指引,`…or create a new repository on the command line` 内他描述了如何创建一个文件夹、创建一个README.md的文件,然后和github仓库绑定。
|
||||
|
||||

|
||||
|
||||
或者你可以跟随新创建之后的指引,`…or create a new repository on the command line` 内他描述了如何创建一个文件夹、创建一个 README.md 的文件,然后和 github 仓库绑定。
|
||||
|
||||

|
||||
|
||||
### 下载代码——clone
|
||||
|
||||
@@ -320,7 +319,7 @@ git clone <url>
|
||||
|
||||
首先,代码的 url 在下图所示的位置
|
||||
|
||||

|
||||

|
||||
|
||||
然后复制完代码后切换回我们的命令行
|
||||
|
||||
@@ -334,11 +333,11 @@ git clone https://github.com/camera-2018/git-example.git
|
||||
>
|
||||
> 如果你使用我的仓库的话,你 clone 过后在你自己电脑更改的文件等东西,是没法直接提交回来的(因为你没有我仓库管理权限)
|
||||
>
|
||||
> 如果你非要给我的仓库添加东西呢 也是可以,参照下面的 PR (Pull Request)教程
|
||||
> 如果你非要给我的仓库添加东西呢 也是可以,参照下面的 PR(Pull Request)教程
|
||||
|
||||
一阵抽搐过后就下载好了
|
||||
|
||||

|
||||

|
||||
|
||||
::: tip
|
||||
用完之后别忘记给 camera-2018 点个 follow 😋 `呃呃 follow 没用 star 有用`
|
||||
@@ -350,7 +349,7 @@ git clone https://github.com/camera-2018/git-example.git
|
||||
|
||||
如图 我在仓库里新建了 `helloworld.c` 并且写了一些代码
|
||||
|
||||

|
||||

|
||||
|
||||
接下来是提交操作
|
||||
|
||||
@@ -358,7 +357,7 @@ git clone https://github.com/camera-2018/git-example.git
|
||||
git status #看一下文件暂存区
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
红色表示文件没有提交到暂存区 我们要提交
|
||||
|
||||
@@ -368,7 +367,7 @@ git status #看一下文件暂存区
|
||||
git add . #将没有提交的所有文件加入暂存区
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
绿色表示所有文件已加入暂存
|
||||
|
||||
@@ -378,18 +377,18 @@ git commit -m "feat(helloworld): add helloworld file"
|
||||
|
||||
将刚才加入暂区的文件发起了一个提交,提交注释(commit message)是 `feat(helloworld): add helloworld file`
|
||||
|
||||

|
||||

|
||||
|
||||
1. 如果这是你自己的仓库有权限(本人仓库或 Collaborators 有权限的情况下)你就可以直接使用
|
||||
1. 如果这是你自己的仓库有权限(本人仓库或 Collaborators 有权限的情况下)你就可以直接使用
|
||||
|
||||
```bash
|
||||
git push origin main # origin是第四步里remote add起的远程名字
|
||||
# main是分支名
|
||||
git push origin main # origin 是第四步里 remote add 起的远程名字
|
||||
# main 是分支名
|
||||
```
|
||||
|
||||
上传本次提交
|
||||
|
||||

|
||||

|
||||
|
||||
2. 如果你没有本仓库的主分支提交权限 可以提交 PR(Pull Requests)
|
||||
|
||||
@@ -397,7 +396,7 @@ git commit -m "feat(helloworld): add helloworld file"
|
||||
|
||||
首先创建一个新分支 命名为 `yourname-dev`
|
||||
|
||||

|
||||

|
||||
|
||||
然后按照上面的方法 `git clone` 并切换到你刚创建的分支
|
||||
|
||||
@@ -405,75 +404,74 @@ git commit -m "feat(helloworld): add helloworld file"
|
||||
git switch camera-2018-dev
|
||||
```
|
||||
|
||||
然后提交一个文件,这里直接使用 vscode 自带的 git 工具试试 (很方便、不用敲命令行)
|
||||
然后提交一个文件,这里直接使用 vscode 自带的 git 工具试试(很方便、不用敲命令行)
|
||||
|
||||

|
||||

|
||||
|
||||
点暂存所有更改 写好 comment 之后点提交
|
||||
|
||||

|
||||

|
||||
|
||||
最后点同步更改上传
|
||||
|
||||

|
||||

|
||||
|
||||
如果是你提交 在 github 上会显示这个 快捷创建 pr 的按钮
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
点它创建 PR
|
||||
|
||||

|
||||

|
||||
|
||||
这样管理本仓库的人看到 pr 请求就可以 merge 合并辣
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
实际合作过程中可能会出现代码冲突无法 merge 的情况 😋 遇到了自己去 STFW 吧
|
||||
|
||||
**第二种情况:我不是协作者、我什么权限也没有,我看了这个 public 项目后觉得很好但是有一些问题,我要给他贡献一些代码**
|
||||
|
||||
可以点击仓库右上角的 fork
|
||||
可以点击仓库右上角的 fork
|
||||
|
||||

|
||||

|
||||
|
||||
这样会在你的名下多出来一份这个同名仓库,而这个仓库你是拥有所有权限的,你可以 clone 你这个同名仓库,更改代码,提交代码之后
|
||||
|
||||
回到源仓库点击 Pull Request 然后创建 PR `New pull request`
|
||||
|
||||
最上面会提示你说
|
||||
最上面会提示你说
|
||||
|
||||
Comparing changes
|
||||
Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also **compare across forks** .
|
||||
|
||||
点击小蓝字 `compare across forks` 这会让你对比你fork仓库和源代码仓库的提交记录,之后就可以创建 PR 了,原作者看到了会合并。
|
||||
|
||||
点击小蓝字 `compare across forks` 这会让你对比你 fork 仓库和源代码仓库的提交记录,之后就可以创建 PR 了,原作者看到了会合并。
|
||||
|
||||
### 其他功能
|
||||
|
||||
问题跟踪:GitHub的问题跟踪功能可用于报告软件中的问题、错误和功能请求,并进行讨论、分配和解决。
|
||||
问题跟踪:GitHub 的问题跟踪功能可用于报告软件中的问题、错误和功能请求,并进行讨论、分配和解决。
|
||||
|
||||
Wiki页面:用户可以创建和编辑与存储库相关的Wiki页面,用于提供项目的文档、指南、示例代码等。
|
||||
Wiki 页面:用户可以创建和编辑与存储库相关的 Wiki 页面,用于提供项目的文档、指南、示例代码等。
|
||||
|
||||
Pull请求(Pull Requests):使用者可以将自己的代码变更提交给其他项目的所有者,并请求合并到主干代码中。
|
||||
Pull 请求(Pull Requests):使用者可以将自己的代码变更提交给其他项目的所有者,并请求合并到主干代码中。
|
||||
|
||||
项目管理:GitHub提供项目管理功能,包括任务管理、里程碑(milestones)、项目板(project boards)等工具,可用于组织和跟踪项目的进展。
|
||||
项目管理:GitHub 提供项目管理功能,包括任务管理、里程碑(milestones)、项目板(project boards)等工具,可用于组织和跟踪项目的进展。
|
||||
|
||||
部署功能:GitHub可以与各种持续集成和部署(CI/CD)工具集成,帮助开发人员自动化构建、测试和部署他们的应用程序。
|
||||
部署功能:GitHub 可以与各种持续集成和部署(CI/CD)工具集成,帮助开发人员自动化构建、测试和部署他们的应用程序。
|
||||
|
||||
统计信息:GitHub提供有关存储库活动和贡献者的统计信息,例如提交图表、活动日历等,有助于跟踪和分析项目的发展。
|
||||
统计信息:GitHub 提供有关存储库活动和贡献者的统计信息,例如提交图表、活动日历等,有助于跟踪和分析项目的发展。
|
||||
|
||||
社交功能:用户可以关注其他用户、存储库和组织,接收他们的更新和活动通知,并与他们进行交流和讨论。
|
||||
|
||||
代码审核(Code Review):GitHub的Pull请求功能允许团队成员对代码进行审查和讨论,以确保代码质量和最佳实践。
|
||||
代码审核(Code Review):GitHub 的 Pull 请求功能允许团队成员对代码进行审查和讨论,以确保代码质量和最佳实践。
|
||||
|
||||
集成和扩展:GitHub支持与其他工具和服务的集成,例如持续集成(CI)工具、代码质量检查工具、项目管理工具等。
|
||||
集成和扩展:GitHub 支持与其他工具和服务的集成,例如持续集成(CI)工具、代码质量检查工具、项目管理工具等。
|
||||
|
||||
页面托管:GitHub Pages功能使您可以托管静态网站和文档,这在展示和共享项目文档、演示和博客等方面非常有用。
|
||||
页面托管:GitHub Pages 功能使您可以托管静态网站和文档,这在展示和共享项目文档、演示和博客等方面非常有用。
|
||||
|
||||
然后还有一些比如说 Copilot 之类的有用的功能。
|
||||
|
||||
@@ -485,22 +483,23 @@ Copilot 可以根据上下文和输入的提示,为开发人员生成代码建
|
||||
|
||||
你需要学生认证你的 Github 账号。
|
||||
|
||||
地址在 https://education.github.com/students 点击 `Sign up for Global Campus` 来开始认证,下面会让你输入学校,绑定学校邮箱(杭电为 @hdu.edu.cn 结尾的邮箱)(如果你是杭电新生的话,可能要等到智慧杭电账号发放时才能注册杭电邮箱)并上传**学生证明**(从21年开始这个验证越来越严,如果不过的话你可以尝试 `学生证第一页`、`学生证第一页英文翻译(像有道翻译那样 P 图上去)`、`学信网学籍证明英文翻译(英文也是 P 上去)`)
|
||||
地址在 [https://education.github.com/students](https://education.github.com/students) 点击 `Sign up for Global Campus` 来开始认证,下面会让你输入学校,绑定学校邮箱(杭电为 @hdu.edu.cn 结尾的邮箱)(如果你是杭电新生的话,可能要等到智慧杭电账号发放时才能注册杭电邮箱)并上传**学生证明**(从 21 年开始这个验证越来越严,如果不过的话你可以尝试 `学生证第一页`、`学生证第一页英文翻译(像有道翻译那样 P 图上去)`、`学信网学籍证明英文翻译(英文也是 P 上去)`)
|
||||
|
||||
通过了的话你的账户会有 Pro 标识 然后你可以享受的 Github 学生包里包含[这些东西](https://education.github.com/pack)
|
||||
|
||||
里面比较有用的有
|
||||
- JETBRAINS 全家桶的免费用(我没用,我用的是jb自己家的验证方式,不是github)
|
||||
里面比较有用的有
|
||||
|
||||
- JETBRAINS 全家桶的免费用(我没用,我用的是 jb 自己家的验证方式,不是 github)
|
||||
- name.com 家的一个一年期的免费域名(大概价值吧 六七十块钱?)
|
||||
- github 的容量扩容和 actions 时间扩容、Codespaces 时间延长、Pages 扩容(没啥用倒是)
|
||||
- Termius 学生包,这是我很喜欢用的终端软件,有学生包可以多端同步ssh的账号密码啥的,很方便。
|
||||
- Termius 学生包,这是我很喜欢用的终端软件,有学生包可以多端同步 ssh 的账号密码啥的,很方便。
|
||||
- Sentry 容量扩容
|
||||
- Copilot 免费用
|
||||
|
||||
你可以在 `settings` 里看到你的copilot ,配置如下
|
||||
你可以在 `settings` 里看到你的 copilot,配置如下
|
||||
|
||||

|
||||

|
||||
|
||||
然后就可以在你喜欢的 IDE 或编辑器上下载 Copilot 插件,来启用他。
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# 从 CS61A 看编程语言学习
|
||||
|
||||
|
||||
|
||||
# 什么是 CS61A
|
||||
## 什么是 CS61A
|
||||
|
||||
首先声明,本教程的 python 部分很多内容摘自 CS61A
|
||||
|
||||
如果你看过 CS 自学指南,想必你在第一页就看过这样的描述
|
||||
|
||||
> 大一入学时我是一个对计算机一无所知的小白,装了几十个 G 的 Visual Studio 天天和 OJ 你死我活。凭着高中的数学底子我数学课学得还不错,但在专业课上对竞赛大佬只有仰望。提到编程我只会打开那笨重的 IDE,新建一个我也不知道具体是干啥的命令行项目,然后就是 `cin`, `cout`, `for` 循环,然后 CE, RE, WA 循环。当时的我就处在一种拼命想学好但不知道怎么学,课上认真听讲但题还不会做,课后做作业完全是用时间和它硬耗的痛苦状态。我至今电脑里还存着自己大一上学期计算概论大作业的源代码 —— 一个 1200 行的 C++ 文件,没有头文件、没有类、没有封装、没有 unit test、没有 Makefile、没有 Git,唯一的优点是它确实能跑,缺点是“能跑”的补集。我一度怀疑我是不是不适合学计算机,因为童年对于极客的所有想象,已经被我第一个学期的体验彻底粉碎了。<br/>这一切的转机发生在我大一的寒假,我心血来潮想学习 Python。无意间看到知乎有人推荐了 CS61A 这门课,说是 UC Berkeley 的大一入门课程,讲的就是 Python。我永远不会忘记那一天,打开 [CS61A](https://cs61a.org/) 课程网站的那个瞬间,就像哥伦布发现了新大陆一样,我开启了新世界的大门。<br/>我一口气 3 个星期上完了这门课,它让我第一次感觉到原来 CS 可以学得如此充实而有趣,原来这世上竟有如此精华的课程。<br/>为避免有崇洋媚外之嫌,我单纯从一个学生的视角来讲讲自学 CS61A 的体验:<br/>- 独立搭建的课程网站: 一个网站将所有课程资源整合一体,条理分明的课程 schedule、所有 slides, hw, discussion 的文件链接、详细明确的课程给分说明、历年的考试题与答案。这样一个网站抛开美观程度不谈,既方便学生,也让资源公正透明。<br/>- 课程教授亲自编写的教材:CS61A 这门课的开课老师将 MIT 的经典教材 Structure and Interpretation of Computer Programs (SICP) 用 Python 这门语言进行改编(原教材基于 Scheme 语言),保证了课堂内容与教材内容的一致性,同时补充了更多细节,可以说诚意满满。而且全书开源,可以直接线上阅读。<br/>- 丰富到让人眼花缭乱的课程作业:14 个 lab 巩固随堂知识点,10 个 homework,还有 4 个代码量均上千行的 project。与大家熟悉的 OJ 和 Word 文档式的作业不同,所有作业均有完善的代码框架,保姆级的作业说明。每个 Project 都有详尽的 handout 文档、全自动的评分脚本。CS61A 甚至专门开发了一个[自动化的作业提交评分系统](https://okpy.org/)(据说还发了论文)。当然,有人会说“一个 project 几千行代码大部分都是助教帮你写好的,你还能学到啥?”。此言差矣,作为一个刚刚接触计算机,连安装 Python 都磕磕绊绊的小白来说,这样完善的代码框架既可以让你专注于巩固课堂上学习到的核心知识点,又能有“我才学了一个月就能做一个小游戏了!”的成就感,还能有机会阅读学习别人高质量的代码,从而为自己所用。我觉得在低年级,这种代码框架可以说百利而无一害。唯一的害也许是苦了老师和助教,因为开发这样的作业可想而知需要相当的时间投入。<br/>- 每周 Discussion 讨论课,助教会讲解知识难点和考试例题:类似于北京大学 ICS 的小班研讨,但习题全部用 LaTeX 撰写,相当规范且会明确给出 solution。<br/>这样的课程,你完全不需要任何计算机的基础,你只需要努力、认真、花时间就够了。此前那种有劲没处使的感觉,那种付出再多时间却得不到回报的感觉,从此烟消云散。这太适合我了,我从此爱上了自学。<br/>试想如果有人能把艰深的知识点嚼碎嚼烂,用生动直白的方式呈现给你,还有那么多听起来就很 fancy,种类繁多的 project 来巩固你的理论知识,你会觉得他们真的是在倾尽全力想方设法地让你完全掌握这门课,你会觉得不学好它简直是对这些课程建设者的侮辱。<br/>如果你觉得我在夸大其词,那么不妨从 [CS61A](https://cs61a.org/) 开始,因为它是我的梦开始的地方。
|
||||
> 大一入学时我是一个对计算机一无所知的小白,装了几十个 G 的 Visual Studio 天天和 OJ 你死我活。凭着高中的数学底子我数学课学得还不错,但在专业课上对竞赛大佬只有仰望。提到编程我只会打开那笨重的 IDE,新建一个我也不知道具体是干啥的命令行项目,然后就是 `cin`, `cout`, `for` 循环,然后 CE, RE, WA 循环。当时的我就处在一种拼命想学好但不知道怎么学,课上认真听讲但题还不会做,课后做作业完全是用时间和它硬耗的痛苦状态。我至今电脑里还存着自己大一上学期计算概论大作业的源代码 —— 一个 1200 行的 C++ 文件,没有头文件、没有类、没有封装、没有 unit test、没有 Makefile、没有 Git,唯一的优点是它确实能跑,缺点是“能跑”的补集。我一度怀疑我是不是不适合学计算机,因为童年对于极客的所有想象,已经被我第一个学期的体验彻底粉碎了。<br/>这一切的转机发生在我大一的寒假,我心血来潮想学习 Python。无意间看到知乎有人推荐了 CS61A 这门课,说是 UC Berkeley 的大一入门课程,讲的就是 Python。我永远不会忘记那一天,打开 [CS61A](https://cs61a.org/) 课程网站的那个瞬间,就像哥伦布发现了新大陆一样,我开启了新世界的大门。<br/>我一口气 3 个星期上完了这门课,它让我第一次感觉到原来 CS 可以学得如此充实而有趣,原来这世上竟有如此精华的课程。<br/>为避免有崇洋媚外之嫌,我单纯从一个学生的视角来讲讲自学 CS61A 的体验:<br/>- 独立搭建的课程网站:一个网站将所有课程资源整合一体,条理分明的课程 schedule、所有 slides, hw, discussion 的文件链接、详细明确的课程给分说明、历年的考试题与答案。这样一个网站抛开美观程度不谈,既方便学生,也让资源公正透明。<br/>- 课程教授亲自编写的教材:CS61A 这门课的开课老师将 MIT 的经典教材 Structure and Interpretation of Computer Programs (SICP) 用 Python 这门语言进行改编(原教材基于 Scheme 语言),保证了课堂内容与教材内容的一致性,同时补充了更多细节,可以说诚意满满。而且全书开源,可以直接线上阅读。<br/>- 丰富到让人眼花缭乱的课程作业:14 个 lab 巩固随堂知识点,10 个 homework,还有 4 个代码量均上千行的 project。与大家熟悉的 OJ 和 Word 文档式的作业不同,所有作业均有完善的代码框架,保姆级的作业说明。每个 Project 都有详尽的 handout 文档、全自动的评分脚本。CS61A 甚至专门开发了一个[自动化的作业提交评分系统](https://okpy.org/)(据说还发了论文)。当然,有人会说“一个 project 几千行代码大部分都是助教帮你写好的,你还能学到啥?”。此言差矣,作为一个刚刚接触计算机,连安装 Python 都磕磕绊绊的小白来说,这样完善的代码框架既可以让你专注于巩固课堂上学习到的核心知识点,又能有“我才学了一个月就能做一个小游戏了!”的成就感,还能有机会阅读学习别人高质量的代码,从而为自己所用。我觉得在低年级,这种代码框架可以说百利而无一害。唯一的害也许是苦了老师和助教,因为开发这样的作业可想而知需要相当的时间投入。<br/>- 每周 Discussion 讨论课,助教会讲解知识难点和考试例题:类似于北京大学 ICS 的小班研讨,但习题全部用 LaTeX 撰写,相当规范且会明确给出 solution。<br/>这样的课程,你完全不需要任何计算机的基础,你只需要努力、认真、花时间就够了。此前那种有劲没处使的感觉,那种付出再多时间却得不到回报的感觉,从此烟消云散。这太适合我了,我从此爱上了自学。<br/>试想如果有人能把艰深的知识点嚼碎嚼烂,用生动直白的方式呈现给你,还有那么多听起来就很 fancy,种类繁多的 project 来巩固你的理论知识,你会觉得他们真的是在倾尽全力想方设法地让你完全掌握这门课,你会觉得不学好它简直是对这些课程建设者的侮辱。<br/>如果你觉得我在夸大其词,那么不妨从 [CS61A](https://cs61a.org/) 开始,因为它是我的梦开始的地方。
|
||||
|
||||
如果看完了这些,你可能会震惊,会怀疑并且试着打开它并且去尝试这门课程,但是也有可能你会被纯英文的视频或者油管劝退,也有可能你会怀疑我在使用别人的课程体系的前提下仍然要把它放到自己的内容里面的目的,That's all right,我会在下面对她讲的东西进行一定的补充,并且附着上自己的学习建议以及学习思考。
|
||||
如果看完了这些,你可能会震惊,会怀疑并且试着打开它并且去尝试这门课程,但是也有可能你会被纯英文的视频或者油管劝退,也有可能你会怀疑我在使用别人的课程体系的前提下仍然要把它放到自己的内容里面的目的,That's all right,我会在下面对她讲的东西进行一定的补充,并且附着上自己的学习建议以及学习思考。
|
||||
|
||||
# 错误的学习方式
|
||||
## 错误的学习方式
|
||||
|
||||
很多人看到要自学 python 之后,第一时间想到的是,我要去 B 站/百度搜一搜,然后一般搜出来就是菜鸟教程,然后就是一连串枯燥乏味的知识堆叠,或者说是培训班的网课,给我们一种知识好像就是这么枯燥乏味以及自己好像学到了很多但是真的用起来却一无所知痛苦万分的感觉。
|
||||
|
||||
@@ -24,17 +22,17 @@
|
||||
|
||||
是很好。
|
||||
|
||||
但是为什么坚持不下来呢?
|
||||
但是为什么坚持不下来呢?
|
||||
|
||||
根据我的统计,原因无外乎是以下几点:英语太难太劝退了!课程设置太难,好像听懂了但是写起东西来却还是推行不下去!我不知道学了这个能干什么,所以没有动力!学了一两章虽然好像学到了东西,但是感觉有很多东西学了却记不住,导致难度曲线陡增了!<del>游戏太好玩了!</del>
|
||||
|
||||
舒适区看起来很难打破!
|
||||
|
||||
# 正确的思考方式
|
||||
## 正确的思考方式
|
||||
|
||||
面对英语,我们前文有过一而再再而三的提醒过使用正确的工具,但是不得不说的是,在翻译的过程中可能难免丢失了一定的信息,使得我们在困惑中可能变得没有了前进的动力,或者从另一个角度来说我们没有动力是因为没有足够的原因来告诉我们的意识,我们到底应该在做这个超过看菜鸟教程所需精力好多倍的工作,到底能得到除了一点新奇感以外的什么东西,以此来给我们更为充分的理由支撑自己学习完成。
|
||||
|
||||
# 建立正确的认知论
|
||||
## 建立正确的认知论
|
||||
|
||||
编程思想本身远比学习某一门编程语言的具体内容更为重要,我们很多的同学在进行这方面内容的时候总是只想着记忆某一门语言的某些内容,然后学的非常的痛苦。
|
||||
|
||||
@@ -42,12 +40,10 @@
|
||||
|
||||
但是编程语言的设计思想一般不会出现太大的波动,并且就算是发展也有其适配的场景和知识脉络的,如果你乐于去发掘这个知识脉络和思想,那么你就可以优雅的掌握一种思维方式而不是简单的拧螺丝一样的机械化工作。而好的思考方式往往是可以应用在同类的所有语言甚至是在所有的更多的
|
||||
|
||||
# 更有趣的练习方式
|
||||
## 更有趣的练习方式
|
||||
|
||||
我不得不承认的一点是,越痛苦的练习往往可以让你获得更大的提升模拟的成长往往与你面对苦难以及解决他的时间是成正比的。
|
||||
|
||||
不过遗憾的是,我们大多数的同学是在面对跳起来都够不到的反馈的时候,会选择放弃。(除非你有 M 的倾向)
|
||||
|
||||
因此,有时候我们说大道至简。好的东西往往是能让人们快速理解的东西,我觉得 61A 的难度梯度设计,就是兼具了趣味性和间接性的,并且全面优于互联网上的非常多的教程,比硬啃要给我们更多的正反馈(当然,改变思维和学习方式是很重要的。
|
||||
|
||||
#
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
当你开始制作大型项目或者复现论文时,环境配置就开始变得至关重要。
|
||||
|
||||
# 什么是环境?
|
||||
## 什么是环境?
|
||||
|
||||
环境是<strong>包的集合</strong>,我们一般用 Anaconda 来配置虚拟环境。
|
||||
|
||||
@@ -10,42 +10,36 @@
|
||||
|
||||
装下来之后具体操作可以看[安装教程](https://blog.csdn.net/in546/article/details/117400839),如果自动配置环境变量的选项是灰色的话,请按照下面的教程把下面的几个文件路径加入环境变量。
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
在里面添加并写入文件路径加入就好了~
|
||||
|
||||

|
||||

|
||||
|
||||
然后打开 Pycharm,创建新项目,设置按照以下方式操作,记得挂梯子。
|
||||
|
||||
如果不挂梯子,请按照教程配置清华源。[我是教程](https://blog.csdn.net/jasneik/article/details/114227716)
|
||||
|
||||

|
||||

|
||||
|
||||
然后一个新的环境就创建好辣~
|
||||
|
||||
# 如何配置环境
|
||||
## 如何配置环境
|
||||
|
||||
## 1.配置你自己的环境
|
||||
### 1.配置你自己的环境
|
||||
|
||||
你可以尝试命令 `pip install <包的名字>` 或者 `conda install <包的名字>`
|
||||
|
||||
```
|
||||
在下载某个包失败的时候可以查一查有没有人写相关攻略~
|
||||
```
|
||||
> 在下载某个包失败的时候可以查一查有没有人写相关攻略~
|
||||
|
||||
你可以用 `conda list` 查看你这个环境已有的包。你也可以在包的名字后面加上 `==版本号` 来指定版本。
|
||||
|
||||
```
|
||||
请注意各个包之间的依赖关系,否则容易导致无法运行或效果变差!
|
||||
```
|
||||
> 请注意各个包之间的依赖关系,否则容易导致无法运行或效果变差!
|
||||
|
||||
## 2.复现论文代码时配置环境
|
||||
### 2.复现论文代码时配置环境
|
||||
|
||||
```
|
||||
一般我们可以在Github的README中找到环境的配置方法,遇到难以下载的特殊版本包时可以考虑下载它的源码手动编译,具体流程不展开了,可以自行搜索
|
||||
```
|
||||
> 一般我们可以在 Github 的 README 中找到环境的配置方法,遇到难以下载的特殊版本包时可以考虑下载它的源码手动编译,具体流程不展开了,可以自行搜索
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 安装 python
|
||||
|
||||
教程
|
||||
::: warning 😍 教程
|
||||
|
||||
[Python 小白必看,非常生动的 Pycharm 与 Anaconda 安装教学,干货!_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1Bp4y117UW)
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
[Win10 下 Conda-Pycharm-Pytorch 的安装_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV15U4y1J7Ss)
|
||||
|
||||
<Bilibili bvid='BV15U4y1J7Ss'/>
|
||||
:::
|
||||
|
||||
# 巧妇难为无米之炊
|
||||
## 巧妇难为无米之炊
|
||||
|
||||
你可以在终端上用 Python 解释器
|
||||
|
||||
@@ -20,9 +21,9 @@
|
||||
|
||||
我们推荐你都进行一波尝试,同样这也会作为一个任务
|
||||
|
||||
# 解释器
|
||||
::: warning <font size=5>😐 解释器</font>
|
||||
|
||||
# Windows 用户
|
||||
<font size=5><strong>Windows 用户</strong></font>
|
||||
|
||||
打开 [Python 官方网站](https://www.python.org/),找到“Download”里的“Latest: Python 3.x.y”。
|
||||
|
||||
@@ -30,17 +31,17 @@
|
||||
|
||||
<strong>注意:windows11 安装好后 命令行输入 python 可能会跳到 Microsoft 应用商店 可在 customize installation(自定义安装)next 勾选 install for all users</strong>
|
||||
|
||||
## GNU/Linux 系统
|
||||
<font size=5><strong>GNU/Linux 系统</strong></font>
|
||||
|
||||
在终端输入 `sudo apt install python3` 即可完成 Python3 的全部安装流程
|
||||
|
||||
可以输入 `python3 --version` 检验是否成功。
|
||||
:::
|
||||

|
||||
|
||||

|
||||
::: warning 🤔 Jupyter Notebook
|
||||
|
||||
# Jupyter Notebook
|
||||
|
||||
[官方网站](https://jupyter.org/) Jupyter Notebook 是基于网页的用于交互计算的应用程序。其可被应用于全过程计算:开发、文档编写、运行代码和展示结果。——[Jupyter ](https://link.zhihu.com/?target=https%3A//jupyter-notebook.readthedocs.io/en/stable/notebook.html)
|
||||
[官方网站](https://jupyter.org/) Jupyter Notebook 是基于网页的用于交互计算的应用程序。其可被应用于全过程计算:开发、文档编写、运行代码和展示结果。——[Jupyter](https://link.zhihu.com/?target=https%3A//jupyter-notebook.readthedocs.io/en/stable/notebook.html)
|
||||
|
||||
你在一个框框中直接输入代码,运行,它立马就在下面给你输出。怎么样,是不是很酷?
|
||||
|
||||
@@ -53,7 +54,8 @@
|
||||
jupyter notebook
|
||||
|
||||
进行使用
|
||||
:::
|
||||
|
||||

|
||||

|
||||
|
||||
[Pycharm](https://www.jetbrains.com/zh-cn/pycharm/):可能很多同学已经用上了,我在这里不做更多解释
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# 阶段零:Python 解释器
|
||||
|
||||
可参考资料
|
||||
::: warning 😍 可参考资料
|
||||
|
||||
[官方文档](https://wiki.python.org/moin/BeginnersGuide)
|
||||
|
||||
[菜鸟教程](https://www.runoob.com/python3/python3-interpreter.html)
|
||||
:::
|
||||
|
||||
你可以在终端与解释器进行交互
|
||||
|
||||
@@ -14,65 +15,44 @@
|
||||
|
||||
你可以自己把玩一下
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 1 + 2
|
||||
```
|
||||
|
||||
```
|
||||
3
|
||||
```
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 3 - 2
|
||||
```
|
||||
|
||||
```
|
||||
1
|
||||
```
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 5 * 6
|
||||
```
|
||||
|
||||
```
|
||||
30
|
||||
```
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 7 / 4
|
||||
```
|
||||
|
||||
```
|
||||
1.75
|
||||
```
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 7 // 4
|
||||
```
|
||||
|
||||
```
|
||||
1
|
||||
```
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 7 % 4
|
||||
```
|
||||
|
||||
```
|
||||
3
|
||||
```
|
||||
|
||||
```
|
||||
>>> 4**3
|
||||
```
|
||||
|
||||
```
|
||||
```python
|
||||
>>> 4 ** 3
|
||||
64
|
||||
```
|
||||
|
||||
同时可以输入 `exit``()` 或按 Ctrl+D 退出交互
|
||||
|
||||
同学们可能已经发现 python 这门编程语言的神奇之处了
|
||||
:::: warning 🤔 同学们可能已经发现 python 这门编程语言的神奇之处了
|
||||
|
||||
在这里留一个思考题
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# 阶段一:熟悉语句
|
||||
|
||||
在进行本章之前,请你谨记一个原则:基本所有的功能都被人提前实现好了
|
||||
::: warning 🐱 在进行本章之前,请你谨记一个原则:基本所有的功能都被人提前实现好了
|
||||
|
||||
你需要关心的仅仅是逻辑该如何设立
|
||||
|
||||
在做本章任务前,请熟悉 python 的函数,循环和判断语句即可
|
||||
:::
|
||||
|
||||
P1:请仅使用一行语句求出三个数的最小平方和
|
||||
|
||||
@@ -30,7 +31,7 @@ def two_of_three(x, y, z):
|
||||
return _____
|
||||
```
|
||||
|
||||
提示:可以使用 max()函数哦
|
||||
提示:可以使用 `min()` 函数哦
|
||||
|
||||
P2:下降阶乘
|
||||
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
|
||||
什么是递归呢?
|
||||
|
||||

|
||||

|
||||
|
||||
#
|
||||
|
||||
# 释义
|
||||
## 释义
|
||||
|
||||
递归是在函数主体中重复调用函数的基本方案
|
||||
|
||||
让我们来看一个经典的例子
|
||||
|
||||
> 阶乘,即 n!=n*(n-1)*......*2*1<br/>例如: `5! = 5 * 4 * 3 * 2 * 1 = 120`.
|
||||
> 阶乘,即 n! =n \* (n - 1) \*...... \* 2 \* 1<br/>例如:5! = 5 \* 4 \* 3 \* 2 \* 1 = 120.
|
||||
|
||||
而阶乘的代码如下编辑
|
||||
|
||||
@@ -29,9 +27,9 @@ def factorial(n):
|
||||
- 想想在最简单的情况下函数将如何跳转
|
||||
- 考虑使用问题的更简单版本来进行解决问题
|
||||
|
||||
# 任务
|
||||
## 任务
|
||||
|
||||
P4:编写一个递归函数 `skip_add`,它接受一个参数 n 并返回 `n + n-2 + n-4 + n-6 + ... + 0`。假设 n 是非负数。
|
||||
P4:编写一个递归函数 `skip_add`,它接受一个参数 n 并返回 `n + n-2 + n-4 + n-6 +...+ 0`。假设 n 是非负数。
|
||||
|
||||
```python
|
||||
def skip_add(n):
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# 阶段三:数据抽象
|
||||
|
||||
数据抽象(Data Abstraction)
|
||||
数据抽象 (Data Abstraction)
|
||||
|
||||
[可参考教程](https://zhuanlan.zhihu.com/p/343133774)
|
||||
::: warning 🐱 [可参考教程](https://zhuanlan.zhihu.com/p/343133774)
|
||||
|
||||
各位需要认真了解以下内容,他们是构建任何大厦的基石
|
||||
:::
|
||||
|
||||
# Data Abstraction
|
||||
## Data Abstraction
|
||||
|
||||
数据抽象是一个伟大的概念,它允许程序员将代码以对象的形式进行看待,并且从更高的层面去审视问题。
|
||||
|
||||
@@ -14,14 +15,14 @@
|
||||
|
||||
举个例子:你在开车时,如果要控制发动机的活塞怎么动,对你来说是否有些太过于困难了。因此将其抽象成了离合器,油门,刹车这些较为简单的操作。
|
||||
|
||||
# 组成
|
||||
## 组成
|
||||
|
||||
一个抽象的数据类型(ADT)由两个主要部分组成
|
||||
|
||||
- Constructors:架构抽象数据类型的主要函数
|
||||
- Selectors:操作数据类型的各式方法
|
||||
|
||||
# 列表与元组
|
||||
## 列表与元组
|
||||
|
||||
列表是可以存储多个元素的 Python 数据结构。每个元素可以是任何类型,甚至可以是另一个列表!
|
||||
|
||||
@@ -37,12 +38,12 @@
|
||||
|
||||
```python
|
||||
tup = (1, 2, 3, 4)
|
||||
new_tup = tup + (5, ) # 创建新的元组new_tup,并依次填充原元组的值
|
||||
new_tup = tup + (5, ) # 创建新的元组 new_tup,并依次填充原元组的值
|
||||
new _tup
|
||||
(1, 2, 3, 4, 5)
|
||||
|
||||
l = [1, 2, 3, 4]
|
||||
l.append(5) # 添加元素5到原列表的末尾
|
||||
l.append(5) # 添加元素 5 到原列表的末尾
|
||||
l
|
||||
[1, 2, 3, 4, 5]
|
||||
```
|
||||
@@ -69,13 +70,14 @@ l
|
||||
3
|
||||
```
|
||||
|
||||
思考题:
|
||||
::: warning 🤔 思考题:
|
||||
|
||||
列表和元组在性能上有什么差异呢?
|
||||
|
||||
他们对应的使用场景有哪些呢?
|
||||
:::
|
||||
|
||||
# 字典与集合
|
||||
## ;ltyi 字典与集合
|
||||
|
||||
字典是一系列由键(key)和值(value)配对组成的元素的集合,在 Python3.7+,字典被确定为有序
|
||||
|
||||
@@ -112,21 +114,22 @@ d
|
||||
{'name': 'jason', 'age': 20, 'gender': 'male'}
|
||||
|
||||
s = {1, 2, 3}
|
||||
s.add(4) # 增加元素4到集合
|
||||
s.add(4) # 增加元素 4 到集合
|
||||
s
|
||||
{1, 2, 3, 4}
|
||||
s.remove(4) # 从集合中删除元素4
|
||||
s.remove(4) # 从集合中删除元素 4
|
||||
s
|
||||
{1, 2, 3}
|
||||
```
|
||||
|
||||
思考题:
|
||||
::: warning 🤔 思考题:
|
||||
|
||||
字典和集合分别是什么原理呢?
|
||||
|
||||
字典可以是一个列表吗?为什么?
|
||||
:::
|
||||
|
||||
# 可变性
|
||||
## 可变性
|
||||
|
||||
我们说如果一个对象可以由代码进行操作而改变那么我们称其具有可变性。
|
||||
|
||||
@@ -176,15 +179,16 @@ False
|
||||
True
|
||||
```
|
||||
|
||||
思考题,你能否从指针的角度去理解可变性呢?
|
||||
::: warning 🤔 思考题,你能否从指针的角度去理解可变性呢?
|
||||
:::
|
||||
|
||||
# 任务
|
||||
## 任务
|
||||
|
||||
P7:9*9 乘法表
|
||||
P7:9*9 乘法表
|
||||
|
||||
可能现在对你来说,构建像下图这样的 99 乘法表已经是非常容易的一件事了,可是如果我要求你使用 python 的列表推导式(list comprehension),在两行以内完成呢?
|
||||
可能现在对你来说,构建像下图这样的 99 乘法表已经是非常容易的一件事了,可是如果我要求你使用 python 的列表推导式 (list comprehension),在两行以内完成呢?
|
||||
|
||||

|
||||

|
||||
|
||||
P8:couple 情侣
|
||||
|
||||
@@ -235,40 +239,40 @@ make_city(name, lat, lon)
|
||||
|
||||
```python
|
||||
def make_city(name, lat, lon):
|
||||
<em>"""</em>
|
||||
<em> >>> city = make_city('Berkeley', 0, 1)</em>
|
||||
<em> >>> get_name(city)</em>
|
||||
<em> 'Berkeley'</em>
|
||||
<em> >>> get_lat(city)</em>
|
||||
<em> 0</em>
|
||||
<em> >>> get_lon(city)</em>
|
||||
<em> 1</em>
|
||||
<em> """</em>
|
||||
<em> </em>return [name, lat, lon]
|
||||
"""
|
||||
>>> city = make_city('Berkeley', 0, 1
|
||||
>>> get_name(city)
|
||||
'Berkeley
|
||||
>>> get_lat(city)
|
||||
0
|
||||
>>> get_lon(city)
|
||||
1
|
||||
"""
|
||||
return [name, lat, lon]
|
||||
|
||||
def get_name(city):
|
||||
<em>"""</em>
|
||||
<em> >>> city = make_city('Berkeley', 0, 1)</em>
|
||||
<em> >>> get_name(city)</em>
|
||||
<em> 'Berkeley'</em>
|
||||
<em> """</em>
|
||||
<em> </em>return city[0]
|
||||
"""
|
||||
>>> city = make_city('Berkeley', 0, 1)
|
||||
>>> get_name(city)
|
||||
'Berkeley'
|
||||
"""
|
||||
return city[0]
|
||||
|
||||
def get_lat(city):
|
||||
<em>"""</em>
|
||||
<em> >>> city = make_city('Berkeley', 0, 1)</em>
|
||||
<em> >>> get_lat(city)</em>
|
||||
<em> 0</em>
|
||||
<em> """</em>
|
||||
<em> </em>return city[1]
|
||||
"""
|
||||
>>> city = make_city('Berkeley', 0, 1)
|
||||
>>> get_lat(city)
|
||||
0
|
||||
"""
|
||||
return city[1]
|
||||
|
||||
def get_lon(city):
|
||||
<em>"""</em>
|
||||
<em> >>> city = make_city('Berkeley', 0, 1)</em>
|
||||
<em> >>> get_lon(city)</em>
|
||||
<em> 1</em>
|
||||
<em> """</em>
|
||||
<em> </em>return city[2]
|
||||
"""
|
||||
>>> city = make_city('Berkeley', 0, 1)
|
||||
>>> get_lon(city)
|
||||
1
|
||||
"""
|
||||
return city[2]
|
||||
```
|
||||
|
||||
首先你试试求出两个地方的距离。
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
# 阶段四:高阶函数
|
||||
|
||||
阅读以及完成本部分内容可以帮助你有效减少代码冗余。
|
||||
::: warning 🐱 阅读以及完成本部分内容可以帮助你有效减少代码冗余。
|
||||
|
||||
让你完成更为优雅的代码
|
||||
|
||||
各位要记住的是
|
||||
|
||||
# 代码首先是给人看的
|
||||
<font size=5><strong>代码首先是给人看的</strong></font>
|
||||
|
||||
机器看的永远只是你的机器码。
|
||||
|
||||
可参考教程 [Lambda](https://zhuanlan.zhihu.com/p/80960485)
|
||||
:::
|
||||
|
||||
# Lambda 介绍
|
||||
## Lambda 介绍
|
||||
|
||||
Lambda 表达式是通过指定两件事来评估函数的表达式:参数和返回表达式。
|
||||
|
||||
请尝试阅读以下英文表格,对比函数与 lambda 表达式的不同
|
||||
|
||||
# Lambda 实验
|
||||
## Lambda 实验
|
||||
|
||||
以下代码 python 会显示什么?通过对这些代码的实验,加深你对代码的学习
|
||||
|
||||
@@ -74,7 +75,7 @@ ______
|
||||
______
|
||||
```
|
||||
|
||||
# 任务
|
||||
## 任务
|
||||
|
||||
P9:我们发现以下两个函数看起来实现的非常相似,是否可以进行改进,将其整合?
|
||||
|
||||
@@ -113,11 +114,11 @@ def is_prime(n):
|
||||
|
||||
需求:
|
||||
|
||||
你需要通过自己写一个函数: `count_cond` ,来接受一个含有两个参数的函数 `condition(n, i)`(使用lambda表达式),
|
||||
你需要通过自己写一个函数: `count_cond` ,来接受一个含有两个参数的函数 `condition(n, i)`(使用 lambda 表达式),
|
||||
|
||||
且`condition`函数应该满足第一个参数为N,而第二个参数将会在`condition`函数中遍历 1 to N。
|
||||
且`condition`函数应该满足第一个参数为 N,而第二个参数将会在`condition`函数中遍历 1 to N。
|
||||
|
||||
`count_cond` 将返回一个单参数函数(ps:一个匿名函数),此单参数函数将会在被调用时返回 1 to N 中所有满足`condition`的数字的个数(如:1到n中素数的个数)。
|
||||
`count_cond` 将返回一个单参数函数 (ps:一个匿名函数),此单参数函数将会在被调用时返回 1 to N 中所有满足`condition`的数字的个数 (如:1 到 n 中素数的个数)。
|
||||
|
||||
```python
|
||||
def count_cond(condition):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 阶段五:迭代生成
|
||||
|
||||
# 前言
|
||||
## 前言
|
||||
|
||||
在写乘法表的时候,你可能写过类似
|
||||
|
||||
@@ -18,13 +18,13 @@ for (int i = 0; i < n; i ++)
|
||||
|
||||
但是,你想过 Python 在处理 for in 语句的时候,具体发生了什么吗?什么样的对象可以被 for in 来枚举呢?
|
||||
|
||||
# 容器迭代
|
||||
## 容器迭代
|
||||
|
||||
容器这个概念非常好理解。
|
||||
|
||||
在 Python 中一切皆对象,对象的抽象就是类,而对象的集合就是容器。
|
||||
|
||||
列表(list: [0, 1, 2]),元组(tuple: (0, 1, 2)),字典(dict: {0:0, 1:1, 2:2}),集合(set: set([0, 1, 2]))都是容器。
|
||||
列表`list: [0, 1, 2]`,元组`tuple: (0, 1, 2)`,字典`dict: {0:0, 1:1, 2:2}`,集合`set: set([0, 1, 2])`都是容器。
|
||||
|
||||
对于容器,你可以很直观地想象成多个元素在一起的单元;而不同容器的区别,正是在于内部数据结构的实现方法。
|
||||
|
||||
@@ -74,11 +74,11 @@ StopIteration
|
||||
[1, 2, 3, 4]
|
||||
```
|
||||
|
||||
# 英语练习,对迭代器的类比
|
||||
## 英语练习,对迭代器的类比
|
||||
|
||||
<strong>Analogy</strong>: An iterable is like a book (one can flip through the pages) and an iterator for a book would be a bookmark (saves the position and can locate the next page). Calling `iter` on a book gives you a new bookmark independent of other bookmarks, but calling `iter` on a bookmark gives you the bookmark itself, without changing its position at all. Calling `next` on the bookmark moves it to the next page, but does not change the pages in the book. Calling `next` on the book wouldn't make sense semantically. We can also have multiple bookmarks, all independent of each other.
|
||||
|
||||
# 生成器:懒人迭代器!
|
||||
## 生成器:懒人迭代器!
|
||||
|
||||
```python
|
||||
def test_iterator():
|
||||
@@ -114,7 +114,8 @@ Wall time: 12.5 s
|
||||
|
||||
声明一个迭代器很简单,[i for i in range(100000000)]就可以生成一个包含一亿元素的列表。每个元素在生成后都会保存到内存中,你通过代码可以看到,它们占用了巨量的内存,内存不够的话就会出现 OOM 错误。
|
||||
|
||||
了解下 yield()函数吧,他可以返回一个生成器对象,试试看懂这个
|
||||
::: warning 🤔 了解下 yield()函数吧,他可以返回一个生成器对象,试试看懂这个
|
||||
:::
|
||||
|
||||
```python
|
||||
>>> def gen_list(lst):
|
||||
@@ -133,7 +134,7 @@ Wall time: 12.5 s
|
||||
StopIteration
|
||||
```
|
||||
|
||||
# 思考题:python 会显示什么?为什么?
|
||||
## 思考题:python 会显示什么?为什么?
|
||||
|
||||
```python
|
||||
>>> s = [1, 2, 3, 4]
|
||||
@@ -185,9 +186,9 @@ ______
|
||||
______
|
||||
```
|
||||
|
||||
# 任务
|
||||
## 任务
|
||||
|
||||
P10:实现 `count`,它接受一个迭代器 `t` 并返回该值 `x` 出现在 的第一个 n 个元素中的次数 `t`
|
||||
P10:实现 `count`,它接受一个迭代器 `t` 并返回该值 `x` 出现在的前 n 个元素中的次数 `t`
|
||||
|
||||
```python
|
||||
def count(t, n, x):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 结语
|
||||
|
||||
# 感觉学了不少内容,又感觉什么都没学?
|
||||
## 感觉学了不少内容,又感觉什么都没学?
|
||||
|
||||
确实,这也是学习过程中一个非常普遍存在的状态,你学了五个非常重要的 python 语言的特性,但是在你用他们真正来优化代码解决代码之前,你都无法真正的掌握他们。
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
同时,python 的核心特性就这些吗?远远不止呢,这些只是你入手其的敲门砖,我在下面会列举一些别的特性,你可以自行前去了解,也可以等到你真正遇到问题的时候去思考?
|
||||
|
||||
# 为什么没有突出面向对象
|
||||
## 为什么没有突出面向对象
|
||||
|
||||
因为代码量实在是太少了,当你去理解面向对象的时候的伟大意义时,最好与 C 语言这种面向过程的语言进行对比,不过,如果你完成了我们的文字冒险小游戏的时候,你可能会有非常深刻的体验。
|
||||
|
||||
# 还有什么是我值得注意的?
|
||||
## 还有什么是我值得注意的?
|
||||
|
||||
这些内容值得你去学习与思考,但是碍于各种因素,我暂时没有详细介绍
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
|
||||
如果有机会,我会继续补充相关内容
|
||||
|
||||
值得一提的是:后面人工智能模块,我们将以 python 为主去进行编程,这对完成了所有任务的你一定是 a piece of cake!
|
||||
值得一提的是:后面人工智能模块,我们将以 python 为主去进行编程,这对完成了所有任务的你一定是 a piece of cake!
|
||||
|
||||
# 一些不错的补充
|
||||
## 一些不错的补充
|
||||
|
||||
[WTF for python](https://github.com/robertparley/wtfpython-cn)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Python for fun
|
||||
|
||||
值得一提的是,Python 是面向对象的编程语言,与你之前书写的 C 语言(面向过程)有非常多的不同。
|
||||
值得一提的是,Python 是面向对象的编程语言,与你之前书写的 C 语言(面向过程)有非常多的不同。
|
||||
|
||||
我们为什么要选择要使用 Python 来首先讲解面向对象这个概念?
|
||||
|
||||
|
||||
426
3.编程思维体系构建/3.6.5.1CS61A Sec1.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# CS61A Sec1
|
||||
|
||||
<strong>观前须知:</strong>
|
||||
|
||||
本章节内容基于 Berkeley 大学的教材 [Composing Programs](http://www.composingprograms.com/) by [John DeNero](http://www.denero.org/),并在此基础上做了部分修改,是在[知识共享协议](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh)下所许可的。
|
||||
|
||||
计算机科学的高生产力之所以可能,是因为该学科是建立在一套优雅而强大的基本思想之上。所有的程序都从信息的表示开始,然后寻找一种逻辑来处理这些信息,并设计抽象概念来解释和控制这种逻辑。有了这些认识后,我们就需要准确的理解计算机是如何解释我们写的程序并进行计算的。
|
||||
|
||||
> A language isn't something you learn so much as something you join.<br/>—[Arika Okrent](http://arikaokrent.com/)
|
||||
|
||||
为了定义计算过程,我们需要一种编程语言;最好是许多人类和大量的计算机都能理解的语言。所以在 cs61a 中,伯克利主要使用 Python 语言来进行教学。
|
||||
|
||||
(在之前的 cs61a 课程中,Berkeley 大学主要使用 Scheme 来进行教学,(可能会写一篇文章来说说 Python 和 Scheme 语言和编程上的区别?) 不过在现在的课程中还是有关于 Scheme 的内容,所以并不用太过伤心?🙄)
|
||||
|
||||
## 学习目标
|
||||
|
||||
在这一部分我们要学习的内容主要是函数(Functions)和控制(Control)
|
||||
|
||||
### 一个简单的例子
|
||||
|
||||
为了给 Python 一个适当的介绍,我们将从一个使用几种语言特征的例子开始。
|
||||
|
||||
Python 内置了对广泛的常见编程的支持,如操作文本、显示图形和通过互联网进行通信。
|
||||
|
||||
```python
|
||||
from urllib.request import urlopen
|
||||
```
|
||||
|
||||
这个 Python 代码是一个导入语句,加载了在互联网上访问数据的功能。实际上,它提供了一个叫做 urlopen 的函数,它可以在一个[统一资源定位符(URL)](https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL)上访问内容,可以通过它来访问互联网上的数据。
|
||||
|
||||
<strong>语句和表达式</strong>
|
||||
|
||||
Python 代码由语句和表达式组成。大体上,计算机程序由以下指令组成
|
||||
|
||||
1. 计算一些值
|
||||
2. 进行一些操作
|
||||
|
||||
语句通常描述行动;当 Python 解释器执行一个语句时,它执行相应的动作。另一方面,表达式通常描述的是计算;当 Python 评估一个表达式时,它计算该表达式的值。在这篇文章下,介绍了几种类型的声明和表达方式。
|
||||
|
||||
<strong>赋值语句</strong>
|
||||
|
||||
```python
|
||||
shakespeare = urlopen('http://www.composingprograms.com/shakespeare.txt')
|
||||
```
|
||||
|
||||
注意:在伯克利大学的教材中,上述代码中的 url 并没有添加"www.",导致现在(至少在写这篇文章的时候)无法打开原文中的 url(可能还会写一篇文章来讲解"www."?)
|
||||
|
||||
将变量名 `shakespeare` 用 `=` 和后面的表达式的值联系起来。该表达式将 `urlopen` 函数应用于一个 URL,该 URL 包含威廉 - 莎士比亚 37 部戏剧的完整文本,全部保存在一个文本文件中。
|
||||
|
||||
<strong>函数</strong>
|
||||
|
||||
函数封装了操作数据的逻辑。
|
||||
|
||||
这句话告诉我们,可以从两个角度来看函数:
|
||||
|
||||
- 在调用函数的时候,我们关注的是要处理的数据
|
||||
- 在定义函数的时候,我们关注的是如何处理数据
|
||||
|
||||
```python
|
||||
shakespeare = urlopen('http://www.composingprograms.com/shakespeare.txt')
|
||||
```
|
||||
|
||||
`urlopen` 是一个函数。一个网络地址是一种数据,而莎士比亚戏剧的文本是另一种数据。从网络地址到文本的过程可能很复杂,但我们可以只用一个简单的表达式来应用这个过程,因为这个复杂性被藏在一个函数中。
|
||||
|
||||
你可能不了解 `urlopen` 这个函数背后的逻辑,但这不影响你去调用这个函数,这就是函数封装的好处之一。
|
||||
|
||||
<strong>因此,函数是本章节关注的重点。</strong>
|
||||
|
||||
我们来看另一个赋值语句:
|
||||
|
||||
```python
|
||||
words = set(shakespeare.read().decode().split())
|
||||
```
|
||||
|
||||
这个语句将名字词与莎士比亚戏剧中出现的所有出现过的词(重复出现的词只统计一次)的集合联系起来,其中有 33,721(?) 个词。上述语句包含一个读取、解码和分割的命令链,每个命令都在一个中间计算实体上操作:我们从打开的 URL 中读取数据,然后将数据解码成文本,最后将文本分割成单词。所有这些词都被放在一个集合(Set,Python 中的一种数据类型)中。
|
||||
|
||||
<strong>对象</strong>
|
||||
|
||||
前文中提到的 Set,不仅仅是数据类型,也是一个对象。对象用一种能同时处理两者复杂性的方式,把数据和操作该数据的逻辑无缝衔接在一起。
|
||||
|
||||
对象会是我们后面章节所要讨论的内容。
|
||||
|
||||
现在让我们来看这个例子中的最后一个语句:
|
||||
|
||||
```python
|
||||
>>> {w for w in words if len(w) == 6 and w[::-1] in words}
|
||||
{'redder', 'drawer', 'reward', 'diaper', 'repaid'}
|
||||
```
|
||||
|
||||
第一行的">>>"表示输入,第二行则是交互式会话的输出
|
||||
|
||||
这是一个复合表达式,其值是所有长度为 6 的、本身和反向拼写都在原集合中的词组成的集合。其中的 `w[::-1]` 是一种隐式表达,它枚举了 `w` 中的所有字母,但因为 `step = -1` 规定了步长是反方向的。
|
||||
|
||||
<strong>解释器</strong>
|
||||
|
||||
计算复合表达式需要一个精确的程序,以可预测的方式解释代码。一个能实现程序和计算符合表达式的程序被称为解释器;没错,其实解释器是程序(可能再写一篇文章来讲讲解释器和编译器的区别?)
|
||||
|
||||
与其他计算机程序相比,编程语言的解释器在通用性方面是独一无二的。Python 的设计没有考虑到莎士比亚,然而,它的不可思议的灵活性使我们能够只用几个语句和表达式来处理大量的文本。
|
||||
|
||||
最后,我们会发现所有这些核心概念都是密切相关的:函数是对象,对象是函数,而解释器是两者的实例。然而,要掌握编程的艺术,关键是要清楚地理解每个概念及其在组织代码中的作用。
|
||||
|
||||
解释器的设计和实现也是我们之后的主要议题。
|
||||
|
||||
### 编程原本
|
||||
|
||||
编程语言不仅仅是指示计算机执行任务的一种手段,同时也是一个框架,我们在这个框架内组织我们关于计算过程的想法。程序的作用是在编程社区的成员之间交流这些想法,所以,编写的程序必须让人们容易阅读,而且只是顺便让机器执行。
|
||||
|
||||
当我们描述一种语言时,我们应该特别注意该语言为结合简单的想法以形成更复杂的想法所提供的手段。
|
||||
|
||||
每种强大的语言都有三种这样的机制:
|
||||
|
||||
- <strong>原始的表达式和语句</strong>,代表了该语言提供的最简单的构建模块。
|
||||
- <strong>组合的方式</strong>,由较简单的元素建立成复合元素。
|
||||
- <strong>抽象的手段</strong>,通过它,复合元素可以作为单位被命名和操作。
|
||||
|
||||
在编程中,我们处理两种元素:函数和数据。(很快就会发现,它们其实并不那么明显)。不那么正式地说,数据是我们想要操作的东西,而函数描述了操作数据的规则。因此,任何强大的编程语言都应该能够描述原始数据和原始函数,以及有一些方法来组合和抽象函数和数据。
|
||||
|
||||
在上一小节中对 Python 解释器进行了实验后,我们现在重新开始,有条不紊地逐个开发 Python 语言元素。如果例子看起来很简单,那就耐心一点,因为更多令人兴奋的点很快就会出现。
|
||||
|
||||
我们从原始表达式开始。一种原始表达式是数字。更确切地说,你输入的表达式由代表十进制的数字组成。
|
||||
|
||||
```python
|
||||
>>> 42
|
||||
42
|
||||
```
|
||||
|
||||
代表数字的表达式可以与数学运算符相结合,形成一个复合表达式,解释器将对其进行计算。
|
||||
|
||||
```python
|
||||
>>> -1 - -1
|
||||
0
|
||||
>>> 1/2 + 1/4 + 1/8 + 1/16 + 1/32 + 1/64 + 1/128
|
||||
0.9921875
|
||||
```
|
||||
|
||||
这些数学表达式使用<em>中缀</em>符号,其中<em>运算符</em>(例如,+,-,*,或/)出现在<em>操作数</em>(数字)之间。Python 包括许多形成复合表达式的方法。我们不会试图立即列举它们,而是会随着我们的学习引入新的表达形式,以及它们所支持的语言特性。
|
||||
|
||||
最重要的一种复合表达式是<em>调用表达式</em>,它将一个函数应用于一些参数。回顾一下代数,函数的数学概念是一个从一些自变量到因变量的映射。例如,一个求最大值的函数将其的多个输入映射到当中最大值的一个单一的输出。Python 表达函数应用的方式与传统数学中相同。
|
||||
|
||||
```python
|
||||
>>> max(7.5, 9.5)
|
||||
9.5
|
||||
```
|
||||
|
||||
这个调用表达式有子表达式:<em>操作符</em>是括号前的表达式,它包含了一个用逗号分隔的<em>操作数</em>列表。
|
||||
|
||||

|
||||
|
||||
运算符指定了一个<em>函数</em>。当这个调用表达式被评估时,我们说对<em>参数</em>`7.5` 和 `9.5`<em>调用</em>函数 `max`,并<em>返回</em>一个 9.5 的<em>返回值</em>。
|
||||
|
||||
调用表达式中参数的顺序很重要。例如,函数 `pow` 计算第一个参数的第二个参数次方。
|
||||
|
||||
```python
|
||||
>>> pow(100, 2)
|
||||
10000
|
||||
>>> pow(2, 100)
|
||||
1267650600228229401496703205376
|
||||
```
|
||||
|
||||
与中缀表示法的数学约定相比,函数表示法有三个主要优点。首先,函数可以接受任意数量的参数:
|
||||
|
||||
```python
|
||||
>>> max(1, -2, 3, -4)
|
||||
3
|
||||
```
|
||||
|
||||
不会产生歧义,因为函数名总是优先于其参数。
|
||||
|
||||
此外,函数符号以一种直接的方式延伸到<em>嵌套表达式</em>,其中的元素本身就是复合表达式。在嵌套的调用表达式中,与复合的中缀表达式不同,嵌套的结构在括号中是完全明确的。
|
||||
|
||||
```python
|
||||
>>> max(min(1, -2), min(pow(3, 5), -4))
|
||||
-2
|
||||
```
|
||||
|
||||
对于这种嵌套的深度以及 Python 解释器可以计算的表达式的整体复杂性,(原则上)没有限制。然而,人类很快就会被多级嵌套所迷惑。作为一个程序员,你的一个重要作用是构造表达式,使它们仍然可以由你自己、你的编程伙伴和其他将来可能阅读你的表达式的人来解释。
|
||||
|
||||
同时,数学符号有各种各样的形式:乘法出现在术语之间,指数显示为上标,除法显示为斜杠,平方根显示为有斜边的屋顶。其中一些符号是很难打出来的!然而,所有这些复杂性都可以通过调用表达式的符号来统一。虽然 Python 支持使用中缀表达式的常见数学运算符(如 `+` 和 `-`),但任何运算符都可以表示为一个有名称的函数。
|
||||
|
||||
### 导入库函数
|
||||
|
||||
Python 定义了大量的函数,包括上一节中提到的运算符函数,但默认不提供它们的所有名称。作为替代,它将函数和其他量组织到模块中,这些模块共同构成了 Python 库。为了使用这些元素,人们将它们导入。例如,数学模块提供了各种熟悉的数学相关的函数。
|
||||
|
||||
```python
|
||||
>>> from math import sqrt
|
||||
>>> sqrt(256)
|
||||
16.0
|
||||
```
|
||||
|
||||
而运算符模块提供了对应于中缀表达式的函数的访问:
|
||||
|
||||
```python
|
||||
>>> from operator import add, sub, mul
|
||||
>>> add(14, 28)
|
||||
42
|
||||
>>> sub(100, mul(7, add(8, 4)))
|
||||
16
|
||||
```
|
||||
|
||||
一个导入语句指定了一个模块的名称(例如,`operator` 或 `math`),然后列出要导入的该模块的命名属性(例如,`sqrt`)。一旦一个函数被导入,它可以被多次调用。
|
||||
|
||||
使用这些运算符函数(如 `add`)和运算符符号本身(如 `+`)之间没有区别。传统上,大多数程序员使用符号和中缀表达式来表达简单的算术。
|
||||
|
||||
[Python 3 库文档](https://docs.python.org/3/library/index.html)列出了每个模块所定义的功能,如[数学模块](https://docs.python.org/3/library/math.html)。然而,这个文档是为那些对整个语言很了解的开发者编写的。现在,你可能会发现,对一个函数进行实验比阅读文档能告诉你更多关于它的行为。随着你对 Python 语言和词汇的熟悉,这个文档将成为有价值的参考来源。
|
||||
|
||||
### 变量名和环境
|
||||
|
||||
编程语言的一个关键方面是它提供了使用变量名来指代计算对象的手段。如果一个值被赋予了一个变量名,我们就说这个变量名与这个值<em>绑定</em>了。
|
||||
|
||||
在 Python 中,我们可以使用赋值语句建立新的绑定,其中包含左边的变量名 `=` 右边的值。
|
||||
|
||||
```python
|
||||
>>> radius = 10
|
||||
>>> radius
|
||||
10
|
||||
>>> 2 * radius
|
||||
20
|
||||
```
|
||||
|
||||
变量名也是可以通过导入语句来绑定的。
|
||||
|
||||
```python
|
||||
>>> from math import pi
|
||||
>>> pi * 71 / 223
|
||||
1.0002380197528042
|
||||
```
|
||||
|
||||
`=` 符号在 Python(以及许多其他语言)中被称为<em>赋值</em>操作符。赋值是我们最简单的抽象手段,因为它允许我们使用简单的名称来指代复合操作的结果。用这种方式,复杂的程序就是通过一步一步地建立复杂度越来越高的计算对象来构建的。
|
||||
|
||||
将变量名与值绑定,然后通过变量名检索这些值意味着解释器必须保持某种内存,以跟踪变量名、值和绑定。这样的内存空间被称为<em>环境</em>。
|
||||
|
||||
变量名也可以被绑定到函数上。例如,变量名 `max` 与我们使用的求最大值的函数绑定。与数字不同的是,函数在呈现为文本时很棘手,所以当被要求描述一个函数时,Python 会打印一个识别描述。
|
||||
|
||||
```python
|
||||
>>> max
|
||||
<built-in function max>
|
||||
```
|
||||
|
||||
我们可以使用赋值语句给现有的函数起别名。
|
||||
|
||||
函数也可以看作是值。
|
||||
|
||||
```python
|
||||
>>> f = max
|
||||
>>> f
|
||||
<built-in function max>
|
||||
>>> f(2, 3, 4)
|
||||
4
|
||||
```
|
||||
|
||||
在同一个环境下的连续的赋值语句可以将一个名字重新绑定到一个新的值。
|
||||
|
||||
```python
|
||||
>>> f = 2
|
||||
>>> f
|
||||
2
|
||||
```
|
||||
|
||||
在 Python 中,名称通常被称为<em>变量名</em>或<em>变量</em>,因为它们在执行程序的过程中可能被绑定到不同的值。当一个名称通过赋值被绑定到一个新的值时,它就不再被绑定到任何以前的值。人们甚至可以将内置名称与新值绑定。
|
||||
|
||||
```python
|
||||
>>> max = 5
|
||||
>>> max
|
||||
5
|
||||
```
|
||||
|
||||
在将 `max` 赋值为 5 后,`max` 这个名称不再与函数绑定,因此试图调用 `max(2, 3, 4)` 会造成错误。
|
||||
|
||||
在执行赋值语句时,Python 在改变对左边变量名的绑定之前,对 `=` 右边的表达式进行计算。因此,人们可以在右侧表达式中引用一个变量名,即使它是要被赋值语句绑定的变量名。
|
||||
|
||||
```python
|
||||
>>> x = 2
|
||||
>>> x = x + 1
|
||||
>>> x
|
||||
3
|
||||
```
|
||||
|
||||
我们还可以在一个语句中给多个变量名赋值,其中左边的变量名和右边的表达式分别用逗号隔开。
|
||||
|
||||
```python
|
||||
>>> area, circumference = pi * radius * radius, 2 * pi * radius
|
||||
>>> area
|
||||
314.1592653589793
|
||||
>>> circumference
|
||||
62.83185307179586
|
||||
```
|
||||
|
||||
改变一个变量的值并不影响其他变量。下面,尽管变量名 `area` 被绑定到一个最初以 `radius` 定义的值,但 `area` 的值并没有改变。更新 `area` 的值需要另一个赋值语句。
|
||||
|
||||
```python
|
||||
>>> radius = 11
|
||||
>>> area
|
||||
314.1592653589793
|
||||
>>> area = pi * radius * radius
|
||||
380.132711084365
|
||||
```
|
||||
|
||||
通过多重赋值的语句,在左边的任何变量名被绑定到这些值之前,右边的所有表达式都将被计算。由于这个规则,交换绑定在两个变量名上的值可以在一个语句中进行。
|
||||
|
||||
```python
|
||||
>>> x, y = 3, 4.5
|
||||
>>> y, x = x, y
|
||||
>>> x
|
||||
4.5
|
||||
>>> y
|
||||
3
|
||||
```
|
||||
|
||||
### 计算嵌套表达式
|
||||
|
||||
我们在本小节的目标之一是分离出关于像程序一样思考的问题。从下面这个例子中,我们应该意识到,在计算嵌套调用表达式时,解释器本身是在遵循某种步骤。
|
||||
|
||||
为了计算一个调用表达式,Python 将做按以下规则来工作:
|
||||
|
||||
1. 计算运算符和操作数的子表达式,然后
|
||||
2. 将作为运算符子表达式的值的函数应用于作为运算符子表达式的值的参数。
|
||||
|
||||
即使这是个简单的程序也说明了关于一般过程的一些重要观点。第一步决定了为了完成一个调用表达式的计算过程,我们必须首先计算其他表达式。因此,计算过程在本质上是<em>递归的</em>;也就是说,作为其步骤之一,它也包括调用规则本身。
|
||||
|
||||
例如,计算
|
||||
|
||||
```python
|
||||
>>> sub(pow(2, add(1, 10)), pow(2, 5))
|
||||
2016
|
||||
```
|
||||
|
||||
需要这个按照上述过程重复四次。如果我们画出每个被计算的表达式,我们就可以直观地看到这个过程的层次结构。
|
||||
|
||||

|
||||
|
||||
这张插图被称为<em>表达式树</em>。在计算机科学中,树(Tree,一种数据结构,我们将在后续的章节中进行讨论)通常是自上而下生长的。树中每一点的对象被称为节点;在这张插图的情况下,节点是与值配对的表达式。
|
||||
|
||||
计算它的根,即顶部的完整表达式,需要首先计算作为其子表达式的分支。叶表达式(即没有分支的节点)代表函数或数字。内部节点有两个部分:我们的计算规则所适用的调用表达式,以及该表达式的结果。从这棵树的计算来看,我们可以想象操作数的值是向上渗滤的,从末端节点开始,然后在越来越高的层级上进行组合。
|
||||
|
||||
接下来,观察一下,步骤一的重复应用使我们需要计算的不是调用表达式,而是数字(如 `2`)和名称(如 `add`)等原始表达式。
|
||||
|
||||
我们通过规定以下几点来处理这种情况:
|
||||
|
||||
- 数字计算为它的名称所代表的数量
|
||||
- 名称计算为与当前环境中的名称相关的值。
|
||||
|
||||
请注意环境在决定表达式中符号的含义方面的重要作用。在 Python 中,在没有给定环境或是明确所有名称所指代的内容时,谈论一个表达式的价值是没有意义的,比如
|
||||
|
||||
```python
|
||||
>>> add(x, 1)
|
||||
```
|
||||
|
||||
而不指定任何关于环境为名称 `x`(甚至是名称 `add`)提供意义的信息。环境提供了计算发生的背景,这对我们理解程序执行起着重要作用。
|
||||
|
||||
上述的计算过程不足以计算所有的 Python 代码,只计算调用表达式、数字和名称。
|
||||
|
||||
例如,它不处理赋值语句
|
||||
|
||||
```python
|
||||
>>> x = 3
|
||||
```
|
||||
|
||||
这个语句不返回一个值,也不在某些参数上调用一个函数,因为赋值的目的是将一个变量名绑定到一个值上。
|
||||
|
||||
一般来说,赋值语句不是被计算而是<em>被执行</em>;它们不产生一个值,而是做一些改变。每种类型的表达式或语句都有自己的计算或执行过程。
|
||||
|
||||
### 纯函数和非纯函数
|
||||
|
||||
在本小节中,我们将区分两种函数
|
||||
**纯函数**
|
||||
函数有一些输入(它们的参数)并返回一些输出(应用它们的结果)。
|
||||
例如内置函数
|
||||
|
||||
```python
|
||||
>>> abs(-2)
|
||||
2
|
||||
```
|
||||
|
||||
可以被描述为一台接受输入并产生输出的小型机器。
|
||||

|
||||
函数 `abs` 是*纯函数*。纯函数的特性是,调用它们除了返回一个值之外没有任何影响。此外,当用相同的参数调用两次时,一个纯函数必须总是返回相同的值。
|
||||
|
||||
**非纯函数**
|
||||
除了返回一个值之外,应用一个非纯函数会产生副作用,从而使解释器或计算机的状态发生一些变化。一个常见的副作用是,使用`print`函数,在返回值之外产生额外的输出。
|
||||
|
||||
```python
|
||||
>>> print(1, 2, 3)
|
||||
1 2 3
|
||||
```
|
||||
|
||||
虽然`print`和`abs`在这些例子中可能看起来很相似,但它们的工作方式根本不同。打印返回的值总是`None`,这是一个特殊的 Python 值,不代表任何东西。交互式 Python 解释器不会自动打印值`None`。在`print`的情况下,函数本身是打印输出,也是被调用的副作用。
|
||||

|
||||
|
||||
对`print`函数的嵌套调用突出了纯函数和非纯函数的区别
|
||||
|
||||
```python
|
||||
>>> print(print(1), print(2))
|
||||
1
|
||||
2
|
||||
None None
|
||||
```
|
||||
|
||||
如果你发现这个输出出乎意料,可以画一个表达式树来弄清楚为什么计算这个表达式会产生这个奇特的输出。
|
||||
|
||||
请注意!`print`函数的返回值`None`意味着它*不应该*是赋值语句中的表达式。
|
||||
|
||||
```python
|
||||
>>> two = print(2)
|
||||
2
|
||||
>>> print(two)
|
||||
None
|
||||
```
|
||||
|
||||
纯函数是被限制的,因为它们不能有副作用或随时间改变行为。施加这些限制会产生巨大的好处。
|
||||
首先,纯函数可以更可靠地组成复合调用表达式。我们可以在上面的非纯函数例子中看到,`print`在操作数表达式中使用时并没有返回一个我们期望的结果。另一方面,我们已经看到,像`max`、`pow`和`sqrt`这样的函数可以有效地用于嵌套表达式。
|
||||
其次,纯函数往往更容易测试。一个参数列表将总是导致相同的返回值,这可以与预期返回值进行比较。关于测试将在之后的章节详细讨论。
|
||||
|
||||
在之后的章节中,我们将说明纯函数对于编写并发程序的重要性,其中多个调用表达式可以同时被计算。
|
||||
与之对应的,我们也将研究非纯函数并了解他们的用途。
|
||||
|
||||
出于这些问题的考虑,我们将在下一章节中着重讨论创建和使用纯函数。`print`函数的使用只是为了让我们看到计算的中间结果。
|
||||
|
||||
## 课后作业
|
||||
|
||||
一个好的课程怎么能少得了精心准备的课后作业呢?🤗
|
||||
|
||||
如果被题目卡住了,那就再去看看食用指南吧!😋
|
||||
|
||||
::: tip 📥
|
||||
本小节课后作业下载 <Download url="https://cdn.xyxsw.site/code/HW 01.zip"/>
|
||||
:::
|
||||
@@ -1,625 +0,0 @@
|
||||
# lab00:让我们开始吧
|
||||
|
||||
# 介绍
|
||||
|
||||
本 lab 将介绍如何设置计算机以完成作业,并介绍 Python 的一些基础知识。
|
||||
|
||||
这个 lab 是必需的。这个设置对于完全课程中的所有其他作业是必要的。
|
||||
|
||||
这个 lab 看起来非常长,但它主要是设置和学习如何使用这个课程的基本工具。这些现在看起来有点困难,但随着我们进一步进入课程,很快就会变得熟悉起来。
|
||||
|
||||
以下是实验室的主要部分:
|
||||
|
||||
- <strong>设置</strong>:设置课程的基本软件。这将需要几个组件,如下所列。
|
||||
|
||||
- <strong>安装终端</strong>:安装终端,以便你可以与本课程中的文件进行交互并运行 OK 命令。如果你的计算机上有终端并且使用起来很舒服,则可以跳过这一部分。
|
||||
- <strong>安装 Python 3</strong>:将 Python 编程语言安装到你的计算机上。如果你已经安装了 Python 3.7 或更高版本(最好是 Python 3.9),则可以跳过这部分。
|
||||
- <strong>安装文本编辑器</strong>:安装软件来编辑 `.py` 本课程的文件(例如 VSCode、Atom 等)。如果你已经有了自己喜欢的文本编辑器,则可以跳过这一部分。
|
||||
- <strong>演练:使用终端</strong>:本节将引导你完成如何使用终端和 Python 解释器。如果你已经能熟练使用这两者并感到轻松,则无需阅读本节。
|
||||
- <strong>演练:组织文件</strong>:本节将指导你完成如何使用终端来组织和导航本课程的文件。<strong>每个人都应该至少浏览一下这一部分</strong>,因为它包含特定于此类的重要信息,但是如果你已经习惯使用终端浏览目录结构,那么其中的大部分内容都会很熟悉。
|
||||
- <strong>复习:Python 基础知识</strong>:这是对讲座中介绍的 Python 的许多基本组件的复习。你应该已经看过此材料,但我们希望在每个 lab 中包含对相关内容的简要回顾,以备你需要复习任何内容。
|
||||
- <strong>要求:完成作业</strong>:你必须完成此部分才能获得作业分数。在这里,你将练习本课程的 lab、hw 和 proj 中要求你完成的不同类型的问题。本作业的主要目的是让你练习使用我们的软件。
|
||||
- <strong>要求:提交作业</strong>:你必须完成此部分才能获得作业分数。这将引导你了解如何在完成上一节后上交作业,以及如何验证你的作业是否已在 OKPY 上上交。(即使我们没有注册账户,也可以在本地运行 ok)
|
||||
- <strong>附录:有用的 Python 命令行选项</strong>:这些命令对调试你的工作很有用,但不是完成本实验所必需的。我们将它们包括在内是因为我们认为它们可能会在整个课程中对你有所帮助。
|
||||
|
||||
## 设置
|
||||
|
||||
### 安装终端
|
||||
|
||||
终端是一个程序,允许你通过输入命令与你的计算机进行交互。
|
||||
|
||||
#### macOS/Linux
|
||||
|
||||
如果你使用的是 Mac 或正在使用某种形式的 Linux(例如 Ubuntu),则你 `Terminal` 的计算机上已经有一个名为或类似名称的程序。打开它,你应该可以开始了。
|
||||
|
||||
#### 视窗
|
||||
|
||||
<strong>选项 1 (WSL):</strong>你可以使用适用于 Linux 的 Windows 子系统或 WSL 在 Windows 上获得终端。这可以通过终端程序访问 `Ubuntu`,该程序模拟 Windows 计算机上的 Ubuntu 操作系统 (OS)。这将使我们的大部分作业在你的设备上顺利进行。
|
||||
|
||||
要安装适用于 Windows 的 Ubuntu,请单击开始并搜索 PowerShell。右键单击并选择“以管理员身份运行”。然后,在 PowerShell 窗口中,键入 `wsl --install` 并按 Enter。该命令必须按照准确的顺序输入。这应该会自动完成设置过程(按照屏幕上可能给出的任何说明进行操作)。
|
||||
|
||||
接下来, [从 Windows 商店下载 Ubuntu](https://apps.microsoft.com/store/detail/ubuntu/9PDXGNCFSCZV?hl=en-us&gl=US),或者你可以运行:
|
||||
|
||||
```sql
|
||||
wsl --install -d ubuntu
|
||||
```
|
||||
|
||||
你可能还会发现访问 [Ubuntu 的下载指南](https://ubuntu.com/tutorials/install-ubuntu-on-wsl2-on-windows-10#1-overview)很有帮助。
|
||||
|
||||
安装完成后,在开始菜单中搜索 Ubuntu。第一次启动可能需要几分钟,但后续启动应该很快。
|
||||
|
||||
<strong>替代选项:</strong>如果你在安装 WSL 时遇到问题,你可以跳过此步骤并使用 Windows PowerShell 代替 61A。PowerShell 预装在 Windows 上,无需额外设置。你可以简单地从“开始”菜单启动它。`cd` 像和 这样的简单命令 `ls` 将起作用(`python` 将在设置后起作用),其中包含本课程所需的大部分 Bash 命令。
|
||||
|
||||
### 安装 Python 3
|
||||
|
||||
Python 3 是本课程中使用的主要编程语言。使用下面的说明安装 Python 3。(这些说明可能适用于旧版本的 Python 3,但步骤相似。)
|
||||
|
||||
<strong>重要提示:</strong>如果你已经安装了旧版本的 Python,请确保下载并安装 Python 3.9。你可以使用 来检查你的 Python 版本 `python3 ––version`。
|
||||
|
||||
#### macOs
|
||||
|
||||
下载并安装 [Python 3(64 位)](https://www.python.org/ftp/python/3.9.6/python-3.9.6-macosx10.9.pkg)。你可能需要右键单击下载图标并选择“打开”。安装后,请关闭并重新打开你的终端。
|
||||
|
||||
如果你安装了 Homebrew,你也可以通过运行安装 Python3 `brew install python3`。
|
||||
|
||||
#### Windows
|
||||
|
||||
<strong>如果你将使用 PowerShell 而不是 WSL,请打开 Microsoft Store 并搜索“python”。</strong>安装 Python Software Foundation 提供的 Python 3.9(这应该是第一个结果)。然后你可以跳过本节的其余部分。(重要提示:如果你以后决定以不同方式重新安装 Python,<strong>请先从 Microsoft Store 卸载它。)</strong>
|
||||
|
||||
否则,如果你使用的是 WSL,请在 Ubuntu 中键入 `sudo apt install python3` 并点击 `enter`. 安装完成后,你可以通过键入来测试它是否安装正确 `python3 --version`。你应该会在响应中看到一条消息,显示你的 python3 版本: `Python 3.9.6`。
|
||||
|
||||
#### Linux
|
||||
|
||||
运行 `sudo apt install python3`(Ubuntu)、`sudo pacman -S python3`(Arch) 或适用于你的发行版的命令。
|
||||
|
||||
#### 其他
|
||||
|
||||
[从下载页面下载 Python](https://www.python.org/downloads/)。
|
||||
|
||||
### 安装文本编辑器
|
||||
|
||||
<strong>你刚刚安装的 Python 解释器</strong>允许你<em>运行</em>Python 代码。你还需要一个<strong>文本编辑器</strong>,你将在其中<em>编写</em>Python 代码。
|
||||
|
||||
[Visual Studio Code (VS Code)](https://code.visualstudio.com/)是这门课程编写 Python 的最流行的选择。下面还列出了员工使用的其他一些编辑器。
|
||||
|
||||
<strong>如果你使用的是 Windows</strong>并遵循我们的 Python 设置过程,VS Code 将最适合你(因为它具有 WSL 支持)。安装 VS Code 后,安装 [Remote Development 扩展包](https://aka.ms/vscode-remote/download/extension)。[然后,你可以使用 VS Code 文档这一部分](https://code.visualstudio.com/docs/remote/wsl#_open-a-remote-folder-or-workspace)中的说明在 VS Code 中打开 WSL 文件夹。
|
||||
|
||||
<strong>我们强烈建议在本课程中使用 VS Code。</strong>这将帮助我们为你提供最好的支持,因为大多数员工也使用 VS Code。
|
||||
|
||||
VS Code 的另一个不错的功能是它具有“嵌入式终端”。因此,在为此类运行终端命令时,你可以在 VS Code 中管理所有内容,而不是在 VS Code 和单独的终端应用程序之间来回导航。`Terminal > New Terminal` 你可以通过转到 VS Code 的导航栏中打开嵌入式终端 。
|
||||
|
||||
> 警告:请不要使用 Microsoft Word 等文字处理器来编辑程序。文字处理器可以向文档添加额外的内容,这会使解释器感到困惑。
|
||||
|
||||
为了你的参考,我们还编写了一些使用流行的文本编辑器的指南。完成实验室后,如果你有兴趣,可以看看:
|
||||
|
||||
- [Visual Studio Code](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/vscode):一个功能齐全的桌面编辑器,具有许多可用于支持不同语言的扩展。
|
||||
- [Atom](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/atom):一个更轻量级的桌面编辑器。
|
||||
- [Vim](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/vim):命令行编辑器。
|
||||
- [Emacs](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/emacs):命令行编辑器。
|
||||
|
||||
其他编译器:
|
||||
|
||||
- [PyCharm](https://www.jetbrains.com/pycharm/):专为 Python 设计的桌面编辑器。
|
||||
- [Sublime Text](https://www.sublimetext.com/):一个使用代码的文本编辑器。
|
||||
|
||||
### 结对编程
|
||||
|
||||
在整个课程中,你将有很多机会在实验室和项目中与其他人协作编码。我们建议你现在下载这些结对编程扩展以备将来使用。
|
||||
|
||||
要共享代码,你可以按照你选择的编辑器的说明进行操作:
|
||||
|
||||
- [VS Code](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/vscode#pair-programming)
|
||||
- [Atom](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/atom#pair-programming)
|
||||
|
||||
### 备份设置
|
||||
|
||||
如你你在安装 Python 解释器、文本编辑器和终端时遇到问题,或者如果你使用的东西不允许你安装软件,比如 iPad,你可以作为临时措施使用以下一些来完成作业在你获得更合适的硬件时执行的步骤。
|
||||
|
||||
#### Soda 实验室计算机
|
||||
|
||||
你将需要一个指导帐户,该帐户允许你登录并使用 Soda 中的任何实验室计算机。你可以访问以下网址查看你现有的教学帐户并为适用的课程创建新的教学帐户:https: [//inst.eecs.berkeley.edu/connecting.html](https://inst.eecs.berkeley.edu/connecting.html)。
|
||||
|
||||
你可以通过你的 CalNet ID 登录该站点。要创建本课程的教学帐户,请单击以“cs61a”为目的的行的“获取新帐户”。
|
||||
|
||||
创建帐户后,你可以使用它登录 Soda 实验室计算机,并使用该计算机完成课程作业。
|
||||
|
||||
#### 在线编辑器作为备份
|
||||
|
||||
> 重要提示: 下面列出的两种替代方法都不适合在本课程中使用。我们建议能够使用您自己的本地设置或使用 Soda 中的实验室计算机(您可以使用您的课程教学帐户访问)。
|
||||
|
||||
<strong>61A 代码:</strong>
|
||||
|
||||
您可以使用 [61A 代码](https://code.cs61a.org/),这是一个在线课程环境,您可以在其中编辑、运行、调试、可视化程序并与教职员工共享程序。可在此处找到 61A 代码的文档: [61A 代码文档](https://cs61a.org/articles/61a-code-docs/)。
|
||||
|
||||
> 注意: 您将无法 `ok` 在 61A 代码中运行命令,解锁测试、运行测试和提交作业需要执行这些命令。
|
||||
|
||||
在 61A Code 上完成此作业的步骤:
|
||||
|
||||
1. 访问 [61A 代码](https://code.cs61a.org/)。
|
||||
2. 打开现有文件:进入您的 `cs61a` 文件夹,然后是作业文件夹 ( `lab00`),您可以在其中找到该作业的文件。
|
||||
3. 系统将提示您授权编辑器。您可以点击“确认”。回到编辑器本身,然后您可以打开要编辑的文件。
|
||||
4. 要打开终端,请单击“控制台”。
|
||||
5. 您可以使用编辑器编写代码,使用控制台运行代码。
|
||||
|
||||
<strong>数据中心:</strong>
|
||||
|
||||
在本地工作的另一种选择是使用加州大学伯克利分校的 Datahub。
|
||||
|
||||
在 Datahub 上完成作业的步骤:
|
||||
|
||||
1. 访问[数据中心](https://datahub.berkeley.edu/)。
|
||||
2. 将作业 zip 文件上传到数据中心。
|
||||
3. 通过按左上角的“新建”并选择终端来打开终端。
|
||||
4. 导航到 zip 文件所在的位置并运行 `unzip lab00.zip`。
|
||||
5. 打开代码文件 ( `lab00.py`) 并在其中键入,然后保存。
|
||||
6. 现在您可以提交实验。
|
||||
|
||||
## 演练:使用终端
|
||||
|
||||
首先,打开一个终端窗口。
|
||||
|
||||

|
||||
|
||||
#### 主目录
|
||||
|
||||
当您第一次打开终端时,您将从“主目录”开始。主<strong>目录</strong>由 `~` 符号表示,您可能会在提示符处看到它。
|
||||
|
||||
> 如果您的终端窗口看起来不完全相同,请不要担心。重要的部分是提示符显示 `$`(表示 Bash)或 `%` (表示 zsh)。
|
||||
|
||||
尝试跑步 `echo "$HOME"`。该命令应显示您的主目录的完整路径。它应该看起来像这样:
|
||||
|
||||
`/Users/OskiBear`
|
||||
|
||||
#### 小路
|
||||
|
||||
PATH 就像一个地址:它告诉您和计算机到某个文件夹的完整路径(或路由)。请记住,您可以通过两种不同的方式访问计算机上的文件和目录(文件夹)。您可以使用终端(这是一个<strong>命令行</strong>界面或 CLI),也可以使用 Finder <strong>。</strong>Finder 是<strong>图形</strong>用户<strong>界面</strong>(或 GUI)的一个 例子<strong>。</strong>导航技术不同,但文件相同。例如,这是我的 CS 61A 实验室文件夹在我的 GUI 中的样子:
|
||||
|
||||

|
||||
|
||||
这是完全相同的文件夹在终端中的外观:
|
||||
|
||||

|
||||
|
||||
请注意,在这两种情况下,黄色框都显示了 PATH,紫色椭圆显示了“labs”文件夹的内容。
|
||||
|
||||
#### 终端与 Python 解释器
|
||||
|
||||
让我们停下来思考一下终端和 Python 解释器之间的区别。
|
||||
|
||||

|
||||
|
||||
1. 哪个是终端?
|
||||
2. 哪个是 Python 解释器?
|
||||
3. 哪个是我的代码编辑器?
|
||||
4. 你怎么知道?
|
||||
|
||||
A 和 D 都是我的终端。在这里您可以运行 bash 命令,例如 `cd` 和 `ls`。D 是 VS Code 内置的终端。
|
||||
|
||||
B 是 Python 解释器。你可以从 >>> 提示中看出这意味着你已经启动了一个 Python 解释器。您还可以判断,因为启动它的命令是可见的:`python3`。该 `python3` 命令启动 Python 解释器。如果您在 Python 解释器中键入 bash 命令,您可能会遇到语法错误!这是一个例子:
|
||||
|
||||

|
||||
|
||||
C 是我的代码编辑器。这是我可以编写 Python 代码以通过我的终端执行的地方。
|
||||
|
||||
## 演练:组织文件
|
||||
|
||||
在本节中,您将学习如何使用终端命令管理文件。
|
||||
|
||||
> 确保您的提示包含 `$` 其中的某处并且不以 `>>>`. 如果它以 `>>>` 您仍在 Python shell 中开头,您需要退出。见上文了解如何。
|
||||
|
||||
### 目录
|
||||
|
||||
您将使用的第一个命令是 `ls`. 尝试在您的终端中输入:
|
||||
|
||||
```
|
||||
ls
|
||||
```
|
||||
|
||||
该 `ls` 命令<strong>列出</strong>了当前目录中<strong>的</strong>所有文件和文件夹。目录是文件夹(如文件夹)的另一个 <strong>名称</strong>`Documents`。
|
||||
|
||||
#### macOS/Linux
|
||||
|
||||
由于您现在位于主目录中,因此在您键入后 `ls` 您应该会看到主目录的内容。
|
||||
|
||||
#### 视窗
|
||||
|
||||
`~` 在 Ubuntu 中,当您键入 时,您将看不到任何文件 `ls`。相反,您首先需要更改目录(见下文)。
|
||||
|
||||
### 更改目录
|
||||
|
||||
要移动到另一个目录,请使用 `cd` 命令 ( <strong>c</strong> hange <strong>directory</strong> )。
|
||||
|
||||
#### macOS/Linux
|
||||
|
||||
让我们尝试进入您的 `Desktop` 目录。首先,确保您位于主目录中(检查 `~` 命令行中的 )并使用 `ls` 查看该 `Desktop` 目录是否存在。
|
||||
|
||||
尝试在您的终端中键入以下命令,这应该会将您移至该目录:
|
||||
|
||||
```
|
||||
cd Desktop
|
||||
```
|
||||
|
||||
如果您<em>不在</em>您的主目录中,请尝试 `cd ~/Desktop`. 这是告诉终端你想去的路径。
|
||||
|
||||
#### Windows
|
||||
|
||||
在 Windows 上,首先切换到您的主目录。
|
||||
|
||||
```
|
||||
cd /mnt/c/Users/
|
||||
```
|
||||
|
||||
现在尝试 `ls` 之前的命令。您应该会看到一些文件夹。其中一个文件夹应与您的用户名匹配。例如,假设您的用户名是 `OskiBear`,您应该会看到一个名为 的文件夹 `OskiBear`。(请注意,您的 Windows 用户名可能与您的 Ubuntu 用户名不同)让我们切换到该文件夹:
|
||||
|
||||
```
|
||||
cd /mnt/c/Users/OskiBear/Desktop
|
||||
```
|
||||
|
||||
如果仍然找不到桌面目录,请在 Piazza 或办公时间寻求帮助。
|
||||
|
||||
### 制作新目录
|
||||
|
||||
下一个命令称为 `mkdir`,<strong>它</strong>创建<strong>一个</strong>新 <strong>目录</strong>。让我们在您的目录中创建一个名为的目录来存储此类的所有作业:`cs61aDesktop`
|
||||
|
||||
```
|
||||
mkdir cs61a
|
||||
```
|
||||
|
||||
名为的文件夹 `cs61a` 将出现在您的桌面上。`ls` 您可以通过再次使用该命令或使用资源管理器 (Windows) 或 Finder (Mac) 检查您的桌面来验证这一点。
|
||||
|
||||
在这一点上,让我们创建更多的目录。首先,确保您位于 `cs61a` 目录中(mac: `~/Desktop/cs61a`, Windows: `/mnt/c/Users/Desktop/cs61a`)。然后,创建两个新文件夹,一个名为 `projects`,另一个名为 `lab`. 两者都应该在您的 `cs61a` 文件夹中:
|
||||
|
||||
#### macOS/Linux
|
||||
|
||||
```
|
||||
cd ~/Desktop/cs61a
|
||||
mkdir projects
|
||||
mkdir lab
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
```
|
||||
cd /mnt/c/Users/OskiBear/Desktop/cs61a
|
||||
mkdir projects
|
||||
mkdir lab
|
||||
```
|
||||
|
||||
现在,如果您列出目录的内容(使用 `ls`),您将看到两个文件夹,`projects` 和 `lab`.
|
||||
|
||||

|
||||
|
||||
### 更多目录更改
|
||||
|
||||
有几种方法可以返回主目录:
|
||||
|
||||
- `cd ..`(两个点)。意思 `..` 是“父目录”,或当前目录之上的一个目录。
|
||||
- `cd ~`(代字号)。请记住,这 `~` 意味着主目录,因此此命令将始终更改为您的主目录。
|
||||
- `cd`(只有 `cd`)。只输入 `cd` 是输入 `cd ~` 的捷径。
|
||||
|
||||
> 如果您愿意,您不必将文件保留在桌面上。您将文件保存在本地的位置不会影响您的成绩。做对您来说最简单、最方便的事情!
|
||||
|
||||
### 下载作业
|
||||
|
||||
如果您还没有,请下载 zip 存档 [lab00.zip](https://inst.eecs.berkeley.edu/~cs61a/fa22/lab/lab00/lab00.zip),其中包含本 lab 所需的所有文件。完成后,让我们找到下载的文件。在大多数计算机上,`lab00.zip` 它可能位于一个名为 `Downloads` 您的主目录的目录中。使用 `ls` 命令检查:
|
||||
|
||||
```
|
||||
ls ~/Downloads
|
||||
```
|
||||
|
||||
如果您没有看到 `lab00.zip`,请在 Piazza 或办公时间寻求帮助。在某些版本的 Safari 上,文件可能会为您解压缩,在这种情况下,您只会看到一个名为 `lab00`.
|
||||
|
||||
### 提取启动文件
|
||||
|
||||
您必须先展开 zip 存档,然后才能处理实验室文件。不同的操作系统和不同的浏览器有不同的解压方式。在 Mac 中单击 .zip 文件将自动解压缩。在 Windows 上,您需要先单击 .zip 文件,然后选择“全部解压”。如果遇到麻烦,可以在线搜索如何解压缩文件。
|
||||
|
||||
这是使用终端解压缩的一种方法:
|
||||
|
||||
> 使用终端,您可以从命令行解压缩 zip 文件。首先,`cd` 进入包含 zip 文件的目录:<br/>``<br/>cd ~/Downloads<br/>`` 现在,`unzip` 使用 zip 文件的名称运行命令:<br/>``<br/>unzip lab00.zip<br/>``
|
||||
|
||||
您只需要解压缩文件一次。
|
||||
|
||||
解压缩后 `lab00.zip`,您将拥有一个名为的新文件夹 `lab00`,其中包含以下文件(使用 `cd lab00` 和检查 `ls`):
|
||||
|
||||
- `lab00.py`:您要将代码添加到的模板文件
|
||||
- `ok`: 用于测试和提交作业的程序
|
||||
- `lab00.ok`: 配置文件 `ok`
|
||||
|
||||
### 移动文件
|
||||
|
||||
将实验室文件移动到您之前创建的实验室文件夹中:
|
||||
|
||||
<strong>macOS/Linux</strong>
|
||||
|
||||
```
|
||||
mv ~/Downloads/lab00 ~/Desktop/cs61a/lab
|
||||
```
|
||||
|
||||
Windows
|
||||
|
||||
```
|
||||
mv /mnt/c/Users/Desktop/lab00 /mnt/c/Users/Desktop/cs61a/lab
|
||||
```
|
||||
|
||||
该 `mv` 命令会将文件夹移动到<strong>文件</strong>夹<strong>中</strong>。如果您愿意,还可以通过将文件拖放到图形文件资源管理器中的正确文件夹中来移动文件,这可能更常见,并且会产生完全相同的结果。`~/Downloads/lab00~/Desktop/cs61a/lab`
|
||||
|
||||
现在,转到 `lab00` 您刚刚移动的文件夹。尝试使用 `cd` 自己的方式导航!如果卡住了,可以使用以下命令:
|
||||
|
||||
<strong>macOS/Linux</strong>
|
||||
|
||||
```
|
||||
cd ~/Desktop/cs61a/lab/lab00
|
||||
```
|
||||
|
||||
Windows
|
||||
|
||||
```
|
||||
cd /mnt/c/Users/Desktop/cs61a/lab/lab00
|
||||
```
|
||||
|
||||
### 概括
|
||||
|
||||
以下是我们刚刚完成的命令摘要,供您参考:
|
||||
|
||||
- `ls`: 列出当前目录下的所有文件
|
||||
- `cd <path to directory>`: 改变进入指定目录
|
||||
- `mkdir <directory name>`: 使用给定名称创建一个新目录
|
||||
- `mv <source path> <destination path>`: 将给定地址的文件移动到给定的目录
|
||||
|
||||
最后,您可以开始编辑实验室文件了!如果这看起来很复杂,请不要担心——随着时间的推移,它会变得容易得多。只要继续练习!您还可以查看我们的 [UNIX 教程](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/unix),了解有关终端命令的更详细说明。
|
||||
|
||||
## 回顾:Python 基础知识
|
||||
|
||||
程序由表达式和语句组成。表达式是一段计算出某个值的代码,而<em>语句是使程序</em>中发生某事的一行或多行代码。
|
||||
|
||||
当您将 Python 表达式输入交互式 Python 解释器时,将显示其值。当您阅读以下示例时,请在您自己的 Python 解释器上尝试一些类似的表达式,您可以通过在终端中输入以下内容来启动它:
|
||||
|
||||
```
|
||||
python3
|
||||
```
|
||||
|
||||
您将在本课程中学习各种类型的表达式和语句。现在,让我们看一下完成本实验所需的内容。
|
||||
|
||||
#### 原始表达式
|
||||
|
||||
原始表达式只需要一步来计算。这些包括数字和布尔值,它们只是对自己求值。
|
||||
|
||||
```python
|
||||
>>> 3
|
||||
3
|
||||
>>> 12.5
|
||||
12.5
|
||||
>>> True
|
||||
True
|
||||
```
|
||||
|
||||
#### 算术表达式
|
||||
|
||||
数字可以与数学运算符组合以形成复合表达式。除了 `+` 运算符(加法)、`-` 运算符(减法)、`*` 运算符(乘法)和 `**` 运算符(求幂)之外,还有三个类似除法的运算符需要记住:
|
||||
|
||||
- 浮点数除法 ( `/`):将第一个数字除以第二个数字,计算结果为带小数点的数字,<em>即使数字被整除也是如此</em>。
|
||||
- 整除 ( `//`):用第一个数字除以第二个数字,然后向下舍入,计算结果为整数。
|
||||
- 模 ( `%`):计算除法剩余的正余数。
|
||||
|
||||
括号可用于将子表达式组合在一起;整个表达式按 PEMDAS(括号、求幂、乘法/除法、加法/减法)顺序求值。
|
||||
|
||||
```python
|
||||
>>> 7 / 4
|
||||
1.75
|
||||
>>> (2 + 6) / 4
|
||||
2.0
|
||||
>>> 7 // 4 # Floor division (rounding down)
|
||||
1
|
||||
>>> 7 % 4 # Modulus (remainder of 7 // 4)
|
||||
3
|
||||
```
|
||||
|
||||
#### 字符串
|
||||
|
||||
字符串由用单引号 ( `''`) 或双引号 ( `""`) 包裹的一个或多个字符组成。字符串实际上与原始表达式略有不同,但出于此赋值的目的,可以将其视为对自身求值的表达式。在接下来的几周中,您将在本课程中了解更多关于字符串的复杂性!
|
||||
|
||||
```python
|
||||
>>> "hello" # Both single and double quotes work!'hello'>>> 'world!''world'
|
||||
```
|
||||
|
||||
#### 赋值语句
|
||||
|
||||
赋值语句由名称和表达式组成。它通过计算符号右侧的表达式 `=` 并将其值<em>绑定</em>到左侧的名称来更改程序的状态。
|
||||
|
||||
```python
|
||||
>>> a = (100 + 50) // 2
|
||||
```
|
||||
|
||||
现在,如果我们计算 `a`,解释器将显示值 75。
|
||||
|
||||
```python
|
||||
>>> a
|
||||
75
|
||||
```
|
||||
|
||||
## 要求:完成作业
|
||||
|
||||
> 在处理作业时,请确保您终端的工作目录是正确的(这个目录可能是您解压缩作业的位置)。
|
||||
|
||||
### Python 会做什么?(WWPD)
|
||||
|
||||
实验室作业的一个组成部分是预测 Python 解释器的行为方式。
|
||||
|
||||
> 在您的终端中输入以下内容以开始此部分:<br/>``<br/>python3 ok -q python-basics -u<br/>`` 系统将提示您输入各种语句/表达式的输出。您必须正确输入它们才能继续,但错误答案不会受到惩罚。<br/>第一次运行 Ok 时,系统将提示您输入 bCourses 电子邮件。请遵循[这些指示](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/using-ok/#signing-in-with-ok)。我们在评分时使用此信息将您的代码与您相关联。
|
||||
|
||||
```python
|
||||
>>> 10 + 2
|
||||
______
|
||||
>>> 7 / 2
|
||||
______
|
||||
>>> 7 // 2
|
||||
______
|
||||
>>> 7 % 2 # 7 modulo 2, the remainder when dividing 7 by 2.
|
||||
______
|
||||
```
|
||||
|
||||
```python
|
||||
>>> x = 20>>> x + 2
|
||||
______
|
||||
>>> x
|
||||
______
|
||||
>>> y = 5>>> y = y + 3>>> y * 2
|
||||
______
|
||||
>>> y = y // 4>>> y + x
|
||||
______
|
||||
```
|
||||
|
||||
### 代码编写题
|
||||
|
||||
#### 理解问题
|
||||
|
||||
实验室还将包括函数编写问题。在你的文本编辑器中打开 `lab00.py`。您可以 `open .` 在 MacOS 或 `start .` Windows 上键入以在 Finder/文件资源管理器中打开当前目录。然后双击或右键单击以在文本编辑器中打开文件。你应该看到这样的东西:
|
||||
|
||||

|
||||
|
||||
三引号中的行 `"""` 称为<strong>文档字符串(Docstring)</strong>,它描述了函数应该做什么。在 61A 中编写代码时,您应该始终阅读文档字符串!
|
||||
|
||||
开头的行 `>>>` 称为<strong>文档测试模块(Doctest)</strong>。回想一下,当使用 Python 解释器时,您在旁边编写 Python 表达式 `>>>`,输出打印在该行下方。文档测试模块通过显示实际的 Python 代码来解释函数的作用。它回答了这个问题:“如果我们输入这段 Python 代码,预期的输出应该是什么?”
|
||||
|
||||
在这里,我们圈出了文档字符串和文档测试,以便于查看:
|
||||
|
||||

|
||||
|
||||
在 `twenty_twenty_two`,
|
||||
|
||||
- 文档字符串告诉你“想出最有创意的表达式,计算到 2022 年”,但你只能使用数字和算术运算符 `+`(add)、`*`(multiply) 和 `-`(subtract)。
|
||||
- doctest 检查函数调用 `twenty_twenty_two()` 是否应返回数字 2022。
|
||||
|
||||
> 你不应该修改文档字符串,除非你想添加你自己的测试!除非另有说明,否则您需要编辑的唯一部分是代码。
|
||||
|
||||
#### 编写代码
|
||||
|
||||
了解问题的含义后,您就可以开始编写代码了!您应该将中的下划线替换 `return ______` 为计算结果为 2022 的表达式。您能想出的最有创意的表达式是什么?
|
||||
|
||||
> 编辑后不要忘记保存您的作业!在大多数文本编辑器中,您可以通过导航到“文件”>“保存”或在 MacOS 上按 Command-S 或在 Windows 上按 Ctrl-S 来保存。
|
||||
|
||||
### 运行测试
|
||||
|
||||
在 CS 61A 中,我们将使用一个名为 的程序 `ok` 来测试我们的代码。`ok` 将包含在本课程的每项作业中。
|
||||
|
||||
> 为了快速生成 ok 命令,您现在可以使用 [ok 命令生成器](https://go.cs61a.org/ok-help)。
|
||||
|
||||
返回终端——确保您位于 `lab00` 我们之前创建的目录中(请记住,该 `cd` 命令允许您[更改目录](https://inst.eecs.berkeley.edu/~cs61a/fa22/lab/lab00/#changing-directories))。
|
||||
|
||||
在该目录中,您可以键入 `ls` 以验证是否存在以下三个文件:
|
||||
|
||||
- `lab00.py`:您刚刚编辑的起始文件
|
||||
- `ok`: 我们的测试程序
|
||||
- `lab00.ok`: Ok 的配置文件
|
||||
|
||||
现在,让我们测试我们的代码以确保它能正常工作。您可以 `ok` 使用此命令运行:
|
||||
|
||||
```
|
||||
python3 ok
|
||||
```
|
||||
|
||||
> 请记住,如果您使用的是 Windows 而该 `python3` 命令不起作用,请尝试仅使用 `python` 或 `py`。有关详细信息,请参阅[安装 Python 3](https://inst.eecs.berkeley.edu/~cs61a/fa22/lab/lab00/#install-python-3) 部分,如果遇到困难,请寻求帮助!
|
||||
|
||||
如果您正确编写了代码并完成了测试解锁,您应该会看到一个成功的测试:
|
||||
|
||||
```sql
|
||||
=====================================================================
|
||||
Assignment: Lab 0
|
||||
Ok, version v1.18.1
|
||||
=====================================================================
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Running tests
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Test summary
|
||||
3 test cases passed! No cases failed.
|
||||
```
|
||||
|
||||
如果您没有通过测试,`ok` 则会向您显示如下内容:
|
||||
|
||||
```python
|
||||
---------------------------------------------------------------------
|
||||
Doctests for twenty_twenty_two
|
||||
|
||||
>>> from lab00 import *
|
||||
>>> twenty_twenty_two()
|
||||
2013
|
||||
|
||||
# Error: expected
|
||||
# 2022
|
||||
# but got
|
||||
# 2013
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Test summary
|
||||
0 test cases passed before encountering first failed test case
|
||||
```
|
||||
|
||||
在您的文本编辑器中修复您的代码,直到测试通过。
|
||||
|
||||
> 每次运行 Ok 时,Ok 都会尝试备份您的工作。如果它说“连接超时”,请不要担心。我们不会使用您的备份进行评分。<br/>虽然是 CS 61A 中的主要作业“autograder”,但有时您可能会发现以[文档测试模块](https://inst.eecs.berkeley.edu/~cs61a/fa22/lab/lab00/#understanding-problems)的形式编写一些您自己的测试很有用。然后,您可以使用 `-m doctest` 命令来测试代码。[Python 命令](https://inst.eecs.berkeley.edu/~cs61a/fa22/lab/lab00/#appendix-useful-python-command-line-options))。
|
||||
|
||||
## 要求:提交作业
|
||||
|
||||
现在您已经完成了第一个 CS 61A 作业,是时候上交了。您可以按照以下后续步骤提交您的作业并获得分数。
|
||||
|
||||
### 第 1 步:提交 `ok`
|
||||
|
||||
在您的终端中,确保您位于包含 `ok`. 如果你还没有,你可以使用这个命令:
|
||||
|
||||
```
|
||||
cd ~/Desktop/cs61a/lab/lab00
|
||||
```
|
||||
|
||||
接下来,使用 `ok` 选项 `--submit`:
|
||||
|
||||
```sql
|
||||
python3 ok --submit
|
||||
```
|
||||
|
||||
如果您之前没有运行过,这将提示您输入电子邮件地址。请按照[这些说明](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/using-ok/#signing-in-with-ok)进行操作,如果遇到问题,请参阅该页面上的故障排除步骤。之后,Ok 将打印出如下消息:
|
||||
|
||||
```
|
||||
Submitting... 100% complete
|
||||
Submission successful for user: ...
|
||||
URL: https://okpy.org/...
|
||||
```
|
||||
|
||||
### 第 2 步:验证您的提交
|
||||
|
||||
您可以点击 Ok 打印出来的链接来查看您的最终提交,或者您可以转到 [okpy.org](https://okpy.org/)。您将能够在登录后查看您提交的内容。
|
||||
|
||||
> 请确保你用你在终端运行 `ok` 时提供的相同电子邮件登录!
|
||||
|
||||
您应该会看到 Lab 0 的成功提交。
|
||||
|
||||
<strong>恭喜</strong>,您刚刚提交了第一份 CS 61A 作业!
|
||||
|
||||
> 有关 Ok 的更多信息,请参见[此处](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/using-ok/)。您还可以使用 `--help` 标志:<br/>``sql<br/>python3 ok --help<br/>`` 这个标志的作用就像我们之前使用的 UNIX 命令一样。
|
||||
|
||||
## 附录:有用的 Python 命令行选项
|
||||
|
||||
运行 Python 文件时,您可以使用命令行选项进一步检查代码。这里有一些会派上用场。如果您想了解有关其他 Python 命令行选项的更多信息,请查看[文档](https://docs.python.org/3.9/using/cmdline.html)。
|
||||
|
||||
- 不使用命令行选项将运行您提供的文件中的代码并返回到命令行。例如,如果我们想以 `lab00.py` 这种方式运行,我们将在终端中写入:
|
||||
|
||||
```
|
||||
python3 lab00.py
|
||||
```
|
||||
|
||||
- <strong>-i</strong>:该 `-i` 选项运行您的 Python 脚本,然后打开交互式会话。在交互式会话中,您逐行运行 Python 代码并获得即时反馈,而不是一次运行整个文件。要退出,请 `exit()` 在解释器提示符中键入。`Ctrl-D` 您还可以在 Linux/Mac 计算机或 Windows 上使用键盘快捷键 `Ctrl-Z Enter`。
|
||||
- 如果您在交互式运行 Python 文件时对其进行编辑,则需要退出并重新启动解释器才能使这些更改生效。
|
||||
- 以下是我们如何以交互方式运行 `lab00.py`:
|
||||
|
||||
```
|
||||
python3 -i lab00.py
|
||||
```
|
||||
|
||||
- <strong>-m doctest</strong>:在特定文件中运行 doctests。Doctests 在函数中被三重引号 ( `"""`) 包围。
|
||||
- 文件中的每个测试都包含 `>>>` 一些 Python 代码和预期的输出(尽管 `>>>` 在 doctest 命令的输出中看不到)。
|
||||
- 要为 运行 doctests `lab00.py`,我们可以运行:
|
||||
|
||||
```
|
||||
python3 -m doctest lab00.py
|
||||
```
|
||||
141
3.编程思维体系构建/3.6.5CS61A食用指南.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# CS61A 食用指南
|
||||
|
||||
## 关于 CS61A
|
||||
|
||||
这是 CS 61A Fall 2022 的网站:
|
||||
|
||||
[https://inst.eecs.berkeley.edu/~cs61a/fa22/](https://inst.eecs.berkeley.edu/~cs61a/fa22/)
|
||||
|
||||
在这个目录下的文章上传的 pdf 基本都是英文的,还请做好准备。
|
||||
|
||||
在每个 disc、lab、hw 和 proj 下,都会有相对应的.zip 文件,文件里装的是在学习过程中所需要的代码。
|
||||
|
||||
在文章的最后会附上 sol。
|
||||
|
||||
当然,在没有头绪的时候,你可以选择参考 sol,也可以重新学习视频和教科书,也可以改变做题的顺序。
|
||||
|
||||
ps:哪怕是 cs61a 这样优秀的课程,给出的解决方案里也会有不是那么正确的代码(它并不能处理所有的情况)
|
||||
|
||||
pps:如果你找到了 sol 里不是那么正确的代码,请联系我们,我们将新建一个文档来记录 sol 中这些“错误”的代码
|
||||
|
||||
ppps:当然存在“错误”的代码的概率很小,你发现它们的概率就更小了
|
||||
|
||||
pppps:不只是在 sol 文件中,youtube 上的视频中的代码也可能会出现一些小问题
|
||||
|
||||
## 为什么要学 CS61A?
|
||||
|
||||
简单来说,学校的编程课只能教会你一些最基本的语法和代码逻辑,从学校的课程中,你很难学到一些实用的编程思想 (甚至一些老师自己也讲不明白)。一部分原因当然是老师上课完全没有想过如何渗透这些编程思想;还有一部分原因我认为是课后作业是在过于随意了,PTA 上的课后作业只是用来检测你是否掌握了一些最基础的语法,这样的题目是很难培养学生的编程思想的。
|
||||
|
||||
## 从 CS61A 中能学到什么?
|
||||
|
||||
分语言来看,cs61a 讲了这三种语言:
|
||||
|
||||
1. Python
|
||||
2. Scheme
|
||||
3. SQL
|
||||
|
||||
分章节来看,cs61a 主要教了以下这些内容:
|
||||
|
||||
1. 用函数构建抽象关系
|
||||
|
||||
1. 函数与函数设计
|
||||
2. 控制
|
||||
3. 高级函数
|
||||
4. 递归函数
|
||||
2. 用数据构建抽象关系
|
||||
|
||||
1. 数据抽象
|
||||
2. 可变数据
|
||||
3. 面向对象编程
|
||||
3. 解释计算机程序
|
||||
|
||||
1. 函数式编程
|
||||
2. 异常
|
||||
3. 解释器的开发
|
||||
4. 数据处理
|
||||
|
||||
1. 声明式编程
|
||||
|
||||
## CS61A 好在哪里?
|
||||
|
||||
~~暂且不考虑由于是伯克利大学的课程所以视频资料和教科书全是英文~~
|
||||
|
||||
每一节课程都有完备的视频资料和相对应的教科书,还有精心设计的 lab、hw、disc、proj
|
||||
|
||||
所有题目都有完善的说明、例子、半自动的测试和批改,哪怕英语不是很好的同学也能理解到题目所要表达的意思
|
||||
|
||||
课程的设计目的是引导学生像计算机那样思考,在课程网站上有相当多的交互式页面来帮助你学习(不过因为我们没有伯克利大学的账号无法使用 🤪)
|
||||
|
||||
课程中还推荐了一个学习 python 很好的网站,可以用来绘制环境图:[Python Tutor](http://tutor.cs61a.org/)
|
||||
|
||||
~~(不像某些学校只会把学生丢进水池里,没淹死的都是学会游泳的好学生)~~
|
||||
|
||||
John Denero 教授的网课也是非常的有趣,如果你的英语够好,还能发现藏在视频里的一些小彩蛋 🤤
|
||||
|
||||
## 如何学习 CS61A?
|
||||
|
||||
cs61a 绝对是一个挑战,但是我们都希望你学习并且成功,所以这里有一些可能对你的旅程有所帮助的小贴士:
|
||||
|
||||
- 在 61A 中成功的方法有很多 ーー 我们发现下面的建议适用于几乎所有的学生,但是对不同的人最有效的方法是不同的,我们建议探索不同的学习和工作策略。~~如果你想和助教谈谈你的方法,我们有专门的~~~~咨询办公时间~~~~做这些事情!~~
|
||||
- 问问题
|
||||
|
||||
- 如果你遇到一些你不知道或不确定的概念或问题,_那就来问吧_~~。我们是来帮助你们学习的,如果你们提出一个问题,我们会告诉你们在哪些方面我们可以帮助你们理解这些材料。~~提出问题的过程本身也有助于你自己弄清楚你特别想问什么,从而找出你认为你可以学到更多的概念。
|
||||
- 小组学习
|
||||
|
||||
- 再次强调,这门课对于大多数学生来说都不是一门简单的课程,你可能会感到不知所措。给你的小伙伴们发送一个信息,并与班上的其他同学取得联系; 一起完成作业或一起学习准备考试,只要你不违反课程大纲中规定的诚实学习的[课程政策](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/about/#academic-misconduct)。
|
||||
- 当遇到问题时,试着组织语言来解释你遇到困难的地方。
|
||||
|
||||
- 这并不需要一个找到懂得如何解决问题的人 (或者甚至是一个人——这种做法通常被称为**橡皮鸭**,因为你可以把一只橡皮鸭当作你的练习对象) ,因为主要目标是让你弄清楚你自己的想法,弄清楚你的理解和代码到底在哪里卡住了。这样你可以知道应该专注于哪一部分,以便更好地理解。
|
||||
- 做所有的 hw(或者至少认真地尝试)。我们没有给出很多 hw 问题,但那些我们给你的可能会发现是具有挑战性、耗时且有回报的。
|
||||
- 做所有的 lab。其中大部分的设计更多的是作为一个课程材料的介绍,并可能需要半个小时左右的时间。这是一个熟悉新的知识点的好时机。
|
||||
|
||||
因为伯克利大学有着非常好的软硬件设施,所以在他们的课程体系中有助教和专门的论坛来辅助学习。杭电实在是没有这样的资源 🥲,所以在遇到问题的时候请先尝试独立解决,然后再和小伙伴们一起讨论,如果遇到实在解决不了的难题就来参考前人的 [GitHub](https://github.com/E1PsyCongroo/CS61A-FA22) 吧
|
||||
|
||||
## 如何使用 ok 进行代码测试?
|
||||
|
||||
在 61a 中,我们使用一个名为 ok 的程序对 lab,hw 和 proj 进行自动评分。在你下载的每一份代码文件中,都应该找到 ok 程序。
|
||||
|
||||
确保在终端打开的路径下,包含 ok 和要测试的代码。
|
||||
|
||||
要使用 Ok 来运行指定函数的 doctests,请运行以下命令
|
||||
|
||||
```bash
|
||||
python3 ok -q <specified function> #会和你要补充的代码一起给出
|
||||
```
|
||||
|
||||
默认情况下,只有没有通过的测试才会出现。你可以使用-v 选项来显示所有的测试,包括你已经通过的测试
|
||||
|
||||
```bash
|
||||
python3 ok -v
|
||||
```
|
||||
|
||||
有时我们会看到类似这样的 ok 指令
|
||||
|
||||
```bash
|
||||
python3 ok -q control -u
|
||||
```
|
||||
|
||||
在终端中输入后,需要按照要求回答问题,有些时候会做为某些函数测试的前置条件
|
||||
|
||||
一般情况下,执行上述 ok 指令后,都会在终端里提示输入 Berkeley 账号进行提交,这时候输入 `Ctrl+C` 退出即可;不过我们可以在代码后面加上 `--local` 进行本地测试;所有的测试都可以本地完成,不需要联网
|
||||
|
||||

|
||||
|
||||
关于使用 Ok 命令的更多信息,请在[此处](https://inst.eecs.berkeley.edu/~cs61a/fa22/articles/using-ok/)了解更多
|
||||
|
||||
## 在学习过程中,你可以能会遇到的问题和解决方法
|
||||
|
||||
1. 在 CS61A 的学习过程中,你可能会找不到 61A 的每一个 lab、hw、disc、proj 的答案,这是因为 61A 是不断更新并进行授课的,所以每过一个季度 61A 是会进行换季的,所以为了避免这个问题,请尽早将 61A 主页的每一个答案保存下来。如果你已经遇到了这种问题,那么向已经学习了这门课的学长学姐求助是个不错的选择。
|
||||
2. 如果出现以下情况,这说明你的并没有在测试对象的目录进行测试,最简单解决办法就是在你对应位置的目录进行鼠标右键点击“在终端中打开”进行输入测试。
|
||||

|
||||
|
||||
3. 如果输入了命令后回车没有任何反应,请试试将测试代码的 python3 变为 python 或者 py 进行操作,如果还没有解决请仔细阅读 61A hw 部分的 Using ok,链接一般位于 HW 01 的开头。
|
||||
4. 如果在解决问题的过程中遇到了问题,那就多读几遍题目吧,题干中或许会给出 `Hint`,这可能很有用
|
||||
|
||||
这是 cs61a 的官网[https://cs61a.org/](https://cs61a.org/)
|
||||
|
||||
如果你觉得全英教学对你来说比较困难,可以参考[2.5 以理工科的方式阅读英语](../2.高效学习/2.5以理工科的方式阅读英语.md)
|
||||
|
||||
也可以看看我们本地化之后的 cs61a 课程,我们尽可能准确和符合中文阅读习惯地翻译了 textbook,但我们保留了作业中的英语(绝对不是因为偷懒),来锻炼同学们的英语能力
|
||||
|
||||
英文学习的痛苦比不上接触国外优秀课程带来的快乐,请保持初心,砥砺前进,祝愿同学们都能有一个有趣的学习体验 🥰
|
||||
@@ -1,19 +0,0 @@
|
||||
# 关于 CS61A
|
||||
|
||||
这是 CS 61A Fall 2022 的网站:
|
||||
|
||||
[https://inst.eecs.berkeley.edu/~cs61a/fa22/](https://inst.eecs.berkeley.edu/~cs61a/fa22/)
|
||||
|
||||
在这个目录下的文章上传的 pdf 基本都是英文的,还请做好准备。
|
||||
|
||||
在每个 disc、lab、hw 和 proj 下,都会有相对应的.zip 文件,文件里装的是在学习过程中所需要的代码。
|
||||
|
||||
在文章的最后会附上 sol。
|
||||
|
||||
当然,在没有头绪的时候,你可以选择参考 sol,也可以重新学习视频和教科书,也可以改变做题的顺序。
|
||||
|
||||
ps:哪怕是 cs61a 这样优秀的课程,给出的解决方案里也会有不是那么正确的代码(它并不能处理所有的情况)
|
||||
|
||||
pps:如果你找到了 sol 里不是那么正确的代码,请联系我们,我们将新建一个文档来记录 sol 中这些“错误”的代码
|
||||
|
||||
ppps:当然存在“错误”的代码的概率很小,你发现它们的概率就更小了
|
||||
@@ -69,9 +69,9 @@ walk(dir);
|
||||
|
||||
此外,还有广义的设计模式,这块内容就很多了,但也是讨论如何降低代码复杂度的,包括:
|
||||
|
||||
1. 程序代码怎么组织(在开发 web 应用时这块经常有争议,可以参考 [https://cloud.tencent.com/developer/article/1837487](https://cloud.tencent.com/developer/article/1837487))
|
||||
1. 程序代码怎么组织(在开发 web 应用时这块经常有争议,可以参考 [浅析整洁架构之道 (二) 初步了解 The Clean Architecture](https://cloud.tencent.com/developer/article/1837487))
|
||||
2. 程序之间怎么组织(是放在一个编成个大的,还是微服务,还是用 FaaS 之类的变体)
|
||||
3. 一些帮助减少程序复杂度的代码原则:[https://zhuanlan.zhihu.com/p/82324809](https://zhuanlan.zhihu.com/p/82324809)
|
||||
3. 一些帮助减少程序复杂度的代码原则:[设计模式之 SOLID 原则](https://zhuanlan.zhihu.com/p/82324809)
|
||||
|
||||
这部分的学习不能操之过急。个人的建议是:
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# 附加模块:Linux
|
||||
|
||||
本来这个模块在编程模块内,但是鉴于大家都反应做这一块非常难,因此我将他提出作为一个额外的附加模块。
|
||||
::: warning 😇 本来这个模块在编程模块内,但是鉴于大家都反应做这一块非常难,因此我将他提出作为一个额外的附加模块。
|
||||
|
||||
如果你想尝试使用 Linux 编程或者想了解更多计算机科学领域知识,你可以学习并阅览本部分内容。
|
||||
|
||||
当然你也可以先尝试完成第三部分的一些内容再回过头解决本部分的内容。
|
||||
|
||||
可能会花费你大量的时间,并且让你感受到非常困难,但是可以保证的是:你的一切投入,都是有收获的。
|
||||
:::
|
||||
|
||||
# What???Linux???
|
||||
## What???Linux???
|
||||
|
||||
大家可能知道我们的电脑是 Windows 作为操作系统的。
|
||||
|
||||
@@ -16,12 +17,12 @@
|
||||
|
||||
它既免费也自由 (能知道它内部的实现),而且互联网上有丰富的 (英文) 文档。
|
||||
|
||||
它的设计继承自 “Keep it simple, stupid” 的 UNIX,这个经典的设计背后的动机反而更容易为第一次接触操作系统的初学者所理解。让我们看看它的威力:
|
||||
它的设计继承自“Keep it simple, stupid”的 UNIX,这个经典的设计背后的动机反而更容易为第一次接触操作系统的初学者所理解。让我们看看它的威力:
|
||||
|
||||
- 首先,操作系统里的一切对象都用文件表示 (Everything is a file)。进程、设备……都可以在任何编程语言里用文件 API 访问。
|
||||
- Linux 的命令行 Shell 是一门编程语言——没错,你每天都在 “编程”!更准确地说,Shell 的功能就是把你想要做的事情 (类似自然语言描述的代码) 翻译成操作系统能看懂的文件/进程管理 API 调用。
|
||||
- Linux 的命令行 Shell 是一门编程语言——没错,你每天都在“编程”!更准确地说,Shell 的功能就是把你想要做的事情 (类似自然语言描述的代码) 翻译成操作系统能看懂的文件/进程管理 API 调用。
|
||||
|
||||
# Why Linux???
|
||||
## Why Linux???
|
||||
|
||||
作为一个双系统用户体验者来说,他除了玩游戏不那么方便以外,可以更为高效且便捷的办到 Windows 费很大力气才能办到的事情。
|
||||
|
||||
@@ -29,11 +30,11 @@
|
||||
|
||||
并且目前,服务器上为了保证低损耗,高效率,基本上百分之九十九都是 Linux 的系统,实验室的服务器也是 Linux 系统。
|
||||
|
||||
简单来说就是,你如果想干点事情,肯定要靠 Linux,因此学会 Linux 的操作是不可或缺的
|
||||
简单来说就是,你如果想干点事情,肯定要靠 Linux,因此学会 Linux 的操作是不可或缺的
|
||||
|
||||
而且我个人认为,linux的自由性对于CSer来说非常适合,他不会阻止你干任何操作,你可以充分体会所以你的命令带来的影响(rm -rf /)
|
||||
而且我个人认为,linux 的自由性对于 CSer 来说非常适合,他不会阻止你干任何操作,你可以充分体会所以你的命令带来的影响 (rm -rf /)
|
||||
|
||||
## GUI 与 CLI
|
||||
### GUI 与 CLI
|
||||
|
||||
诚然,我们现在的图形化界面(GUI)已经深入到了生活的方方面面,但是优劣如何对比呢?
|
||||
|
||||
@@ -41,26 +42,20 @@
|
||||
|
||||
这篇文章详细对比了图形化界面和单纯的终端命令的优劣
|
||||
|
||||
# How Linux???
|
||||
## How Linux???
|
||||
|
||||
那么这么好的东西哪里可以获得呢?
|
||||
|
||||
因为 Linux 有诸多发行版本,我在这里建议大家使用 Ubuntu22.04 作为主要版本进行使用
|
||||
|
||||
如果你很猛,去试试arch!
|
||||
如果你很猛,去试试 arch!
|
||||
|
||||
任务:装 Ubuntu22.04或者debian,如果你想删了自己的系统,可以试试deepin,当然,也会有一些兼容性问题,不过很支持一些中文软件
|
||||
任务:装 Ubuntu22.04 或者 debian,如果你想删了自己的系统,可以试试 deepin,当然,也会有一些兼容性问题,不过会支持一些中文软件
|
||||
|
||||
tip1:推荐这个 [3.Y.1VMware 的安装与安装 ubuntu22.04 系统](3.Y.1VMware%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E5%AE%89%E8%A3%85Ubuntu22.04%E7%B3%BB%E7%BB%9F.md)
|
||||
|
||||
tip2:可以使用 WSL[3.Y.2WSL 的安装](3.Y.2WSL%E7%9A%84%E5%AE%89%E8%A3%85.md),<del>但是我更建议实装到电脑上双系统之类的</del>(我不建议 bug 很多 例如开不开机 要开好几回 网络连不上等),正好锻炼一下<del>装系统</del>倒腾的能力。大可不必删了 windows 换成 ubuntu。
|
||||
tip2:可以使用 WSL[3.Y.2WSL 的安装](3.Y.2WSL%E7%9A%84%E5%AE%89%E8%A3%85.md),<del>但是我更建议实装到电脑上双系统之类的</del>,正好锻炼一下<del>装系统</del>倒腾的能力。大可不必删了 windows 换成 ubuntu。
|
||||
|
||||
tip3:前两个 tip 二选一
|
||||
|
||||
任务:阅读 GUI 与命令行之间对比的文章,尝试开始阅读英文文章
|
||||
|
||||
# 教程推荐
|
||||
|
||||
[https://missing-semester-cn.github.io/](https://missing-semester-cn.github.io/)
|
||||
|
||||
计算机教育中缺失的一课
|
||||
任务:阅读 GUI 与命令行之间对比的文章,尝试开始阅读英文文章
|
||||
|
||||
31
3.编程思维体系构建/3.Y.0计算机教育中缺失的一课.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 计算机教育中缺失的一课
|
||||
|
||||
Author : ek1ng , Data : 2023.07.24 , Mail : <ek1ng@qq.com>
|
||||
|
||||
## 计算机教育中缺失的一课
|
||||
>
|
||||
> [https://missing-semester-cn.github.io/](https://missing-semester-cn.github.io/)
|
||||
> [https://ek1ng.com/Missing%20Semester.html](https://ek1ng.com/Missing%20Semester.html)
|
||||
|
||||
这是一份国外的课程,主要专注于各类工具的使用,可以看一看课程的介绍:
|
||||
|
||||
>大学里的计算机课程通常专注于讲授从操作系统到机器学习这些学院派的课程或主题,而对于如何精通工具这一主题则往往会留给学生自行探索。在这个系列课程中,我们讲授命令行、强大的文本编辑器的使用、使用版本控制系统提供的多种特性等等。学生在他们受教育阶段就会和这些工具朝夕相处(在他们的职业生涯中更是这样)。
|
||||
>因此,花时间打磨使用这些工具的能力并能够最终熟练地、流畅地使用它们是非常有必要的。
|
||||
|
||||
以及相应的目录:
|
||||
|
||||
- **1/13**: [课程概览与 shell](https://missing-semester-cn.github.io/2020/course-shell/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//course-shell-solution)
|
||||
- **1/14**: [Shell 工具和脚本](https://missing-semester-cn.github.io/2020/shell-tools/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//shell-tools-solution)
|
||||
- **1/15**: [编辑器 (Vim)](https://missing-semester-cn.github.io/2020/editors/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//editors-solution)
|
||||
- **1/16**: [数据整理](https://missing-semester-cn.github.io/2020/data-wrangling/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//data-wrangling-solution)
|
||||
- **1/21**: [命令行环境](https://missing-semester-cn.github.io/2020/command-line/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//command-line-solution)
|
||||
- **1/22**: [版本控制 (Git)](https://missing-semester-cn.github.io/2020/version-control/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//version-control-solution)
|
||||
- **1/23**: [调试及性能分析](https://missing-semester-cn.github.io/2020/debugging-profiling/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//debugging-profiling-solution)
|
||||
- **1/27**: [元编程](https://missing-semester-cn.github.io/2020/metaprogramming/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//metaprogramming-solution)
|
||||
- **1/28**: [安全和密码学](https://missing-semester-cn.github.io/2020/security/)[](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//security-solution)
|
||||
- **1/29**: [大杂烩](https://missing-semester-cn.github.io/2020/potpourri/)
|
||||
- **1/30**: [提问&回答](https://missing-semester-cn.github.io/2020/qa/)
|
||||
|
||||
目录中的内容和这份`Wiki`中不少内容重合,当然我觉得作为一份校园学生为爱发电多人合作编辑的`Wiki`,内容有重复冗余再所难免。我比较推荐以这份教材作为计算机工具的学习,下面是我大一时学习课程的一些记录,这些课程都比较缺少一些中文的文章,能够直接看英文的一些材料当然很好,但是如果遇到一些困难,也许你可以在这里找到我先前所踩的坑。
|
||||
|
||||
> [The Missing Semester of Your CS Education](https://ek1ng.com/Missing%20Semester.html)
|
||||
@@ -1,6 +1,10 @@
|
||||
# VMware 的安装与安装 Ubuntu22.04 系统
|
||||
|
||||
与 wsl 安装二选一 安装了 wsl 不用 VMware
|
||||
::: warning 💡
|
||||
一般与 wsl 安装二选一,因为都是虚拟系统,安装了 wsl 不用 VMware
|
||||
|
||||
文章撰写于 2022 年,可能其中的一些内容已过时。
|
||||
:::
|
||||
|
||||
首先下载 VMware
|
||||
|
||||
@@ -8,37 +12,42 @@
|
||||
|
||||
如果是 pro17 版本(key <strong>JU090-6039P-08409-8J0QH-2YR7F</strong><strong> </strong>)
|
||||
|
||||
本文写的时候用的版本是 pro16 ,但目前已经更新到 pro17 所以来更新个 key (如下安装与 16 版本无异)
|
||||
本文写的时候用的版本是 pro16,但目前已经更新到 pro17 所以来更新个 key(如下安装与 16 版本无异)
|
||||
|
||||
[https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html](https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html)
|
||||
|
||||
一路下一步
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
这俩我推荐勾掉
|
||||
|
||||

|
||||

|
||||
|
||||
安装过后点许可证 输上面的 key 激活
|
||||
|
||||
[https://mirror.nju.edu.cn/ubuntu-releases/22.04/ubuntu-22.04.2-desktop-amd64.iso](https://mirror.nju.edu.cn/ubuntu-releases/22.04/ubuntu-22.04.2-desktop-amd64.iso)
|
||||
[https://mirror.nju.edu.cn/ubuntu-releases/22.04](https://mirror.nju.edu.cn/ubuntu-releases/22.04)
|
||||
|
||||
去这里下载 Ubuntu22.04 镜像包 iso 选择 `ubuntu-<version>-desktop-amd64.iso`
|
||||
|
||||
:::tip
|
||||
这里推荐使用多线程下载器下载,比如 [IDM](../2.高效学习/2.2优雅的使用工具.md),如果直接用浏览器下载(线程少)可能会出现下载慢、下载失败的情况。
|
||||
:::
|
||||
|
||||
去这里下载 Ubuntu22.04 镜像包 iso
|
||||
|
||||
下好回到 VMware
|
||||
|
||||

|
||||

|
||||
|
||||
创建新的虚拟机-典型(推荐)-下一步-安装程序 iso 选中你刚下的 iso 下一步
|
||||
创建新的虚拟机 - 典型(推荐)- 下一步 - 安装程序 iso 选中你刚下的 iso 下一步
|
||||
|
||||

|
||||

|
||||
|
||||
这里填你一会儿要登录 linux 的个人信息
|
||||
|
||||

|
||||

|
||||
|
||||
这里建议把位置改到其他盘
|
||||
|
||||
@@ -46,21 +55,21 @@
|
||||
|
||||
启动后进入 Ubuntu 安装
|
||||
|
||||

|
||||

|
||||
|
||||
键盘映射 直接 continue
|
||||
|
||||
接下来一路 continue install now
|
||||
|
||||

|
||||

|
||||
|
||||
最后 restart
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
这个 skip
|
||||
|
||||
@@ -68,15 +77,15 @@
|
||||
|
||||
点右上角 settings
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
然后按指引 restart 系统
|
||||
|
||||

|
||||

|
||||
|
||||
会提示你要不要重新命名这些用户下的文件夹
|
||||
|
||||
@@ -84,27 +93,27 @@
|
||||
|
||||
如果你的语言还没有变过来的话
|
||||
|
||||

|
||||

|
||||
|
||||
点击这个他会安装语言
|
||||
|
||||

|
||||

|
||||
|
||||
把汉语拖到英文之上 点应用到整个系统
|
||||
|
||||

|
||||

|
||||
|
||||
右上角 logout 重新登陆 就是中文辣
|
||||
|
||||
最后在设置-电源把息屏改成从不
|
||||
最后在设置 - 电源把息屏改成从不
|
||||
|
||||

|
||||

|
||||
|
||||
<strong>至此 恭喜安装完成!</strong>
|
||||
|
||||
之后就可以在桌面上右键
|
||||
|
||||

|
||||

|
||||
|
||||
打开命令行
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# WSL 的安装
|
||||
|
||||
与 VMware 安装二选一 安装了 VMware 不用 wsl
|
||||
::: warning 💡与 VMware 安装二选一 安装了 VMware 不用 wsl
|
||||
:::
|
||||
|
||||
先说<strong>坏处</strong>:
|
||||
|
||||
@@ -15,10 +16,10 @@
|
||||
|
||||
(现在可能是只开 `适用于Linux的windows子系统`)
|
||||
|
||||

|
||||

|
||||
|
||||
如果你的 windows 版本为<strong>家庭版</strong> 那么 hyperv 选项是没有的
|
||||
|
||||
你需要右键以管理员权限打开以下脚本来强行开启 hyperv
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,131 +1,134 @@
|
||||
# Linux 初探索
|
||||
|
||||
如果你是第一次接触link,请一边仔细阅读,一边尝试敲命令在终端内。
|
||||
如果你是第一次接触 link,请一边仔细阅读,一边尝试敲命令在终端内。
|
||||
|
||||
有一点非常重要,这章节的内容到后面会略为困难,并且 linux 知识繁杂多样。
|
||||
|
||||
希望你可以参考这个链接!
|
||||
[一些基本常识](https://linux.cn/article-6160-1.html)
|
||||
|
||||
当然,你也可以从蓝桥云课开始,不过学会linux的最好办法是删掉你的windows换一个linux系统当开发环境,比任何临时的训练都有效!
|
||||
当然,你也可以从蓝桥云课开始,不过学会 linux 的最好办法是删掉你的 windows 换一个 linux 系统当开发环境,比任何临时的训练都有效!
|
||||
|
||||
# 探索命令行
|
||||
[蓝桥云课-Linux 基础入门](https://www.lanqiao.cn/courses/1)
|
||||
|
||||
Linux 命令行中的命令使用格式都是相同的:
|
||||
## 探索命令行
|
||||
|
||||
```
|
||||
Linux 命令行中的命令使用格式都是相同的:
|
||||
|
||||
```bash
|
||||
命令名称 参数1 参数2 参数3 ...
|
||||
```
|
||||
|
||||
参数之间用任意数量的空白字符分开. 关于命令行, 可以先阅读[一些基本常识](https://linux.cn/article-6160-1.html). 然后我们介绍最常用的一些命令:
|
||||
参数之间用任意数量的空白字符分开。关于命令行,可以先阅读[一些基本常识](https://linux.cn/article-6160-1.html). 然后我们介绍最常用的一些命令:
|
||||
|
||||
- (重要)首先教一个命令 `sudo su` 进入 root 账户(敲完之后会让你敲当前登录账户的密码 密码敲得过程中没有*****这种传统敲密码的提示 为 linux 传统艺能 其实是敲进去了),因为本身普通账户没什么权限,会出现处处的权限提示,建议直接使用 root 账户。
|
||||
|
||||
```
|
||||
这里有一个彩蛋 (如果你用的是centos的话)
|
||||
<strong>当用户第一次使用sudo权限时CentOS的系统提示:</strong>
|
||||
```txt
|
||||
这里有一个彩蛋(如果你用的是 centos 的话)
|
||||
当用户第一次使用 sudo 权限时 CentOS 的系统提示:
|
||||
我们信任您已经从系统管理员那里了解了日常注意事项。
|
||||
总结起来无外乎这三点:
|
||||
#1) 尊重别人的隐私。
|
||||
#2) 输入前要先考虑(后果和风险)。
|
||||
#2) 输入前要先考虑 (后果和风险)。
|
||||
#3) 权力越大,责任越大。
|
||||
```
|
||||
|
||||
- `ls` 用于列出当前目录(即"文件夹")下的所有文件(或目录). 目录会用蓝色显示. `ls -l` 可以显示详细信息.
|
||||
- `pwd` 能够列出当前所在的目录.
|
||||
- `cd DIR` 可以切换到 `DIR` 目录. 在 Linux 中, 每个目录中都至少包含两个目录: `.` 指向该目录自身, `..` 指向它的上级目录. 文件系统的根是 `/`.
|
||||
- `touch NEWFILE` 可以创建一个内容为空的新文件 `NEWFILE`, 若 `NEWFILE` 已存在, 其内容不会丢失.
|
||||
- `cp SOURCE DEST` 可以将 `SOURCE` 文件复制为 `DEST` 文件; 如果 `DEST` 是一个目录, 则将 `SOURCE` 文件复制到该目录下.
|
||||
- `mv SOURCE DEST` 可以将 `SOURCE` 文件重命名为 `DEST` 文件; 如果 `DEST` 是一个目录, 则将 `SOURCE` 文件移动到该目录下.
|
||||
- `mkdir DIR` 能够创建一个 `DIR` 目录.
|
||||
- `rm FILE` 能够删除 `FILE` 文件; 如果使用 `-r` 选项则可以递归删除一个目录. 删除后的文件无法恢复, 使用时请谨慎!
|
||||
- `man` 可以查看命令的帮助. 例如 `man ls` 可以查看 `ls` 命令的使用方法. 灵活应用 `man` 和互联网搜索, 可以快速学习新的命令.
|
||||
- `ls` 用于列出当前目录 (即"文件夹") 下的所有文件 (或目录). 目录会用蓝色显示。`ls -l` 可以显示详细信息。
|
||||
- `pwd` 能够列出当前所在的目录。
|
||||
- `cd DIR` 可以切换到 `DIR` 目录。在 Linux 中,每个目录中都至少包含两个目录:`.` 指向该目录自身,`..` 指向它的上级目录。文件系统的根是 `/`.
|
||||
- `touch NEWFILE` 可以创建一个内容为空的新文件 `NEWFILE`, 若 `NEWFILE` 已存在,其内容不会丢失。
|
||||
- `cp SOURCE DEST` 可以将 `SOURCE` 文件复制为 `DEST` 文件; 如果 `DEST` 是一个目录,则将 `SOURCE` 文件复制到该目录下。
|
||||
- `mv SOURCE DEST` 可以将 `SOURCE` 文件重命名为 `DEST` 文件; 如果 `DEST` 是一个目录,则将 `SOURCE` 文件移动到该目录下。
|
||||
- `mkdir DIR` 能够创建一个 `DIR` 目录。
|
||||
- `rm FILE` 能够删除 `FILE` 文件; 如果使用 `-r` 选项则可以递归删除一个目录。删除后的文件无法恢复,使用时请谨慎!
|
||||
- `man` 可以查看命令的帮助。例如 `man ls` 可以查看 `ls` 命令的使用方法。灵活应用 `man` 和互联网搜索,可以快速学习新的命令。
|
||||
|
||||
`man` 的功能不仅限于此. `man` 后可以跟两个参数, 可以查看不同类型的帮助(请在互联网上搜索). 例如当你不知道 C 标准库函数 `freopen` 如何使用时, 可以键入命令
|
||||
`man` 的功能不仅限于此。`man` 后可以跟两个参数,可以查看不同类型的帮助 (请在互联网上搜索). 例如当你不知道 C 标准库函数 `freopen` 如何使用时,可以键入命令
|
||||
|
||||
```
|
||||
```bash
|
||||
man 3 freopen
|
||||
```
|
||||
|
||||
## <strong>统计代码行数</strong>
|
||||
### <strong>统计代码行数</strong>
|
||||
|
||||
第一个例子是统计一个目录中(包含子目录)中的代码行数. 如果想知道当前目录下究竟有多少行的代码, 就可以在命令行中键入如下命令:
|
||||
第一个例子是统计一个目录中 (包含子目录) 中的代码行数。如果想知道当前目录下究竟有多少行的代码,就可以在命令行中键入如下命令:
|
||||
|
||||
```
|
||||
```bash
|
||||
find . | grep '\.c$\|\.h$' | xargs wc -l
|
||||
```
|
||||
|
||||
如果用 `man find` 查看 `find` 操作的功能, 可以看到 `find` 是搜索目录中的文件. Linux 中一个点 `.` 始终表示 Shell 当前所在的目录, 因此 `find .` 实际能够列出当前目录下的所有文件. 如果在文件很多的地方键入 `find .`, 将会看到过多的文件, 此时可以按 `CTRL + c` 退出.
|
||||
如果用 `man find` 查看 `find` 操作的功能,可以看到 `find` 是搜索目录中的文件。Linux 中一个点 `.` 始终表示 Shell 当前所在的目录,因此 `find .` 实际能够列出当前目录下的所有文件。如果在文件很多的地方键入 `find .`, 将会看到过多的文件,此时可以按 `CTRL + c` 退出。
|
||||
|
||||
同样, 用 `man` 查看 `grep` 的功能——"print lines matching a pattern". `grep` 实现了输入的过滤, 我们的 `grep` 有一个参数, 它能够匹配以 `.c` 或 `.h` 结束的文件. 正则表达式是处理字符串非常强大的工具之一, 每一个程序员都应该掌握其相关的知识. ? 上述的 `grep` 命令能够提取所有 `.c` 和 `.h` 结尾的文件.
|
||||
同样,用 `man` 查看 `grep` 的功能——"print lines matching a pattern". `grep` 实现了输入的过滤,我们的 `grep` 有一个参数,它能够匹配以 `.c` 或 `.h` 结束的文件。正则表达式是处理字符串非常强大的工具之一,每一个程序员都应该掌握其相关的知识。? 上述的 `grep` 命令能够提取所有 `.c` 和 `.h` 结尾的文件。
|
||||
|
||||
刚才的 `find` 和 `grep` 命令, 都从标准输入中读取数据, 并输出到标准输出. 关于什么是标准输入输出, 请参考[这里](http://en.wikipedia.org/wiki/Standard_streams). 连接起这两个命令的关键就是管道符号 `|`. 这一符号的左右都是 Shell 命令, `A | B` 的含义是创建两个进程 `A` 和 `B`, 并将 `A` 进程的标准输出连接到 `B` 进程的标准输入. 这样, 将 `find` 和 `grep` 连接起来就能够筛选出当前目录(`.`)下所有以 `.c` 或 `.h` 结尾的文件.
|
||||
刚才的 `find` 和 `grep` 命令,都从标准输入中读取数据,并输出到标准输出。关于什么是标准输入输出,请参考[这里](http://en.wikipedia.org/wiki/Standard_streams). 连接起这两个命令的关键就是管道符号 `|`. 这一符号的左右都是 Shell 命令,`A | B` 的含义是创建两个进程 `A` 和 `B`, 并将 `A` 进程的标准输出连接到 `B` 进程的标准输入。这样,将 `find` 和 `grep` 连接起来就能够筛选出当前目录 (`.`) 下所有以 `.c` 或 `.h` 结尾的文件。
|
||||
|
||||
我们最后的任务是统计这些文件所占用的总行数, 此时可以用 `man` 查看 `wc` 命令. `wc` 命令的 `-l` 选项能够计算代码的行数. `xargs` 命令十分特殊, 它能够将标准输入转换为参数, 传送给第一个参数所指定的程序. 所以, 代码中的 `xargs wc -l` 就等价于执行 `wc -l aaa.c bbb.c include/ccc.h ...`, 最终完成代码行数统计.
|
||||
我们最后的任务是统计这些文件所占用的总行数,此时可以用 `man` 查看 `wc` 命令。`wc` 命令的 `-l` 选项能够计算代码的行数。`xargs` 命令十分特殊,它能够将标准输入转换为参数,传送给第一个参数所指定的程序。所以,代码中的 `xargs wc -l` 就等价于执行 `wc -l aaa.c bbb.c include/ccc.h ...`, 最终完成代码行数统计。
|
||||
|
||||
## <strong>统计磁盘使用情况</strong>
|
||||
### <strong>统计磁盘使用情况</strong>
|
||||
|
||||
以下命令统计 `/usr/share` 目录下各个目录所占用的磁盘空间:
|
||||
以下命令统计 `/usr/share` 目录下各个目录所占用的磁盘空间:
|
||||
|
||||
```
|
||||
```bash
|
||||
du -sc /usr/share/* | sort -nr
|
||||
```
|
||||
|
||||
`du` 是磁盘空间分析工具, `du -sc` 将目录的大小顺次输出到标准输出, 继而通过管道传送给 `sort`. `sort` 是数据排序工具, 其中的选项 `-n` 表示按照数值进行排序, 而 `-r` 则表示从大到小输出. `sort` 可以将这些参数连写在一起.
|
||||
`du` 是磁盘空间分析工具,`du -sc` 将目录的大小顺次输出到标准输出,继而通过管道传送给 `sort`. `sort` 是数据排序工具,其中的选项 `-n` 表示按照数值进行排序,而 `-r` 则表示从大到小输出。`sort` 可以将这些参数连写在一起。
|
||||
|
||||
然而我们发现, `/usr/share` 中的目录过多, 无法在一个屏幕内显示. 此时, 我们可以再使用一个命令: `more` 或 `less`.
|
||||
然而我们发现,`/usr/share` 中的目录过多,无法在一个屏幕内显示。此时,我们可以再使用一个命令:`more` 或 `less`.
|
||||
|
||||
```
|
||||
```bash
|
||||
du -sc /usr/share/* | sort -nr | more
|
||||
```
|
||||
|
||||
此时将会看到输出的前几行结果. `more` 工具使用空格翻页, 并可以用 `q` 键在中途退出. `less` 工具则更为强大, 不仅可以向下翻页, 还可以向上翻页, 同样使用 `q` 键退出. 这里还有一个[关于 less 的小故事](http://en.wikipedia.org/wiki/Less_(Unix)).
|
||||
此时将会看到输出的前几行结果。`more` 工具使用空格翻页,并可以用 `q` 键在中途退出。`less` 工具则更为强大,不仅可以向下翻页,还可以向上翻页,同样使用 `q` 键退出。这里还有一个[关于 less 的小故事](http://en.wikipedia.org/wiki/Less_(Unix)).
|
||||
|
||||
## <strong>在 Linux 下编写 Hello World 程序</strong>
|
||||
### <strong>在 Linux 下编写 Hello World 程序</strong>
|
||||
|
||||
Linux 中用户的主目录是 `/home/用户名称`, 如果你的用户名是 `user`, 你的主目录就是 `/home/user`. 用户的 `home` 目录可以用波浪符号 `~` 替代, 例如临时文件目录 `/home/user/Templates` 可以简写为 `~/Templates`. 现在我们就可以进入主目录并编辑文件了. 如果 `Templates` 目录不存在, 可以通过 `mkdir` 命令创建它:
|
||||
Linux 中用户的主目录是 `/home/用户名称`, 如果你的用户名是 `user`, 你的主目录就是 `/home/user`. 用户的 `home` 目录可以用波浪符号 `~` 替代,例如临时文件目录 `/home/user/Templates` 可以简写为 `~/Templates`. 现在我们就可以进入主目录并编辑文件了。如果 `Templates` 目录不存在,可以通过 `mkdir` 命令创建它:
|
||||
|
||||
```
|
||||
```bash
|
||||
cd ~
|
||||
mkdir Templates
|
||||
```
|
||||
|
||||
创建成功后, 键入
|
||||
创建成功后,键入
|
||||
|
||||
```
|
||||
```bash
|
||||
cd Templates
|
||||
```
|
||||
|
||||
可以完成目录的切换. 注意在输入目录名时, `tab` 键可以提供联想.
|
||||
可以完成目录的切换。注意在输入目录名时,`tab` 键可以提供联想。
|
||||
|
||||
##### <strong> 你感到键入困难吗?</strong>
|
||||
#### <strong> 你感到键入困难吗?</strong>
|
||||
|
||||
你可能会经常要在终端里输入类似于
|
||||
::: warning 💡 你可能会经常要在终端里输入类似于
|
||||
|
||||
cd AVeryVeryLongFileName
|
||||
|
||||
的命令, 你一定觉得非常烦躁. 回顾上面所说的原则之一: 如果你感到有什么地方不对, 就一定有什么好办法来解决. 试试 `tab` 键吧.
|
||||
的命令,你一定觉得非常烦躁。回顾上面所说的原则之一:如果你感到有什么地方不对,就一定有什么好办法来解决。试试 `tab` 键吧。
|
||||
|
||||
Shell 中有很多这样的小技巧, 你也可以使用其他的 Shell 例如 zsh, 提供更丰富好用的功能. 总之, 尝试和改变是最重要的.
|
||||
Shell 中有很多这样的小技巧,你也可以使用其他的 Shell 例如 zsh, 提供更丰富好用的功能。总之,尝试和改变是最重要的。
|
||||
:::
|
||||
|
||||
进入正确的目录后就可以编辑文件了, 开源世界中主流的两大编辑器是 `vi(m)` 和 `emacs`, 你可以使用其中的任何一种. 如果你打算使用 `emacs`, 你还需要安装它
|
||||
进入正确的目录后就可以编辑文件了,开源世界中主流的两大编辑器是 `vi(m)` 和 `emacs`, 你可以使用其中的任何一种。如果你打算使用 `emacs`, 你还需要安装它
|
||||
|
||||
```
|
||||
```bash
|
||||
apt-get install emacs
|
||||
```
|
||||
|
||||
`vi` 和 `emacs` 这两款编辑器都需要一定的时间才能上手, 它们共同的特点是需要花较多的时间才能适应基本操作方式(命令或快捷键), 但一旦熟练运用, 编辑效率就比传统的编辑器快很多.
|
||||
`vi` 和 `emacs` 这两款编辑器都需要一定的时间才能上手,它们共同的特点是需要花较多的时间才能适应基本操作方式 (命令或快捷键), 但一旦熟练运用,编辑效率就比传统的编辑器快很多。
|
||||
|
||||
进入了正确的目录后, 输入相应的命令就能够开始编辑文件. 例如输入
|
||||
进入了正确的目录后,输入相应的命令就能够开始编辑文件。例如输入
|
||||
|
||||
```
|
||||
```bash
|
||||
vi hello.c
|
||||
或emacs hello.c
|
||||
```
|
||||
|
||||
就能开启一个文件编辑. 例如可以键入如下代码(对于首次使用 `vi` 或 `emacs` 的同学, 键入代码可能会花去一些时间, 在编辑的同时要大量查看网络上的资料):
|
||||
就能开启一个文件编辑。例如可以键入如下代码 (对于首次使用 `vi` 或 `emacs` 的同学,键入代码可能会花去一些时间,在编辑的同时要大量查看网络上的资料):
|
||||
|
||||
```
|
||||
```c
|
||||
#include <stdio.h>
|
||||
int main(void) {
|
||||
printf("Hello, Linux World!\n");
|
||||
@@ -134,23 +137,22 @@ int main(void) {
|
||||
```
|
||||
|
||||
> 相信你在写完代码之后苦于不知道怎么保存并退出,不用担心,这个是正常的,毕竟上面提到的两个文本编辑器都是以入门时的学习曲线及其陡峭而著称。
|
||||
> 对于 vi(m)风格的编辑器,你需要先按 `ESC` 返回 NORMAL 模式(具体处于那个模式可以观察窗口左下角,NORMAL 模式是空白的),再输入 `:wq` 来保存并退出(注意 `:` 是输入的一部分 )(`:q 仅退出` `:q! 不保存退出` )
|
||||
> 对于 vi(m) 风格的编辑器,你需要先按 `ESC` 返回 NORMAL 模式(具体处于那个模式可以观察窗口左下角,NORMAL 模式是空白的),再输入 `:wq` 来保存并退出(注意 `:` 是输入的一部分)(`:q 仅退出` `:q! 不保存退出` )
|
||||
>
|
||||
> [【保姆级入门】Vim 编辑器](https://www.bilibili.com/video/BV13t4y1t7Wg)
|
||||
>
|
||||
> <Bilibili bvid='BV13t4y1t7Wg'/>
|
||||
|
||||
保存后就能够看到 `hello.c` 的内容了. 终端中可以用 `cat hello.c` 查看代码的内容. 如果要将它编译, 可以使用 `gcc` 命令:
|
||||
保存后就能够看到 `hello.c` 的内容了。终端中可以用 `cat hello.c` 查看代码的内容。如果要将它编译,可以使用 `gcc` 命令:
|
||||
|
||||
```
|
||||
```bash
|
||||
gcc hello.c -o hello
|
||||
```
|
||||
|
||||
`gcc` 的 `-o` 选项指定了输出文件的名称, 如果将 `-o hello` 改为 `-o hi`, 将会生成名为 `hi` 的可执行文件. 如果不使用 `-o` 选项, 则会默认生成名为 `a.out` 的文件, 它的含义是 [assembler output](http://en.wikipedia.org/wiki/A.out). 在命令行输入
|
||||
`gcc` 的 `-o` 选项指定了输出文件的名称,如果将 `-o hello` 改为 `-o hi`, 将会生成名为 `hi` 的可执行文件。如果不使用 `-o` 选项,则会默认生成名为 `a.out` 的文件,它的含义是 [assembler output](http://en.wikipedia.org/wiki/A.out). 在命令行输入
|
||||
|
||||
```
|
||||
```bash
|
||||
./hello
|
||||
```
|
||||
|
||||
就能够运行改程序. 命令中的 `./` 是不能少的, 点代表了当前目录, 而 `./hello` 则表示当前目录下的 `hello` 文件. 与 Windows 不同, Linux 系统默认情况下并不查找当前目录, 这是因为 Linux 下有大量的标准工具(如 `test` 等), 很容易与用户自己编写的程序重名, 不搜索当前目录消除了命令访问的歧义.
|
||||
|
||||
就能够运行改程序。命令中的 `./` 是不能少的,点代表了当前目录,而 `./hello` 则表示当前目录下的 `hello` 文件。与 Windows 不同,Linux 系统默认情况下并不查找当前目录,这是因为 Linux 下有大量的标准工具 (如 `test` 等), 很容易与用户自己编写的程序重名,不搜索当前目录消除了命令访问的歧义。
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
# Vim 初探索
|
||||
|
||||
# 下载 vim
|
||||
## 下载 vim
|
||||
|
||||
vim 被称为编辑器之神
|
||||
|
||||
看到这一句可能就激发了你学习 vim 的热情,但是看完整篇文章和文章里面的所有参考资料,可能这股来之不易的热情也早就消失了。为了避免这种情况,我给一个小小的建议:
|
||||
::: warning 💡 看到这一句可能就激发了你学习 vim 的热情,但是看完整篇文章和文章里面的所有参考资料,可能这股来之不易的热情也早就消失了。为了避免这种情况,我给一个小小的建议:
|
||||
|
||||
1. 首先学会盲打,不会的话,不是很建议用 vim / Emacs 这样的编辑器,还是拥抱鼠标吧
|
||||
2. 学习使用 hjklia 这六个键,然后理解插入模式和普通模式,再了解怎么退出
|
||||
3. 使用 vim 作为日常的编辑工具,在你所有的代码编辑器里面都装上 vim 插件并使用,强迫自己习惯 hjkl 的移动和带模式的输入,习惯按 `<ESC>`
|
||||
4. 到这个时候你就会感觉的确可以不用鼠标了,但是有的时候会比较别扭,比如想新建一行时,得按 L 移到行尾,然后按 a 追加,再按回车,远远比鼠标麻烦有没有,这种情况就可以上网查询,`vim 如何新建一行`,就会学到 o 可以向下新建一行,O 可以向上新建一行,然后你就能自然地学会更多的操作。
|
||||
:::
|
||||
|
||||
因为其具有着非常完整的生态以及诸多配套的插件,但是第一次使用得你可能感觉很不习惯。
|
||||
|
||||
讲一个笑话,你如何获得一个随机字符串,只要让新人使用 vim 就好了。
|
||||
|
||||
不开玩笑,为了让你不小心在命令行模式下进入 vim 又不知道怎么退出时不需要拔电源来退出,先按几次 `<ESC>` 键(避免你之前不小心按到了 i 或 a 或 o 或等等按键)进入普通模式,然后顺序敲击 `:q`(冒号 和 q 两个按键 ),再按回车就可以退出了。
|
||||
::: waning 💡 不开玩笑,为了让你不小心在命令行模式下进入 vim 又不知道怎么退出时不需要拔电源来退出,先按几次 `<ESC>` 键(避免你之前不小心按到了 i 或 a 或 o 或等等按键)进入普通模式,然后顺序敲击 `:q`(冒号 和 q 两个按键 ),再按回车就可以退出了。
|
||||
:::
|
||||
|
||||
```
|
||||
```bash
|
||||
apt-get install vim
|
||||
```
|
||||
|
||||
但是我仍然推荐你尝试使用或者结合 VSC 一起使用,使用习惯后将有效提高你的开发效率。
|
||||
|
||||
# 如何学习 vim
|
||||
## 如何学习 vim
|
||||
|
||||
作为程序员,我们大部分时间都花在代码编辑上,所以花点时间掌握某个适合自己的编辑器是非常值得的。通常学习使用一个新的编辑器包含以下步骤:
|
||||
|
||||
@@ -33,25 +35,25 @@ apt-get install vim
|
||||
|
||||
如果您能够遵循上述步骤,并且坚持使用新的编辑器完成您所有的文本编辑任务,那么学习一个复杂的代码编辑器的过程一般是这样的:头两个小时,您会学习到编辑器的基本操作,例如打开和编辑文件、保存与退出、浏览缓冲区。当学习时间累计达到 20 个小时之后,您使用新编辑器的效率应该已经和使用老编辑器一样快。在此之后,其益处开始显现:有了足够的知识和肌肉记忆后,使用新编辑器将大大节省你的时间。而现代文本编辑器都是些复杂且强大的工具,永远有新东西可学:学的越多,效率越高。
|
||||
|
||||
# <strong>Vim 的哲学</strong>
|
||||
## <strong>Vim 的哲学</strong>
|
||||
|
||||
在编程的时候,你会把大量时间花在阅读/编辑而不是在写代码上。所以,Vim 是一个_多模态_编辑 器:它对于插入文字和操纵文字有不同的模式。Vim 是可编程的(可以使用 Vimscript 或者像 Python 一样的其他程序语言),Vim 的接口本身也是一个程序语言:键入操作(以及其助记名) 是命令,这些命令也是可组合的。Vim 避免了使用鼠标,因为那样太慢了;Vim 甚至避免用 上下左右键因为那样需要太多的手指移动。
|
||||
在编程的时候,你会把大量时间花在阅读/编辑而不是在写代码上。所以,Vim 是一个_多模态_编辑 器:它对于插入文字和操纵文字有不同的模式。Vim 是可编程的(可以使用 Vimscript 或者像 Python 一样的其他程序语言),Vim 的接口本身也是一个程序语言:键入操作(以及其助记名)是命令,这些命令也是可组合的。Vim 避免了使用鼠标,因为那样太慢了;Vim 甚至避免用 上下左右键因为那样需要太多的手指移动。
|
||||
|
||||
这样的设计哲学使得 Vim 成为了一个能跟上你思维速度的编辑器。
|
||||
|
||||
# 学习 Vim
|
||||
## 学习 Vim
|
||||
|
||||
如果想要使用他最基本的操作的话,在电脑上键入 vimtutor
|
||||
|
||||
会有官方的教程进行引导哦。
|
||||
|
||||
# 配置 vim
|
||||
## 配置 vim
|
||||
|
||||
vim 有大量的配置,通过更改./vimrc 文件或者安装插件都可以有效提高你的开发效率,定制属于你个人的编辑器哦~
|
||||
|
||||
快去试试吧
|
||||
|
||||
# 任务
|
||||
## 任务
|
||||
|
||||
定制 vim 成为你喜欢的模样,加装足够多的插件和更改足够多的配置让他满足以下几点或以上
|
||||
|
||||
@@ -67,7 +69,7 @@ vim 有大量的配置,通过更改./vimrc 文件或者安装插件都可以
|
||||
|
||||
[vim awesome](https://vimawesome.com/)
|
||||
|
||||
# 拓展阅读
|
||||
## 拓展阅读
|
||||
|
||||
[Learn-Vim(the Smart Way) 中文翻译](https://github.com/wsdjeg/Learn-Vim_zh_cn)
|
||||
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
4. 用 `touch` 在 `missing` 文件夹中新建一个叫 `semester` 的文件。
|
||||
5. 将以下内容一行一行地写入 `semester` 文件:
|
||||
|
||||
```
|
||||
```bash
|
||||
#!/bin/sh
|
||||
curl --head --silent https://missing.csail.mit.edu
|
||||
```
|
||||
|
||||
第一行可能有点棘手, `#` 在 Bash 中表示注释,而 `!` 即使被双引号(`"`)包裹也具有特殊的含义。 单引号(`'`)则不一样,此处利用这一点解决输入问题。更多信息请参考 <u>Bash quoting 手册</u>
|
||||
第一行可能有点棘手, `#` 在 Bash 中表示注释,而 `!` 即使被双引号(`"`)包裹也具有特殊的含义。单引号(`'`)则不一样,此处利用这一点解决输入问题。更多信息请参考 <u>Bash quoting 手册</u>
|
||||
|
||||
1. 尝试执行这个文件。例如,将该脚本的路径(`./semester`)输入到您的 shell 中并回车。如果程序无法执行,请使用 `ls` 命令来获取信息并理解其不能执行的原因。
|
||||
2. 查看 `chmod` 的手册(例如,使用 `man chmod` 命令)
|
||||
2. 查看 `chmod` 的手册 (例如,使用 `man chmod` 命令)
|
||||
3. 使用 `chmod` 命令改变权限,使 `./semester` 能够成功执行,不要使用 `sh semester` 来执行该程序。您的 shell 是如何知晓这个文件需要使用 `sh` 来解析呢?更多信息请参考:<u>shebang</u>
|
||||
4. 使用 `|` 和 `>` ,将 `semester` 文件输出的最后更改日期信息,写入主目录下的 `last-modified.txt` 的文件中
|
||||
5. 写一段命令来从 `/sys` 中获取笔记本的电量信息,或者台式机 CPU 的温度
|
||||
6. 使用shell编程写一个类似脚本的图书管理系统,包含增删改查四个功能
|
||||
6. 使用 shell 编程写一个类似脚本的图书管理系统,包含增删改查四个功能
|
||||
|
||||
当然,可能会有点困难我在这里附上一段参考代码
|
||||
|
||||
@@ -124,4 +124,3 @@ search()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
但是,他作为一个交通工具,你不开你就得腿着去学校。想想如果你从杭电走到西湖只能走着去,那是多么恐怖的一件事。
|
||||
|
||||
(当然,现在GPT的强大功能可以帮大伙解决相当多的工作,因此,我们可能需要掌握更多的逻辑思维能力和分解问题的能力,将问题简化之后用GPT解决也不失为一个选择)
|
||||
(当然,现在 GPT 的强大功能可以帮大伙解决相当多的工作,因此,我们可能需要掌握更多的逻辑思维能力和分解问题的能力,将问题简化之后用 GPT 解决也不失为一个选择)
|
||||
|
||||
因此本章节的目标是让大家面对一个实际问题,有使用编程解决的思路和能力。
|
||||
|
||||
@@ -16,37 +16,38 @@
|
||||
|
||||
本章提供了非常多的软实力文章,阅读相关软实力文章,你可以根据自己的情况构建适合自己一通百通的学习编程知识的方法论。
|
||||
|
||||
本章提供了相当完善的,足够面对多数需求的C语言体系结构,通过完成C语言的体系,你可以较为熟练地掌握C语言,并且对构建一个较小规模的项目组织和项目拆分有一定的理解
|
||||
本章提供了相当完善的,足够面对多数需求的 C 语言体系结构,通过完成 C 语言的体系,你可以较为熟练地掌握 C 语言,并且对构建一个较小规模的项目组织和项目拆分有一定的理解
|
||||
|
||||
python内容完成后,基本学习到如何使用python当一门工具使用,当有具体的需求可以进行后续的补充学习
|
||||
python 内容完成后,基本学习到如何使用 python 当一门工具使用,当有具体的需求可以进行后续的补充学习
|
||||
|
||||
与此同时,Git or Linux也是作为一个CSer 或者说想要提升自身效率的程序员,不可或缺的一个内容,希望你能掌握
|
||||
与此同时,Git or Linux 也是作为一个 CSer 或者说想要提升自身效率的程序员,不可或缺的一个内容,希望你能掌握
|
||||
|
||||
如果你要开始,推荐你从3.0开始阅读,然后挑选你喜欢的内容
|
||||
如果你要开始,推荐你从 3.0 开始阅读,然后挑选你喜欢的内容
|
||||
|
||||

|
||||
[【计算机科学速成课】[40集全/精校] - Crash Course Computer Science](https://www.bilibili.com/video/BV1EW411u7th)
|
||||
|
||||
<Bilibili bvid='BV1EW411u7th'/>
|
||||
|
||||

|
||||
|
||||
## 本章参考内容
|
||||
|
||||
[cs61a](https://cs61a.org/)
|
||||
|
||||
[CS自学指南](https://csdiy.wiki/)
|
||||
[CS 自学指南](https://csdiy.wiki/)
|
||||
|
||||
[MIT-Missing-Semester](https://missing.csail.mit.edu/2020/)
|
||||
|
||||
[Introductory C Programming](https://www.coursera.org/specializations/c-programming)
|
||||
|
||||
[一生一芯nemu](https://ysyx.oscc.cc/)
|
||||
[一生一芯 nemu](https://ysyx.oscc.cc/)
|
||||
|
||||
[jyy的OS课程](https://jyywiki.cn/)
|
||||
[jyy 的 OS 课程](https://jyywiki.cn/)
|
||||
|
||||
[迷宫game](https://github.com/helderman/htpataic)
|
||||
[迷宫 game](https://github.com/helderman/htpataic)
|
||||
|
||||
[GDB User Manual](https://www.sourceware.org/gdb/)
|
||||
|
||||
[learn vim](https://github.com/wsdjeg/Learn-Vim_zh_cn)
|
||||
|
||||
Book:教材替换用书——《C Primer Plus》
|
||||
|
||||
|
||||
|
||||
BIN
3.编程思维体系构建/code/HW 01.zip
Normal file
BIN
3.编程思维体系构建/static/call_expression.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
3.编程思维体系构建/static/expression_tree.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
3.编程思维体系构建/static/function_abs.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
3.编程思维体系构建/static/function_print.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
3.编程思维体系构建/static/ok01.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
3.编程思维体系构建/static/ok02.png
Normal file
|
After Width: | Height: | Size: 12 KiB |