2022-08-04 06:59:33 +00:00
<!DOCTYPE HTML>
< html lang = "zh" class = "sidebar-visible no-js light" >
< head >
2024-08-13 00:15:53 +00:00
<!-- Book generated using https://github.com/wa - lang/wabook -->
2022-08-04 06:59:33 +00:00
< meta charset = "UTF-8" >
< title > 递归 - Go语言圣经< / title >
<!-- Custom HTML head -->
< meta content = "text/html; charset=utf-8" http-equiv = "Content-Type" >
2024-08-08 07:47:03 +00:00
< meta name = "description" content = "" >
2022-08-04 06:59:33 +00:00
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< meta name = "theme-color" content = "#ffffff" / >
2024-08-08 07:47:03 +00:00
< link rel = "icon" href = "../favicon.svg" >
< link rel = "shortcut icon" href = "../favicon.png" >
2024-08-13 00:15:53 +00:00
< link rel = "stylesheet" href = "../static/wabook/css/variables.css" >
< link rel = "stylesheet" href = "../static/wabook/css/general.css" >
< link rel = "stylesheet" href = "../static/wabook/css/chrome.css" >
< link rel = "stylesheet" href = "../static/wabook/css/print.css" media = "print" >
2022-08-04 06:59:33 +00:00
<!-- Fonts -->
2024-08-13 00:15:53 +00:00
< link rel = "stylesheet" href = "../static/wabook/FontAwesome/css/font-awesome.css" >
< link rel = "stylesheet" href = "../static/wabook/fonts/fonts.css" >
2022-08-04 06:59:33 +00:00
<!-- Highlight.js Stylesheets -->
2024-08-13 00:15:53 +00:00
< link rel = "stylesheet" href = "../static/wabook/highlight.css" >
< link rel = "stylesheet" href = "../static/wabook/tomorrow-night.css" >
< link rel = "stylesheet" href = "../static/wabook/ayu-highlight.css" >
2022-08-04 06:59:33 +00:00
<!-- Custom theme stylesheets -->
< / head >
< body >
<!-- Provide site root to javascript -->
< script type = "text/javascript" >
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
< / script >
<!-- Work around some values being stored in localStorage wrapped in quotes -->
< script type = "text/javascript" >
try {
2024-08-13 00:15:53 +00:00
var theme = localStorage.getItem('wabook-theme');
var sidebar = localStorage.getItem('wabook-sidebar');
2022-08-04 06:59:33 +00:00
if (theme.startsWith('"') & & theme.endsWith('"')) {
2024-08-13 00:15:53 +00:00
localStorage.setItem('wabook-theme', theme.slice(1, theme.length - 1));
2022-08-04 06:59:33 +00:00
}
if (sidebar.startsWith('"') & & sidebar.endsWith('"')) {
2024-08-13 00:15:53 +00:00
localStorage.setItem('wabook-sidebar', sidebar.slice(1, sidebar.length - 1));
2022-08-04 06:59:33 +00:00
}
} catch (e) { }
< / script >
<!-- Set the theme before any content is loaded, prevents flash -->
< script type = "text/javascript" >
var theme;
2024-08-13 00:15:53 +00:00
try { theme = localStorage.getItem('wabook-theme'); } catch(e) { }
2022-08-04 06:59:33 +00:00
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
< / script >
<!-- Hide / unhide sidebar before it is displayed -->
< script type = "text/javascript" >
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
2024-08-13 00:15:53 +00:00
try { sidebar = localStorage.getItem('wabook-sidebar'); } catch(e) { }
2022-08-04 06:59:33 +00:00
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
< / script >
< nav id = "sidebar" class = "sidebar" aria-label = "Table of contents" >
< div class = "sidebar-scrollbox" >
2024-08-08 07:47:03 +00:00
< ol class = "chapter" >
< li class = "chapter-item expanded " >
< a href = "../index.html" > Go语言圣经< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../preface-zh.html" > 译者序< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../preface.html" > 前言< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1.html" > < strong aria-hidden = "true" > 1.< / strong > 入门< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-01.html" > < strong aria-hidden = "true" > 1.1.< / strong > Hello, World< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-02.html" > < strong aria-hidden = "true" > 1.2.< / strong > 命令行参数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-03.html" > < strong aria-hidden = "true" > 1.3.< / strong > 查找重复的行< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-04.html" > < strong aria-hidden = "true" > 1.4.< / strong > GIF动画< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-05.html" > < strong aria-hidden = "true" > 1.5.< / strong > 获取URL< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-06.html" > < strong aria-hidden = "true" > 1.6.< / strong > 并发获取多个URL< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-07.html" > < strong aria-hidden = "true" > 1.7.< / strong > Web服务< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch1/ch1-08.html" > < strong aria-hidden = "true" > 1.8.< / strong > 本章要点< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2.html" > < strong aria-hidden = "true" > 2.< / strong > 程序结构< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-01.html" > < strong aria-hidden = "true" > 2.1.< / strong > 命名< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-02.html" > < strong aria-hidden = "true" > 2.2.< / strong > 声明< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-03.html" > < strong aria-hidden = "true" > 2.3.< / strong > 变量< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-04.html" > < strong aria-hidden = "true" > 2.4.< / strong > 赋值< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-05.html" > < strong aria-hidden = "true" > 2.5.< / strong > 类型< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-06.html" > < strong aria-hidden = "true" > 2.6.< / strong > 包和文件< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch2/ch2-07.html" > < strong aria-hidden = "true" > 2.7.< / strong > 作用域< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3.html" > < strong aria-hidden = "true" > 3.< / strong > 基础数据类型< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3-01.html" > < strong aria-hidden = "true" > 3.1.< / strong > 整型< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3-02.html" > < strong aria-hidden = "true" > 3.2.< / strong > 浮点数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3-03.html" > < strong aria-hidden = "true" > 3.3.< / strong > 复数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3-04.html" > < strong aria-hidden = "true" > 3.4.< / strong > 布尔型< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3-05.html" > < strong aria-hidden = "true" > 3.5.< / strong > 字符串< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch3/ch3-06.html" > < strong aria-hidden = "true" > 3.6.< / strong > 常量< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4.html" > < strong aria-hidden = "true" > 4.< / strong > 复合数据类型< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4-01.html" > < strong aria-hidden = "true" > 4.1.< / strong > 数组< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4-02.html" > < strong aria-hidden = "true" > 4.2.< / strong > Slice< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4-03.html" > < strong aria-hidden = "true" > 4.3.< / strong > Map< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4-04.html" > < strong aria-hidden = "true" > 4.4.< / strong > 结构体< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4-05.html" > < strong aria-hidden = "true" > 4.5.< / strong > JSON< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch4/ch4-06.html" > < strong aria-hidden = "true" > 4.6.< / strong > 文本和HTML模板< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5.html" > < strong aria-hidden = "true" > 5.< / strong > 函数< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-01.html" > < strong aria-hidden = "true" > 5.1.< / strong > 函数声明< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-02.html" class = "active" > < strong aria-hidden = "true" > 5.2.< / strong > 递归< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-03.html" > < strong aria-hidden = "true" > 5.3.< / strong > 多返回值< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-04.html" > < strong aria-hidden = "true" > 5.4.< / strong > 错误< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-05.html" > < strong aria-hidden = "true" > 5.5.< / strong > 函数值< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-06.html" > < strong aria-hidden = "true" > 5.6.< / strong > 匿名函数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-07.html" > < strong aria-hidden = "true" > 5.7.< / strong > 可变参数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-08.html" > < strong aria-hidden = "true" > 5.8.< / strong > Deferred函数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-09.html" > < strong aria-hidden = "true" > 5.9.< / strong > Panic异常< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch5/ch5-10.html" > < strong aria-hidden = "true" > 5.10.< / strong > Recover捕获异常< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6.html" > < strong aria-hidden = "true" > 6.< / strong > 方法< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6-01.html" > < strong aria-hidden = "true" > 6.1.< / strong > 方法声明< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6-02.html" > < strong aria-hidden = "true" > 6.2.< / strong > 基于指针对象的方法< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6-03.html" > < strong aria-hidden = "true" > 6.3.< / strong > 通过嵌入结构体来扩展类型< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6-04.html" > < strong aria-hidden = "true" > 6.4.< / strong > 方法值和方法表达式< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6-05.html" > < strong aria-hidden = "true" > 6.5.< / strong > 示例: Bit数组< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch6/ch6-06.html" > < strong aria-hidden = "true" > 6.6.< / strong > 封装< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7.html" > < strong aria-hidden = "true" > 7.< / strong > 接口< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-01.html" > < strong aria-hidden = "true" > 7.1.< / strong > 接口是合约< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-02.html" > < strong aria-hidden = "true" > 7.2.< / strong > 接口类型< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-03.html" > < strong aria-hidden = "true" > 7.3.< / strong > 实现接口的条件< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-04.html" > < strong aria-hidden = "true" > 7.4.< / strong > flag.Value接口< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-05.html" > < strong aria-hidden = "true" > 7.5.< / strong > 接口值< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-06.html" > < strong aria-hidden = "true" > 7.6.< / strong > sort.Interface接口< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-07.html" > < strong aria-hidden = "true" > 7.7.< / strong > http.Handler接口< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-08.html" > < strong aria-hidden = "true" > 7.8.< / strong > error接口< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-09.html" > < strong aria-hidden = "true" > 7.9.< / strong > 示例: 表达式求值< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-10.html" > < strong aria-hidden = "true" > 7.10.< / strong > 类型断言< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-11.html" > < strong aria-hidden = "true" > 7.11.< / strong > 基于类型断言识别错误类型< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-12.html" > < strong aria-hidden = "true" > 7.12.< / strong > 通过类型断言查询接口< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-13.html" > < strong aria-hidden = "true" > 7.13.< / strong > 类型分支< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-14.html" > < strong aria-hidden = "true" > 7.14.< / strong > 示例: 基于标记的XML解码< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch7/ch7-15.html" > < strong aria-hidden = "true" > 7.15.< / strong > 补充几点< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8.html" > < strong aria-hidden = "true" > 8.< / strong > Goroutines和Channels< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-01.html" > < strong aria-hidden = "true" > 8.1.< / strong > Goroutines< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-02.html" > < strong aria-hidden = "true" > 8.2.< / strong > 示例: 并发的Clock服务< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-03.html" > < strong aria-hidden = "true" > 8.3.< / strong > 示例: 并发的Echo服务< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-04.html" > < strong aria-hidden = "true" > 8.4.< / strong > Channels< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-05.html" > < strong aria-hidden = "true" > 8.5.< / strong > 并发的循环< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-06.html" > < strong aria-hidden = "true" > 8.6.< / strong > 示例: 并发的Web爬虫< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-07.html" > < strong aria-hidden = "true" > 8.7.< / strong > 基于select的多路复用< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-08.html" > < strong aria-hidden = "true" > 8.8.< / strong > 示例: 并发的目录遍历< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-09.html" > < strong aria-hidden = "true" > 8.9.< / strong > 并发的退出< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch8/ch8-10.html" > < strong aria-hidden = "true" > 8.10.< / strong > 示例: 聊天服务< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9.html" > < strong aria-hidden = "true" > 9.< / strong > 基于共享变量的并发< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-01.html" > < strong aria-hidden = "true" > 9.1.< / strong > 竞争条件< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-02.html" > < strong aria-hidden = "true" > 9.2.< / strong > sync.Mutex互斥锁< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-03.html" > < strong aria-hidden = "true" > 9.3.< / strong > sync.RWMutex读写锁< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-04.html" > < strong aria-hidden = "true" > 9.4.< / strong > 内存同步< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-05.html" > < strong aria-hidden = "true" > 9.5.< / strong > sync.Once惰性初始化< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-06.html" > < strong aria-hidden = "true" > 9.6.< / strong > 竞争条件检测< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-07.html" > < strong aria-hidden = "true" > 9.7.< / strong > 示例: 并发的非阻塞缓存< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch9/ch9-08.html" > < strong aria-hidden = "true" > 9.8.< / strong > Goroutines和线程< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10.html" > < strong aria-hidden = "true" > 10.< / strong > 包和工具< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-01.html" > < strong aria-hidden = "true" > 10.1.< / strong > 包简介< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-02.html" > < strong aria-hidden = "true" > 10.2.< / strong > 导入路径< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-03.html" > < strong aria-hidden = "true" > 10.3.< / strong > 包声明< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-04.html" > < strong aria-hidden = "true" > 10.4.< / strong > 导入声明< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-05.html" > < strong aria-hidden = "true" > 10.5.< / strong > 包的匿名导入< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-06.html" > < strong aria-hidden = "true" > 10.6.< / strong > 包和命名< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch10/ch10-07.html" > < strong aria-hidden = "true" > 10.7.< / strong > 工具< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11.html" > < strong aria-hidden = "true" > 11.< / strong > 测试< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11-01.html" > < strong aria-hidden = "true" > 11.1.< / strong > go test< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11-02.html" > < strong aria-hidden = "true" > 11.2.< / strong > 测试函数< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11-03.html" > < strong aria-hidden = "true" > 11.3.< / strong > 测试覆盖率< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11-04.html" > < strong aria-hidden = "true" > 11.4.< / strong > 基准测试< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11-05.html" > < strong aria-hidden = "true" > 11.5.< / strong > 剖析< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch11/ch11-06.html" > < strong aria-hidden = "true" > 11.6.< / strong > 示例函数< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12.html" > < strong aria-hidden = "true" > 12.< / strong > 反射< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-01.html" > < strong aria-hidden = "true" > 12.1.< / strong > 为何需要反射?< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-02.html" > < strong aria-hidden = "true" > 12.2.< / strong > reflect.Type和reflect.Value< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-03.html" > < strong aria-hidden = "true" > 12.3.< / strong > Display递归打印< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-04.html" > < strong aria-hidden = "true" > 12.4.< / strong > 示例: 编码S表达式< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-05.html" > < strong aria-hidden = "true" > 12.5.< / strong > 通过reflect.Value修改值< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-06.html" > < strong aria-hidden = "true" > 12.6.< / strong > 示例: 解码S表达式< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-07.html" > < strong aria-hidden = "true" > 12.7.< / strong > 获取结构体字段标签< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-08.html" > < strong aria-hidden = "true" > 12.8.< / strong > 显示一个类型的方法集< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch12/ch12-09.html" > < strong aria-hidden = "true" > 12.9.< / strong > 几点忠告< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../ch13/ch13.html" > < strong aria-hidden = "true" > 13.< / strong > 底层编程< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../ch13/ch13-01.html" > < strong aria-hidden = "true" > 13.1.< / strong > unsafe.Sizeof, Alignof 和 Offsetof< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch13/ch13-02.html" > < strong aria-hidden = "true" > 13.2.< / strong > unsafe.Pointer< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch13/ch13-03.html" > < strong aria-hidden = "true" > 13.3.< / strong > 示例: 深度相等判断< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch13/ch13-04.html" > < strong aria-hidden = "true" > 13.4.< / strong > 通过cgo调用C代码< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../ch13/ch13-05.html" > < strong aria-hidden = "true" > 13.5.< / strong > 几点忠告< / a >
< / li >
< / ol >
< li class = "chapter-item expanded " >
< a href = "../appendix/appendix.html" > < strong aria-hidden = "true" > 14.< / strong > 附录< / a >
< / li >
< ol class = "section" >
< li class = "chapter-item expanded " >
< a href = "../appendix/appendix-a-errata.html" > < strong aria-hidden = "true" > 14.1.< / strong > 附录A: 原文勘误< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../appendix/appendix-b-author.html" > < strong aria-hidden = "true" > 14.2.< / strong > 附录B: 作者译者< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../appendix/appendix-c-cpoyright.html" > < strong aria-hidden = "true" > 14.3.< / strong > 附录C: 译文授权< / a >
< / li >
< li class = "chapter-item expanded " >
< a href = "../appendix/appendix-d-translations.html" > < strong aria-hidden = "true" > 14.4.< / strong > 附录D: 其它语言< / a >
< / li >
< / ol >
< / ol >
< / div >
2022-08-04 06:59:33 +00:00
< div id = "sidebar-resize-handle" class = "sidebar-resize-handle" > < / div >
< / nav >
< div id = "page-wrapper" class = "page-wrapper" >
< div class = "page" >
< div id = "menu-bar-hover-placeholder" > < / div >
< div id = "menu-bar" class = "menu-bar sticky bordered" >
< div class = "left-buttons" >
< button id = "sidebar-toggle" class = "icon-button" type = "button" title = "Toggle Table of Contents" aria-label = "Toggle Table of Contents" aria-controls = "sidebar" >
< i class = "fa fa-bars" > < / i >
< / button >
< button id = "theme-toggle" class = "icon-button" type = "button" title = "Change theme" aria-label = "Change theme" aria-haspopup = "true" aria-expanded = "false" aria-controls = "theme-list" >
< i class = "fa fa-paint-brush" > < / i >
< / button >
< ul id = "theme-list" class = "theme-popup" aria-label = "Themes" role = "menu" >
< li role = "none" > < button role = "menuitem" class = "theme" id = "light" > Light (default)< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "coal" > Coal< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "navy" > Navy< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "ayu" > Ayu< / button > < / li >
< / ul >
2024-08-08 07:47:03 +00:00
< / div >
2022-08-04 06:59:33 +00:00
2024-08-08 07:47:03 +00:00
< h1 class = "menu-title" > < a href = "../index.html" > Go语言圣经< / a > < / h1 >
2022-08-04 06:59:33 +00:00
< div class = "right-buttons" >
2024-08-08 07:47:03 +00:00
< a href = "https://github.com/gopl-zh/gopl-zh.github.com" title = "Git repository" aria-label = "Git repository" >
2022-08-04 06:59:33 +00:00
< i id = "git-repository-button" class = "fa fa-github" > < / i >
< / a >
2024-08-08 07:47:03 +00:00
< a href = "https://github.com/gopl-zh/gopl-zh.github.com/edit/master/ch5/ch5-02.md" title = "Suggest an edit" aria-label = "Suggest an edit" >
2022-08-04 06:59:33 +00:00
< i id = "git-edit-button" class = "fa fa-edit" > < / i >
< / a >
< / div >
< / div >
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
< script type = "text/javascript" >
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
< / script >
< div id = "content" class = "content" >
<!-- Page table of contents -->
< div class = "sidetoc" > < nav class = "pagetoc" > < / nav > < / div >
2024-08-08 07:47:03 +00:00
2022-08-04 06:59:33 +00:00
< main >
2024-08-13 00:15:53 +00:00
< ul dir = "auto" > < li > < em > 凹语言(Go实现, 面向WASM设计): < a href = "https://github.com/wa-lang/wa" > https://github.com/wa-lang/wa< / a > < / em > < / li > < li > < em > WaBook(Go语言实现的MD电子书构建工具): < a href = "https://github.com/wa-lang/wabook" > https://github.com/wa-lang/wabook< / a > < / em > < / li > < / ul > < hr >
2022-08-04 06:59:33 +00:00
2024-08-08 07:47:03 +00:00
< h2 > 5.2. 递归< / h2 >
2022-08-04 06:59:33 +00:00
< p > 函数可以是递归的, 这意味着函数可以直接或间接的调用自身。对许多问题而言, 递归是一种强有力的技术, 例如处理递归的数据结构。在4.4节, 我们通过遍历二叉树来实现简单的插入排序, 在本章节, 我们再次使用它来处理HTML文件。< / p >
< p > 下文的示例代码使用了非标准包 golang.org/x/net/html , 解析HTML。golang.org/x/... 目录下存储了一些由Go团队设计、维护, 对网络编程、国际化文件处理、移动平台、图像处理、加密解密、开发者工具提供支持的扩展包。未将这些扩展包加入到标准库原因有二, 一是部分包仍在开发中, 二是对大多数Go语言的开发者而言, 扩展包提供的功能很少被使用。< / p >
2023-06-26 17:31:22 +00:00
< p > 例子中调用golang.org/x/net/html的部分api如下所示。html.Parse函数读入一组bytes解析后, 返回html.Node类型的HTML页面树状结构根节点。HTML拥有很多类型的结点如text( 文本) 、comments( 注释) 类型, 在下面的例子中, 我们 只关注< name key='value' > 形式的结点。< / p >
2024-08-08 07:47:03 +00:00
< p > <!-- raw HTML omitted --> <!-- raw HTML omitted --> golang.org/x/net/html<!-- raw HTML omitted --> <!-- raw HTML omitted --> < / p >
< pre > < code class = "language-Go" > package html
type Node struct {
Type NodeType
Data string
Attr []Attribute
FirstChild, NextSibling *Node
}
type NodeType int32
const (
ErrorNode NodeType = iota
TextNode
DocumentNode
ElementNode
CommentNode
DoctypeNode
)
type Attribute struct {
Key, Val string
}
func Parse(r io.Reader) (*Node, error)
2022-08-04 06:59:33 +00:00
< / code > < / pre >
< p > main函数解析HTML标准输入, 通过递归函数visit获得links( 链接) , 并打印出这些links: < / p >
2024-08-08 07:47:03 +00:00
< p > <!-- raw HTML omitted --> <!-- raw HTML omitted --> gopl.io/ch5/findlinks1<!-- raw HTML omitted --> <!-- raw HTML omitted --> < / p >
< pre > < code class = "language-Go" > // Findlinks1 prints the links in an HTML document read from standard input.
package main
import (
" fmt"
" os"
" golang.org/x/net/html"
)
func main() {
doc, err := html.Parse(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, " findlinks1: %v\n" , err)
os.Exit(1)
}
for _, link := range visit(nil, doc) {
fmt.Println(link)
}
}
2022-08-04 06:59:33 +00:00
< / code > < / pre >
< p > visit函数遍历HTML的节点树, 从每一个anchor元素的href属性获得link,将这些links存入字符串数组中, 并返回这个字符串数组。< / p >
2024-08-08 07:47:03 +00:00
< pre > < code class = "language-Go" > // visit appends to links each link found in n and returns the result.
func visit(links []string, n *html.Node) []string {
if n.Type == html.ElementNode & & n.Data == " a" {
for _, a := range n.Attr {
if a.Key == " href" {
links = append(links, a.Val)
}
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
links = visit(links, c)
}
return links
}
2022-08-04 06:59:33 +00:00
< / code > < / pre >
< p > 为了遍历结点n的所有后代结点, 每次遇到n的孩子结点时, visit递归的调用自身。这些孩子结点存放在FirstChild链表中。< / p >
< p > 让我们以Go的主页( golang.org) 作为目标, 运行findlinks。我们以fetch( 1.5章) 的输出作为findlinks的输入。下面的输出做了简化处理。< / p >
2024-08-08 07:47:03 +00:00
< pre > < code > $ go build gopl.io/ch1/fetch
$ go build gopl.io/ch5/findlinks1
$ ./fetch https://golang.org | ./findlinks1
#
/doc/
/pkg/
/help/
/blog/
http://play.golang.org/
//tour.golang.org/
https://golang.org/dl/
//blog.golang.org/
/LICENSE
/doc/tos.html
http://www.google.com/intl/en/policies/privacy/
2022-08-04 06:59:33 +00:00
< / code > < / pre >
2024-08-08 07:47:03 +00:00
< p > 注意在页面中出现的链接格式,在之后我们会介绍如何将这些链接,根据根路径( < a href = "https://golang.org" > https://golang.org< / a > ) 生成可以直接访问的url。< / p >
2022-08-04 06:59:33 +00:00
< p > 在函数outline中, 我们通过递归的方式遍历整个HTML结点树, 并输出树的结构。在outline内部, 每遇到一个HTML元素标签, 就将其入栈, 并输出。< / p >
2024-08-08 07:47:03 +00:00
< p > <!-- raw HTML omitted --> <!-- raw HTML omitted --> gopl.io/ch5/outline<!-- raw HTML omitted --> <!-- raw HTML omitted --> < / p >
< pre > < code class = "language-Go" > func main() {
doc, err := html.Parse(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, " outline: %v\n" , err)
os.Exit(1)
}
outline(nil, doc)
}
func outline(stack []string, n *html.Node) {
if n.Type == html.ElementNode {
stack = append(stack, n.Data) // push tag
fmt.Println(stack)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
outline(stack, c)
}
}
2022-08-04 06:59:33 +00:00
< / code > < / pre >
< p > 有一点值得注意: outline有入栈操作, 但没有相对应的出栈操作。当outline调用自身时, 被调用者接收的是stack的拷贝。被调用者对stack的元素追加操作, 修改的是stack的拷贝, 其可能会修改slice底层的数组甚至是申请一块新的内存空间进行扩容; 但这个过程并不会修改调用方的stack。因此当函数返回时, 调用方的stack与其调用自身之前完全一致。< / p >
2024-08-08 07:47:03 +00:00
< p > 下面是 < a href = "https://golang.org" > https://golang.org< / a > 页面的简要结构:< / p >
< pre > < code > $ go build gopl.io/ch5/outline
$ ./fetch https://golang.org | ./outline
[html]
[html head]
[html head meta]
[html head title]
[html head link]
[html body]
[html body div]
[html body div]
[html body div div]
[html body div div form]
[html body div div form div]
[html body div div form div a]
...
2022-08-04 06:59:33 +00:00
< / code > < / pre >
< p > 正如你在上面实验中所见, 大部分HTML页面只需几层递归就能被处理, 但仍然有些页面需要深层次的递归。< / p >
< p > 大部分编程语言使用固定大小的函数调用栈, 常见的大小从64KB到2MB不等。固定大小栈会限制递归的深度, 当你用递归处理大量数据时, 需要避免栈溢出; 除此之外, 还会导致安全性问题。与此相反, Go语言使用可变栈, 栈的大小按需增加( 初始时很小) 。这使得我们使用递归时不必考虑溢出和安全问题。< / p >
< p > < strong > 练习 5.1: < / strong > 修改findlinks代码中遍历n.FirstChild链表的部分, 将循环调用visit, 改成递归调用。< / p >
< p > < strong > 练习 5.2: < / strong > 编写函数, 记录在HTML树中出现的同名元素的次数。< / p >
< p > < strong > 练习 5.3: < / strong > 编写函数输出所有text结点的内容。注意不要访问< code > < script> < / code > 和< code > < style> < / code > 元素,因为这些元素对浏览者是不可见的。< / p >
< p > < strong > 练习 5.4: < / strong > 扩展visit函数, 使其能够处理其他类型的结点, 如images、scripts和style sheets。< / p >
2024-08-08 07:47:03 +00:00
< hr > < table > < tr > < td > < img width = "222px" src = "https://chai2010.cn/advanced-go-programming-book/css.png" > < / td > < td > < img width = "222px" src = "https://chai2010.cn/advanced-go-programming-book/cch.png" > < / td > < / tr > < / table >
< div id = "giscus-container" > < / div >
2022-08-04 06:59:33 +00:00
2024-08-08 07:47:03 +00:00
< footer class = "page-footer" >
< span > © 2015-2016 | < a href = "https://github.com/gopl-zh" > Go语言圣经中文版< / a > , 仅学习交流使用< / span >
< / footer >
2022-08-04 06:59:33 +00:00
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
2024-08-08 07:47:03 +00:00
< a rel = "prev" href = "../ch5/ch5-01.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2022-08-04 06:59:33 +00:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2024-08-08 07:47:03 +00:00
<!-- ../ch5/ch5 - 03.html -->
< a rel = "next" href = "../ch5/ch5-03.html" class = "mobile-nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
2022-08-04 06:59:33 +00:00
< i class = "fa fa-angle-right" > < / i >
< / a >
< div style = "clear: both" > < / div >
< / nav >
< / div >
< / div >
< nav class = "nav-wide-wrapper" aria-label = "Page navigation" >
2024-08-08 07:47:03 +00:00
< a rel = "prev" href = "../ch5/ch5-01.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2022-08-04 06:59:33 +00:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2024-08-08 07:47:03 +00:00
< a rel = "next" href = "../ch5/ch5-03.html" class = "nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
2022-08-04 06:59:33 +00:00
< i class = "fa fa-angle-right" > < / i >
< / a >
2024-08-08 07:47:03 +00:00
< / nav >
2022-08-04 06:59:33 +00:00
< / div >
2024-08-08 07:47:03 +00:00
< script type = "text/javascript" >
2022-08-04 06:59:33 +00:00
window.playground_copyable = true;
< / script >
2024-08-13 00:15:53 +00:00
< script src = "../static/wabook/mark.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../static/wabook/clipboard.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../static/wabook/highlight.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../static/wabook/book.js" type = "text/javascript" charset = "utf-8" > < / script >
2022-08-04 06:59:33 +00:00
< script type = "text/javascript" charset = "utf-8" >
2024-08-08 07:47:03 +00:00
var pagePath = "ch5/ch5-02.md"
2022-08-04 06:59:33 +00:00
< / script >
<!-- Custom JS scripts -->
2024-08-13 00:15:53 +00:00
< script src = "../static/wabook/giscus.js" type = "text/javascript" charset = "utf-8" > < / script >
2022-08-04 06:59:33 +00:00
2024-08-08 07:47:03 +00:00
2022-08-04 06:59:33 +00:00
< / body >
< / html >