diff options
Diffstat (limited to 'zh-cn')
52 files changed, 6723 insertions, 3000 deletions
diff --git a/zh-cn/asciidoc-cn.html.markdown b/zh-cn/asciidoc-cn.html.markdown new file mode 100644 index 00000000..0709e8c1 --- /dev/null +++ b/zh-cn/asciidoc-cn.html.markdown @@ -0,0 +1,135 @@ +--- +language: asciidoc +filename: asciidoc-cn.adoc +contributors: + - ["Ryan Mavilia", "http://unoriginality.rocks/"] + - ["Abel Salgado Romero", "https://twitter.com/abelsromero"] +translators: + - ["Liu Yihua", "https://github.com/yihuajack"] +lang: zh-cn +--- + +AsciiDoc 是一种类似于 Markdown 的标记语言,它可以用来写从书到博客的任何东西。它由 Stuart Rackham 在 2002 年发明,它的语言很简单,但同时也可以有丰富的定制。 + +文档标题 + +标题是可选的,且不可以包含空行。它必须离正文空出至少一个空行。 + +仅标题 + +``` += 文档标题 + +文档的第一句话。 +``` + +标题和作者 + +``` += 文档标题 +张三 <san.zhang@learnxinyminutes.com> + +文档开始。 +``` + +多名作者的情形 + +``` += 文档标题 +李四 <si@go.com>; 王五 <wu@yo.com>; 赵六 <xzhao@pirate.com> + +有多名作者的文档开始。 +``` + +版本行(需要作者行) + +``` += 文档标题(第一版) +土豆人 <chip@crunchy.com> +v1.0, 2016-01-13 + +这篇关于炸薯条的文章会很有趣。 +``` + +段落 + +``` +段落不需要什么特别操作。 + +在两段之间用一个空行隔开。 + +当你需要换行时,添加一个 + +你就会得到一个换行符! +``` + +文本格式化 + +``` +_用下划线创建斜体_ +*用星号加粗* +*_组合起来用更有趣_* +`用重音符显示等宽字体` +`*加粗等宽字体*` +``` + +节标题 + +``` += 第 0 级 (一般只用于文档标题) + +== 第 1 级 <h2> + +=== 第 2 级 <h3> + +==== 第 3 级 <h4> + +===== 第 4 级 <h5> + +``` + +列表 + +用星号创建无序列表。 + +``` +* 甲 +* 乙 +* 丙 +``` + +用句点创建有序列表。 + +``` +. 项目 1 +. 项目 2 +. 项目 3 +``` + +你可以用额外的星号或句点来嵌套最多五次列表。 + +``` +* 甲 1 +** 甲 2 +*** 甲 3 +**** 甲 4 +***** 甲 5 + +. 甲 1 +.. 甲 2 +... 甲 3 +.... 甲 4 +..... 甲 5 +``` + +## 补充材料 + +处理 AsciiDoc 文档有两种工具: + +1. [AsciiDoc](http://asciidoc.org/): 原版的 Python 实现,在主流 Linux 发行版中已附带,目前处于稳定版本维护模式。 +2. [Asciidoctor](http://asciidoctor.org/): 使用 Ruby 的另一种实现,也可以从 Java 和 JavaScript 中使用。它处于积极的开发中,目标是用新特性和输出格式扩展 AsciiDoc 的语法。 + +以下是 `Asciidoctor` 实现的相关链接: + +* [Markdown - AsciiDoc 语法比较](http://asciidoctor.org/docs/user-manual/#comparison-by-example):并列比较一般 Markdown 和 AsciiDoc 的元素。 +* [入门](http://asciidoctor.org/docs/#get-started-with-asciidoctor):安装和快速启动指南,帮助构建简单的文档。 +* [Asciidoctor 用户手册](http://asciidoctor.org/docs/user-manual/): 完整的单文档指南,包含语法参考、示例、渲染工具等。 diff --git a/zh-cn/awk-cn.html.markdown b/zh-cn/awk-cn.html.markdown index 8ee2db2c..02f1f7d5 100644 --- a/zh-cn/awk-cn.html.markdown +++ b/zh-cn/awk-cn.html.markdown @@ -179,7 +179,7 @@ function string_functions( localvar, arr) { # 都是返回替换的个数 localvar = "fooooobar" sub("fo+", "Meet me at the ", localvar) # localvar => "Meet me at the bar" - gsub("e+", ".", localvar) # localvar => "m..t m. at th. bar" + gsub("e", ".", localvar) # localvar => "M..t m. at th. bar" # 搜索匹配正则的字符串 # index() 也是搜索, 不支持正则 diff --git a/zh-cn/bash-cn.html.markdown b/zh-cn/bash-cn.html.markdown index d85e5b8f..225df06b 100644 --- a/zh-cn/bash-cn.html.markdown +++ b/zh-cn/bash-cn.html.markdown @@ -18,7 +18,7 @@ filename: LearnBash-cn.sh lang: zh-cn --- -Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 Mac OS X 下的默认 shell。 +Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 macOS 下的默认 shell。 以下大多数例子可以作为脚本的一部分运行,也可直接在 shell 下交互执行。 [更多信息](http://www.gnu.org/software/bash/manual/bashref.html) diff --git a/zh-cn/c++-cn.html.markdown b/zh-cn/c++-cn.html.markdown index e0d6b6fe..db36ebc4 100644 --- a/zh-cn/c++-cn.html.markdown +++ b/zh-cn/c++-cn.html.markdown @@ -1,572 +1,572 @@ ----
-language: c++
-filename: learncpp-cn.cpp
-contributors:
- - ["Steven Basart", "http://github.com/xksteven"]
- - ["Matt Kline", "https://github.com/mrkline"]
-translators:
- - ["Arnie97", "https://github.com/Arnie97"]
-lang: zh-cn
----
-
-C++是一种系统编程语言。用它的发明者,
-[Bjarne Stroustrup的话](http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote)来说,C++的设计目标是:
-
-- 成为“更好的C语言”
-- 支持数据的抽象与封装
-- 支持面向对象编程
-- 支持泛型编程
-
-C++提供了对硬件的紧密控制(正如C语言一样),
-能够编译为机器语言,由处理器直接执行。
-与此同时,它也提供了泛型、异常和类等高层功能。
-虽然C++的语法可能比某些出现较晚的语言更复杂,它仍然得到了人们的青睞——
-功能与速度的平衡使C++成为了目前应用最广泛的系统编程语言之一。
-
-```c++
-////////////////
-// 与C语言的比较
-////////////////
-
-// C++_几乎_是C语言的一个超集,它与C语言的基本语法有许多相同之处,
-// 例如变量和函数的声明,原生数据类型等等。
-
-// 和C语言一样,在C++中,你的程序会从main()开始执行,
-// 该函数的返回值应当为int型,这个返回值会作为程序的退出状态值。
-// 不过,大多数的编译器(gcc,clang等)也接受 void main() 的函数原型。
-// (参见 http://en.wikipedia.org/wiki/Exit_status 来获取更多信息)
-int main(int argc, char** argv)
-{
- // 和C语言一样,命令行参数通过argc和argv传递。
- // argc代表命令行参数的数量,
- // 而argv是一个包含“C语言风格字符串”(char *)的数组,
- // 其中每个字符串代表一个命令行参数的内容,
- // 首个命令行参数是调用该程序时所使用的名称。
- // 如果你不关心命令行参数的值,argc和argv可以被忽略。
- // 此时,你可以用int main()作为函数原型。
-
- // 退出状态值为0时,表示程序执行成功
- return 0;
-}
-
-// 然而,C++和C语言也有一些区别:
-
-// 在C++中,字符字面量的大小是一个字节。
-sizeof('c') == 1
-
-// 在C语言中,字符字面量的大小与int相同。
-sizeof('c') == sizeof(10)
-
-
-// C++的函数原型与函数定义是严格匹配的
-void func(); // 这个函数不能接受任何参数
-
-// 而在C语言中
-void func(); // 这个函数能接受任意数量的参数
-
-// 在C++中,用nullptr代替C语言中的NULL
-int* ip = nullptr;
-
-// C++也可以使用C语言的标准头文件,
-// 但是需要加上前缀“c”并去掉末尾的“.h”。
-#include <cstdio>
-
-int main()
-{
- printf("Hello, world!\n");
- return 0;
-}
-
-///////////
-// 函数重载
-///////////
-
-// C++支持函数重载,你可以定义一组名称相同而参数不同的函数。
-
-void print(char const* myString)
-{
- printf("String %s\n", myString);
-}
-
-void print(int myInt)
-{
- printf("My int is %d", myInt);
-}
-
-int main()
-{
- print("Hello"); // 解析为 void print(const char*)
- print(15); // 解析为 void print(int)
-}
-
-///////////////////
-// 函数参数的默认值
-///////////////////
-
-// 你可以为函数的参数指定默认值,
-// 它们将会在调用者没有提供相应参数时被使用。
-
-void doSomethingWithInts(int a = 1, int b = 4)
-{
- // 对两个参数进行一些操作
-}
-
-int main()
-{
- doSomethingWithInts(); // a = 1, b = 4
- doSomethingWithInts(20); // a = 20, b = 4
- doSomethingWithInts(20, 5); // a = 20, b = 5
-}
-
-// 默认参数必须放在所有的常规参数之后。
-
-void invalidDeclaration(int a = 1, int b) // 这是错误的!
-{
-}
-
-
-///////////
-// 命名空间
-///////////
-
-// 命名空间为变量、函数和其他声明提供了分离的的作用域。
-// 命名空间可以嵌套使用。
-
-namespace First {
- namespace Nested {
- void foo()
- {
- printf("This is First::Nested::foo\n");
- }
- } // 结束嵌套的命名空间Nested
-} // 结束命名空间First
-
-namespace Second {
- void foo()
- {
- printf("This is Second::foo\n")
- }
-}
-
-void foo()
-{
- printf("This is global foo\n");
-}
-
-int main()
-{
- // 如果没有特别指定,就从“Second”中取得所需的内容。
- using namespace Second;
-
- foo(); // 显示“This is Second::foo”
- First::Nested::foo(); // 显示“This is First::Nested::foo”
- ::foo(); // 显示“This is global foo”
-}
-
-////////////
-// 输入/输出
-////////////
-
-// C++使用“流”来输入输出。<<是流的插入运算符,>>是流提取运算符。
-// cin、cout、和cerr分别代表
-// stdin(标准输入)、stdout(标准输出)和stderr(标准错误)。
-
-#include <iostream> // 引入包含输入/输出流的头文件
-
-using namespace std; // 输入输出流在std命名空间(也就是标准库)中。
-
-int main()
-{
- int myInt;
-
- // 在标准输出(终端/显示器)中显示
- cout << "Enter your favorite number:\n";
- // 从标准输入(键盘)获得一个值
- cin >> myInt;
-
- // cout也提供了格式化功能
- cout << "Your favorite number is " << myInt << "\n";
- // 显示“Your favorite number is <myInt>”
-
- cerr << "Used for error messages";
-}
-
-/////////
-// 字符串
-/////////
-
-// C++中的字符串是对象,它们有很多成员函数
-#include <string>
-
-using namespace std; // 字符串也在std命名空间(标准库)中。
-
-string myString = "Hello";
-string myOtherString = " World";
-
-// + 可以用于连接字符串。
-cout << myString + myOtherString; // "Hello World"
-
-cout << myString + " You"; // "Hello You"
-
-// C++中的字符串是可变的,具有“值语义”。
-myString.append(" Dog");
-cout << myString; // "Hello Dog"
-
-
-/////////////
-// 引用
-/////////////
-
-// 除了支持C语言中的指针类型以外,C++还提供了_引用_。
-// 引用是一种特殊的指针类型,一旦被定义就不能重新赋值,并且不能被设置为空值。
-// 使用引用时的语法与原变量相同:
-// 也就是说,对引用类型进行解引用时,不需要使用*;
-// 赋值时也不需要用&来取地址。
-
-using namespace std;
-
-string foo = "I am foo";
-string bar = "I am bar";
-
-
-string& fooRef = foo; // 建立了一个对foo的引用。
-fooRef += ". Hi!"; // 通过引用来修改foo的值
-cout << fooRef; // "I am foo. Hi!"
-
-// 这句话的并不会改变fooRef的指向,其效果与“foo = bar”相同。
-// 也就是说,在执行这条语句之后,foo == "I am bar"。
-fooRef = bar;
-
-const string& barRef = bar; // 建立指向bar的常量引用。
-// 和C语言中一样,(指针和引用)声明为常量时,对应的值不能被修改。
-barRef += ". Hi!"; // 这是错误的,不能修改一个常量引用的值。
-
-///////////////////
-// 类与面向对象编程
-///////////////////
-
-// 有关类的第一个示例
-#include <iostream>
-
-// 声明一个类。
-// 类通常在头文件(.h或.hpp)中声明。
-class Dog {
- // 成员变量和成员函数默认情况下是私有(private)的。
- std::string name;
- int weight;
-
-// 在这个标签之后,所有声明都是公有(public)的,
-// 直到重新指定“private:”(私有继承)或“protected:”(保护继承)为止
-public:
-
- // 默认的构造器
- Dog();
-
- // 这里是成员函数声明的一个例子。
- // 可以注意到,我们在此处使用了std::string,而不是using namespace std
- // 语句using namespace绝不应当出现在头文件当中。
- void setName(const std::string& dogsName);
-
- void setWeight(int dogsWeight);
-
- // 如果一个函数不对对象的状态进行修改,
- // 应当在声明中加上const。
- // 这样,你就可以对一个以常量方式引用的对象执行该操作。
- // 同时可以注意到,当父类的成员函数需要被子类重写时,
- // 父类中的函数必须被显式声明为_虚函数(virtual)_。
- // 考虑到性能方面的因素,函数默认情况下不会被声明为虚函数。
- virtual void print() const;
-
- // 函数也可以在class body内部定义。
- // 这样定义的函数会自动成为内联函数。
- void bark() const { std::cout << name << " barks!\n" }
-
- // 除了构造器以外,C++还提供了析构器。
- // 当一个对象被删除或者脱离其定义域时,它的析构函数会被调用。
- // 这使得RAII这样的强大范式(参见下文)成为可能。
- // 为了衍生出子类来,基类的析构函数必须定义为虚函数。
- virtual ~Dog();
-
-}; // 在类的定义之后,要加一个分号
-
-// 类的成员函数通常在.cpp文件中实现。
-void Dog::Dog()
-{
- std::cout << "A dog has been constructed\n";
-}
-
-// 对象(例如字符串)应当以引用的形式传递,
-// 对于不需要修改的对象,最好使用常量引用。
-void Dog::setName(const std::string& dogsName)
-{
- name = dogsName;
-}
-
-void Dog::setWeight(int dogsWeight)
-{
- weight = dogsWeight;
-}
-
-// 虚函数的virtual关键字只需要在声明时使用,不需要在定义时重复
-void Dog::print() const
-{
- std::cout << "Dog is " << name << " and weighs " << weight << "kg\n";
-}
-
-void Dog::~Dog()
-{
- std::cout << "Goodbye " << name << "\n";
-}
-
-int main() {
- Dog myDog; // 此时显示“A dog has been constructed”
- myDog.setName("Barkley");
- myDog.setWeight(10);
- myDog.print(); // 显示“Dog is Barkley and weighs 10 kg”
- return 0;
-} // 显示“Goodbye Barkley”
-
-// 继承:
-
-// 这个类继承了Dog类中的公有(public)和保护(protected)对象
-class OwnedDog : public Dog {
-
- void setOwner(const std::string& dogsOwner)
-
- // 重写OwnedDogs类的print方法。
- // 如果你不熟悉子类多态的话,可以参考这个页面中的概述:
- // http://zh.wikipedia.org/wiki/%E5%AD%90%E7%B1%BB%E5%9E%8B
-
- // override关键字是可选的,它确保你所重写的是基类中的方法。
- void print() const override;
-
-private:
- std::string owner;
-};
-
-// 与此同时,在对应的.cpp文件里:
-
-void OwnedDog::setOwner(const std::string& dogsOwner)
-{
- owner = dogsOwner;
-}
-
-void OwnedDog::print() const
-{
- Dog::print(); // 调用基类Dog中的print方法
- // "Dog is <name> and weights <weight>"
-
- std::cout << "Dog is owned by " << owner << "\n";
- // "Dog is owned by <owner>"
-}
-
-/////////////////////
-// 初始化与运算符重载
-/////////////////////
-
-// 在C++中,通过定义一些特殊名称的函数,
-// 你可以重载+、-、*、/等运算符的行为。
-// 当运算符被使用时,这些特殊函数会被调用,从而实现运算符重载。
-
-#include <iostream>
-using namespace std;
-
-class Point {
-public:
- // 可以以这样的方式为成员变量设置默认值。
- double x = 0;
- double y = 0;
-
- // 定义一个默认的构造器。
- // 除了将Point初始化为(0, 0)以外,这个函数什么都不做。
- Point() { };
-
- // 下面使用的语法称为初始化列表,
- // 这是初始化类中成员变量的正确方式。
- Point (double a, double b) :
- x(a),
- y(b)
- { /* 除了初始化成员变量外,什么都不做 */ }
-
- // 重载 + 运算符
- Point operator+(const Point& rhs) const;
-
- // 重载 += 运算符
- Point& operator+=(const Point& rhs);
-
- // 增加 - 和 -= 运算符也是有意义的,但这里不再赘述。
-};
-
-Point Point::operator+(const Point& rhs) const
-{
- // 创建一个新的点,
- // 其横纵坐标分别为这个点与另一点在对应方向上的坐标之和。
- return Point(x + rhs.x, y + rhs.y);
-}
-
-Point& Point::operator+=(const Point& rhs)
-{
- x += rhs.x;
- y += rhs.y;
- return *this;
-}
-
-int main () {
- Point up (0,1);
- Point right (1,0);
- // 这里使用了Point类型的运算符“+”
- // 调用up(Point类型)的“+”方法,并以right作为函数的参数
- Point result = up + right;
- // 显示“Result is upright (1,1)”
- cout << "Result is upright (" << result.x << ',' << result.y << ")\n";
- return 0;
-}
-
-///////////
-// 异常处理
-///////////
-
-// 标准库中提供了一些基本的异常类型
-// (参见http://en.cppreference.com/w/cpp/error/exception)
-// 但是,其他任何类型也可以作为一个异常被拋出
-#include <exception>
-
-// 在_try_代码块中拋出的异常可以被随后的_catch_捕获。
-try {
- // 不要用 _new_关键字在堆上为异常分配空间。
- throw std::exception("A problem occurred");
-}
-// 如果拋出的异常是一个对象,可以用常量引用来捕获它
-catch (const std::exception& ex)
-{
- std::cout << ex.what();
-// 捕获尚未被_catch_处理的所有错误
-} catch (...)
-{
- std::cout << "Unknown exception caught";
- throw; // 重新拋出异常
-}
-
-///////
-// RAII
-///////
-
-// RAII指的是“资源获取就是初始化”(Resource Allocation Is Initialization),
-// 它被视作C++中最强大的编程范式之一。
-// 简单说来,它指的是,用构造函数来获取一个对象的资源,
-// 相应的,借助析构函数来释放对象的资源。
-
-// 为了理解这一范式的用处,让我们考虑某个函数使用文件句柄时的情况:
-void doSomethingWithAFile(const char* filename)
-{
- // 首先,让我们假设一切都会顺利进行。
-
- FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
-
- doSomethingWithTheFile(fh);
- doSomethingElseWithIt(fh);
-
- fclose(fh); // 关闭文件句柄
-}
-
-// 不幸的是,随着错误处理机制的引入,事情会变得复杂。
-// 假设fopen函数有可能执行失败,
-// 而doSomethingWithTheFile和doSomethingElseWithIt会在失败时返回错误代码。
-// (虽然异常是C++中处理错误的推荐方式,
-// 但是某些程序员,尤其是有C语言背景的,并不认可异常捕获机制的作用)。
-// 现在,我们必须检查每个函数调用是否成功执行,并在问题发生的时候关闭文件句柄。
-bool doSomethingWithAFile(const char* filename)
-{
- FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
- if (fh == nullptr) // 当执行失败是,返回的指针是nullptr
- return false; // 向调用者汇报错误
-
- // 假设每个函数会在执行失败时返回false
- if (!doSomethingWithTheFile(fh)) {
- fclose(fh); // 关闭文件句柄,避免造成内存泄漏。
- return false; // 反馈错误
- }
- if (!doSomethingElseWithIt(fh)) {
- fclose(fh); // 关闭文件句柄
- return false; // 反馈错误
- }
-
- fclose(fh); // 关闭文件句柄
- return true; // 指示函数已成功执行
-}
-
-// C语言的程序员通常会借助goto语句简化上面的代码:
-bool doSomethingWithAFile(const char* filename)
-{
- FILE* fh = fopen(filename, "r");
- if (fh == nullptr)
- return false;
-
- if (!doSomethingWithTheFile(fh))
- goto failure;
-
- if (!doSomethingElseWithIt(fh))
- goto failure;
-
- fclose(fh); // 关闭文件
- return true; // 执行成功
-
-failure:
- fclose(fh);
- return false; // 反馈错误
-}
-
-// 如果用异常捕获机制来指示错误的话,
-// 代码会变得清晰一些,但是仍然有优化的余地。
-void doSomethingWithAFile(const char* filename)
-{
- FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
- if (fh == nullptr)
- throw std::exception("Could not open the file.");
-
- try {
- doSomethingWithTheFile(fh);
- doSomethingElseWithIt(fh);
- }
- catch (...) {
- fclose(fh); // 保证出错的时候文件被正确关闭
- throw; // 之后,重新抛出这个异常
- }
-
- fclose(fh); // 关闭文件
- // 所有工作顺利完成
-}
-
-// 相比之下,使用C++中的文件流类(fstream)时,
-// fstream会利用自己的析构器来关闭文件句柄。
-// 只要离开了某一对象的定义域,它的析构函数就会被自动调用。
-void doSomethingWithAFile(const std::string& filename)
-{
- // ifstream是输入文件流(input file stream)的简称
- std::ifstream fh(filename); // 打开一个文件
-
- // 对文件进行一些操作
- doSomethingWithTheFile(fh);
- doSomethingElseWithIt(fh);
-
-} // 文件已经被析构器自动关闭
-
-// 与上面几种方式相比,这种方式有着_明显_的优势:
-// 1. 无论发生了什么情况,资源(此例当中是文件句柄)都会被正确关闭。
-// 只要你正确使用了析构器,就_不会_因为忘记关闭句柄,造成资源的泄漏。
-// 2. 可以注意到,通过这种方式写出来的代码十分简洁。
-// 析构器会在后台关闭文件句柄,不再需要你来操心这些琐事。
-// 3. 这种方式的代码具有异常安全性。
-// 无论在函数中的何处拋出异常,都不会阻碍对文件资源的释放。
-
-// 地道的C++代码应当把RAII的使用扩展到各种类型的资源上,包括:
-// - 用unique_ptr和shared_ptr管理的内存
-// - 各种数据容器,例如标准库中的链表、向量(容量自动扩展的数组)、散列表等;
-// 当它们脱离作用域时,析构器会自动释放其中储存的内容。
-// - 用lock_guard和unique_lock实现的互斥
-```
-扩展阅读:
-
-* [CPP Reference](http://cppreference.com/w/cpp) 提供了最新的语法参考。
-* 可以在 [CPlusPlus](http://cplusplus.com) 找到一些补充资料。
-* 可以在 [TheChernoProject - C ++](https://www.youtube.com/playlist?list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb)上找到涵盖语言基础和设置编码环境的教程。
+--- +language: C++ +filename: learncpp-cn.cpp +contributors: + - ["Steven Basart", "http://github.com/xksteven"] + - ["Matt Kline", "https://github.com/mrkline"] +translators: + - ["Arnie97", "https://github.com/Arnie97"] +lang: zh-cn +--- + +C++是一种系统编程语言。用它的发明者, +[Bjarne Stroustrup的话](http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote)来说,C++的设计目标是: + +- 成为“更好的C语言” +- 支持数据的抽象与封装 +- 支持面向对象编程 +- 支持泛型编程 + +C++提供了对硬件的紧密控制(正如C语言一样), +能够编译为机器语言,由处理器直接执行。 +与此同时,它也提供了泛型、异常和类等高层功能。 +虽然C++的语法可能比某些出现较晚的语言更复杂,它仍然得到了人们的青睞—— +功能与速度的平衡使C++成为了目前应用最广泛的系统编程语言之一。 + +```c++ +//////////////// +// 与C语言的比较 +//////////////// + +// C++_几乎_是C语言的一个超集,它与C语言的基本语法有许多相同之处, +// 例如变量和函数的声明,原生数据类型等等。 + +// 和C语言一样,在C++中,你的程序会从main()开始执行, +// 该函数的返回值应当为int型,这个返回值会作为程序的退出状态值。 +// 不过,大多数的编译器(gcc,clang等)也接受 void main() 的函数原型。 +// (参见 http://en.wikipedia.org/wiki/Exit_status 来获取更多信息) +int main(int argc, char** argv) +{ + // 和C语言一样,命令行参数通过argc和argv传递。 + // argc代表命令行参数的数量, + // 而argv是一个包含“C语言风格字符串”(char *)的数组, + // 其中每个字符串代表一个命令行参数的内容, + // 首个命令行参数是调用该程序时所使用的名称。 + // 如果你不关心命令行参数的值,argc和argv可以被忽略。 + // 此时,你可以用int main()作为函数原型。 + + // 退出状态值为0时,表示程序执行成功 + return 0; +} + +// 然而,C++和C语言也有一些区别: + +// 在C++中,字符字面量的大小是一个字节。 +sizeof('c') == 1 + +// 在C语言中,字符字面量的大小与int相同。 +sizeof('c') == sizeof(10) + + +// C++的函数原型与函数定义是严格匹配的 +void func(); // 这个函数不能接受任何参数 + +// 而在C语言中 +void func(); // 这个函数能接受任意数量的参数 + +// 在C++中,用nullptr代替C语言中的NULL +int* ip = nullptr; + +// C++也可以使用C语言的标准头文件, +// 但是需要加上前缀“c”并去掉末尾的“.h”。 +#include <cstdio> + +int main() +{ + printf("Hello, world!\n"); + return 0; +} + +/////////// +// 函数重载 +/////////// + +// C++支持函数重载,你可以定义一组名称相同而参数不同的函数。 + +void print(char const* myString) +{ + printf("String %s\n", myString); +} + +void print(int myInt) +{ + printf("My int is %d", myInt); +} + +int main() +{ + print("Hello"); // 解析为 void print(const char*) + print(15); // 解析为 void print(int) +} + +/////////////////// +// 函数参数的默认值 +/////////////////// + +// 你可以为函数的参数指定默认值, +// 它们将会在调用者没有提供相应参数时被使用。 + +void doSomethingWithInts(int a = 1, int b = 4) +{ + // 对两个参数进行一些操作 +} + +int main() +{ + doSomethingWithInts(); // a = 1, b = 4 + doSomethingWithInts(20); // a = 20, b = 4 + doSomethingWithInts(20, 5); // a = 20, b = 5 +} + +// 默认参数必须放在所有的常规参数之后。 + +void invalidDeclaration(int a = 1, int b) // 这是错误的! +{ +} + + +/////////// +// 命名空间 +/////////// + +// 命名空间为变量、函数和其他声明提供了分离的的作用域。 +// 命名空间可以嵌套使用。 + +namespace First { + namespace Nested { + void foo() + { + printf("This is First::Nested::foo\n"); + } + } // 结束嵌套的命名空间Nested +} // 结束命名空间First + +namespace Second { + void foo() + { + printf("This is Second::foo\n") + } +} + +void foo() +{ + printf("This is global foo\n"); +} + +int main() +{ + // 如果没有特别指定,就从“Second”中取得所需的内容。 + using namespace Second; + + foo(); // 显示“This is Second::foo” + First::Nested::foo(); // 显示“This is First::Nested::foo” + ::foo(); // 显示“This is global foo” +} + +//////////// +// 输入/输出 +//////////// + +// C++使用“流”来输入输出。<<是流的插入运算符,>>是流提取运算符。 +// cin、cout、和cerr分别代表 +// stdin(标准输入)、stdout(标准输出)和stderr(标准错误)。 + +#include <iostream> // 引入包含输入/输出流的头文件 + +using namespace std; // 输入输出流在std命名空间(也就是标准库)中。 + +int main() +{ + int myInt; + + // 在标准输出(终端/显示器)中显示 + cout << "Enter your favorite number:\n"; + // 从标准输入(键盘)获得一个值 + cin >> myInt; + + // cout也提供了格式化功能 + cout << "Your favorite number is " << myInt << "\n"; + // 显示“Your favorite number is <myInt>” + + cerr << "Used for error messages"; +} + +///////// +// 字符串 +///////// + +// C++中的字符串是对象,它们有很多成员函数 +#include <string> + +using namespace std; // 字符串也在std命名空间(标准库)中。 + +string myString = "Hello"; +string myOtherString = " World"; + +// + 可以用于连接字符串。 +cout << myString + myOtherString; // "Hello World" + +cout << myString + " You"; // "Hello You" + +// C++中的字符串是可变的,具有“值语义”。 +myString.append(" Dog"); +cout << myString; // "Hello Dog" + + +///////////// +// 引用 +///////////// + +// 除了支持C语言中的指针类型以外,C++还提供了_引用_。 +// 引用是一种特殊的指针类型,一旦被定义就不能重新赋值,并且不能被设置为空值。 +// 使用引用时的语法与原变量相同: +// 也就是说,对引用类型进行解引用时,不需要使用*; +// 赋值时也不需要用&来取地址。 + +using namespace std; + +string foo = "I am foo"; +string bar = "I am bar"; + + +string& fooRef = foo; // 建立了一个对foo的引用。 +fooRef += ". Hi!"; // 通过引用来修改foo的值 +cout << fooRef; // "I am foo. Hi!" + +// 这句话的并不会改变fooRef的指向,其效果与“foo = bar”相同。 +// 也就是说,在执行这条语句之后,foo == "I am bar"。 +fooRef = bar; + +const string& barRef = bar; // 建立指向bar的常量引用。 +// 和C语言中一样,(指针和引用)声明为常量时,对应的值不能被修改。 +barRef += ". Hi!"; // 这是错误的,不能修改一个常量引用的值。 + +/////////////////// +// 类与面向对象编程 +/////////////////// + +// 有关类的第一个示例 +#include <iostream> + +// 声明一个类。 +// 类通常在头文件(.h或.hpp)中声明。 +class Dog { + // 成员变量和成员函数默认情况下是私有(private)的。 + std::string name; + int weight; + +// 在这个标签之后,所有声明都是公有(public)的, +// 直到重新指定“private:”(私有继承)或“protected:”(保护继承)为止 +public: + + // 默认的构造器 + Dog(); + + // 这里是成员函数声明的一个例子。 + // 可以注意到,我们在此处使用了std::string,而不是using namespace std + // 语句using namespace绝不应当出现在头文件当中。 + void setName(const std::string& dogsName); + + void setWeight(int dogsWeight); + + // 如果一个函数不对对象的状态进行修改, + // 应当在声明中加上const。 + // 这样,你就可以对一个以常量方式引用的对象执行该操作。 + // 同时可以注意到,当父类的成员函数需要被子类重写时, + // 父类中的函数必须被显式声明为_虚函数(virtual)_。 + // 考虑到性能方面的因素,函数默认情况下不会被声明为虚函数。 + virtual void print() const; + + // 函数也可以在class body内部定义。 + // 这样定义的函数会自动成为内联函数。 + void bark() const { std::cout << name << " barks!\n" } + + // 除了构造器以外,C++还提供了析构器。 + // 当一个对象被删除或者脱离其定义域时,它的析构函数会被调用。 + // 这使得RAII这样的强大范式(参见下文)成为可能。 + // 为了衍生出子类来,基类的析构函数必须定义为虚函数。 + virtual ~Dog(); + +}; // 在类的定义之后,要加一个分号 + +// 类的成员函数通常在.cpp文件中实现。 +void Dog::Dog() +{ + std::cout << "A dog has been constructed\n"; +} + +// 对象(例如字符串)应当以引用的形式传递, +// 对于不需要修改的对象,最好使用常量引用。 +void Dog::setName(const std::string& dogsName) +{ + name = dogsName; +} + +void Dog::setWeight(int dogsWeight) +{ + weight = dogsWeight; +} + +// 虚函数的virtual关键字只需要在声明时使用,不需要在定义时重复 +void Dog::print() const +{ + std::cout << "Dog is " << name << " and weighs " << weight << "kg\n"; +} + +void Dog::~Dog() +{ + std::cout << "Goodbye " << name << "\n"; +} + +int main() { + Dog myDog; // 此时显示“A dog has been constructed” + myDog.setName("Barkley"); + myDog.setWeight(10); + myDog.print(); // 显示“Dog is Barkley and weighs 10 kg” + return 0; +} // 显示“Goodbye Barkley” + +// 继承: + +// 这个类继承了Dog类中的公有(public)和保护(protected)对象 +class OwnedDog : public Dog { + + void setOwner(const std::string& dogsOwner) + + // 重写OwnedDogs类的print方法。 + // 如果你不熟悉子类多态的话,可以参考这个页面中的概述: + // http://zh.wikipedia.org/wiki/%E5%AD%90%E7%B1%BB%E5%9E%8B + + // override关键字是可选的,它确保你所重写的是基类中的方法。 + void print() const override; + +private: + std::string owner; +}; + +// 与此同时,在对应的.cpp文件里: + +void OwnedDog::setOwner(const std::string& dogsOwner) +{ + owner = dogsOwner; +} + +void OwnedDog::print() const +{ + Dog::print(); // 调用基类Dog中的print方法 + // "Dog is <name> and weights <weight>" + + std::cout << "Dog is owned by " << owner << "\n"; + // "Dog is owned by <owner>" +} + +///////////////////// +// 初始化与运算符重载 +///////////////////// + +// 在C++中,通过定义一些特殊名称的函数, +// 你可以重载+、-、*、/等运算符的行为。 +// 当运算符被使用时,这些特殊函数会被调用,从而实现运算符重载。 + +#include <iostream> +using namespace std; + +class Point { +public: + // 可以以这样的方式为成员变量设置默认值。 + double x = 0; + double y = 0; + + // 定义一个默认的构造器。 + // 除了将Point初始化为(0, 0)以外,这个函数什么都不做。 + Point() { }; + + // 下面使用的语法称为初始化列表, + // 这是初始化类中成员变量的正确方式。 + Point (double a, double b) : + x(a), + y(b) + { /* 除了初始化成员变量外,什么都不做 */ } + + // 重载 + 运算符 + Point operator+(const Point& rhs) const; + + // 重载 += 运算符 + Point& operator+=(const Point& rhs); + + // 增加 - 和 -= 运算符也是有意义的,但这里不再赘述。 +}; + +Point Point::operator+(const Point& rhs) const +{ + // 创建一个新的点, + // 其横纵坐标分别为这个点与另一点在对应方向上的坐标之和。 + return Point(x + rhs.x, y + rhs.y); +} + +Point& Point::operator+=(const Point& rhs) +{ + x += rhs.x; + y += rhs.y; + return *this; +} + +int main () { + Point up (0,1); + Point right (1,0); + // 这里使用了Point类型的运算符“+” + // 调用up(Point类型)的“+”方法,并以right作为函数的参数 + Point result = up + right; + // 显示“Result is upright (1,1)” + cout << "Result is upright (" << result.x << ',' << result.y << ")\n"; + return 0; +} + +/////////// +// 异常处理 +/////////// + +// 标准库中提供了一些基本的异常类型 +// (参见http://en.cppreference.com/w/cpp/error/exception) +// 但是,其他任何类型也可以作为一个异常被拋出 +#include <exception> + +// 在_try_代码块中拋出的异常可以被随后的_catch_捕获。 +try { + // 不要用 _new_关键字在堆上为异常分配空间。 + throw std::exception("A problem occurred"); +} +// 如果拋出的异常是一个对象,可以用常量引用来捕获它 +catch (const std::exception& ex) +{ + std::cout << ex.what(); +// 捕获尚未被_catch_处理的所有错误 +} catch (...) +{ + std::cout << "Unknown exception caught"; + throw; // 重新拋出异常 +} + +/////// +// RAII +/////// + +// RAII指的是“资源获取就是初始化”(Resource Allocation Is Initialization), +// 它被视作C++中最强大的编程范式之一。 +// 简单说来,它指的是,用构造函数来获取一个对象的资源, +// 相应的,借助析构函数来释放对象的资源。 + +// 为了理解这一范式的用处,让我们考虑某个函数使用文件句柄时的情况: +void doSomethingWithAFile(const char* filename) +{ + // 首先,让我们假设一切都会顺利进行。 + + FILE* fh = fopen(filename, "r"); // 以只读模式打开文件 + + doSomethingWithTheFile(fh); + doSomethingElseWithIt(fh); + + fclose(fh); // 关闭文件句柄 +} + +// 不幸的是,随着错误处理机制的引入,事情会变得复杂。 +// 假设fopen函数有可能执行失败, +// 而doSomethingWithTheFile和doSomethingElseWithIt会在失败时返回错误代码。 +// (虽然异常是C++中处理错误的推荐方式, +// 但是某些程序员,尤其是有C语言背景的,并不认可异常捕获机制的作用)。 +// 现在,我们必须检查每个函数调用是否成功执行,并在问题发生的时候关闭文件句柄。 +bool doSomethingWithAFile(const char* filename) +{ + FILE* fh = fopen(filename, "r"); // 以只读模式打开文件 + if (fh == nullptr) // 当执行失败是,返回的指针是nullptr + return false; // 向调用者汇报错误 + + // 假设每个函数会在执行失败时返回false + if (!doSomethingWithTheFile(fh)) { + fclose(fh); // 关闭文件句柄,避免造成内存泄漏。 + return false; // 反馈错误 + } + if (!doSomethingElseWithIt(fh)) { + fclose(fh); // 关闭文件句柄 + return false; // 反馈错误 + } + + fclose(fh); // 关闭文件句柄 + return true; // 指示函数已成功执行 +} + +// C语言的程序员通常会借助goto语句简化上面的代码: +bool doSomethingWithAFile(const char* filename) +{ + FILE* fh = fopen(filename, "r"); + if (fh == nullptr) + return false; + + if (!doSomethingWithTheFile(fh)) + goto failure; + + if (!doSomethingElseWithIt(fh)) + goto failure; + + fclose(fh); // 关闭文件 + return true; // 执行成功 + +failure: + fclose(fh); + return false; // 反馈错误 +} + +// 如果用异常捕获机制来指示错误的话, +// 代码会变得清晰一些,但是仍然有优化的余地。 +void doSomethingWithAFile(const char* filename) +{ + FILE* fh = fopen(filename, "r"); // 以只读模式打开文件 + if (fh == nullptr) + throw std::exception("Could not open the file."); + + try { + doSomethingWithTheFile(fh); + doSomethingElseWithIt(fh); + } + catch (...) { + fclose(fh); // 保证出错的时候文件被正确关闭 + throw; // 之后,重新抛出这个异常 + } + + fclose(fh); // 关闭文件 + // 所有工作顺利完成 +} + +// 相比之下,使用C++中的文件流类(fstream)时, +// fstream会利用自己的析构器来关闭文件句柄。 +// 只要离开了某一对象的定义域,它的析构函数就会被自动调用。 +void doSomethingWithAFile(const std::string& filename) +{ + // ifstream是输入文件流(input file stream)的简称 + std::ifstream fh(filename); // 打开一个文件 + + // 对文件进行一些操作 + doSomethingWithTheFile(fh); + doSomethingElseWithIt(fh); + +} // 文件已经被析构器自动关闭 + +// 与上面几种方式相比,这种方式有着_明显_的优势: +// 1. 无论发生了什么情况,资源(此例当中是文件句柄)都会被正确关闭。 +// 只要你正确使用了析构器,就_不会_因为忘记关闭句柄,造成资源的泄漏。 +// 2. 可以注意到,通过这种方式写出来的代码十分简洁。 +// 析构器会在后台关闭文件句柄,不再需要你来操心这些琐事。 +// 3. 这种方式的代码具有异常安全性。 +// 无论在函数中的何处拋出异常,都不会阻碍对文件资源的释放。 + +// 地道的C++代码应当把RAII的使用扩展到各种类型的资源上,包括: +// - 用unique_ptr和shared_ptr管理的内存 +// - 各种数据容器,例如标准库中的链表、向量(容量自动扩展的数组)、散列表等; +// 当它们脱离作用域时,析构器会自动释放其中储存的内容。 +// - 用lock_guard和unique_lock实现的互斥 +``` +扩展阅读: + +* [CPP Reference](http://cppreference.com/w/cpp) 提供了最新的语法参考。 +* 可以在 [CPlusPlus](http://cplusplus.com) 找到一些补充资料。 +* 可以在 [TheChernoProject - C ++](https://www.youtube.com/playlist?list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb)上找到涵盖语言基础和设置编码环境的教程。 diff --git a/zh-cn/c-cn.html.markdown b/zh-cn/c-cn.html.markdown index 8566e811..851f2981 100644 --- a/zh-cn/c-cn.html.markdown +++ b/zh-cn/c-cn.html.markdown @@ -1,5 +1,5 @@ --- -language: c +language: C filename: learnc-cn.c contributors: - ["Adam Bard", "http://adambard.com/"] @@ -128,7 +128,7 @@ printf("Enter the array size: "); // 询问用户数组长度 char buf[0x100]; fgets(buf, sizeof buf, stdin); -// stroul 将字符串解析为无符号整数 +// strtoul 将字符串解析为无符号整数 size_t size = strtoul(buf, NULL, 10); int var_length_array[size]; // 声明VLA printf("sizeof array = %zu\n", sizeof var_length_array); @@ -474,7 +474,7 @@ void testFunc() { // 用户自定义类型和结构 /////////////////////////////////////// -// Typedefs可以创建类型别名 +// typedef 可以创建类型别名 typedef int my_type; my_type my_type_var = 0; @@ -612,11 +612,11 @@ typedef void (*my_fnp_type)(char *); 最好找一本 [K&R, aka "The C Programming Language", “C程序设计语言”](https://en.wikipedia.org/wiki/The_C_Programming_Language)。它是关于C最重要的一本书,由C的创作者撰写。不过需要留意的是它比较古老了,因此有些不准确的地方。 -另一个比较好的资源是 [Learn C the hard way](http://c.learncodethehardway.org/book/) +另一个比较好的资源是 [Learn C the hard way](http://learncodethehardway.org/c/) 如果你有问题,请阅读[compl.lang.c Frequently Asked Questions](http://c-faq.com/)。 -使用合适的空格、缩进,保持一致的代码风格非常重要。可读性强的代码比聪明的代码、高速的代码更重要。可以参考下[Linux内核编码风格](https://www.kernel.org/doc/Documentation/CodingStyle) +使用合适的空格、缩进,保持一致的代码风格非常重要。可读性强的代码比聪明的代码、高速的代码更重要。可以参考下[Linux内核编码风格](https://www.kernel.org/doc/Documentation/process/coding-style.rst) 。 除了这些,多多Google吧 diff --git a/zh-cn/clojure-cn.html.markdown b/zh-cn/clojure-cn.html.markdown index fa241384..e98eac2a 100644 --- a/zh-cn/clojure-cn.html.markdown +++ b/zh-cn/clojure-cn.html.markdown @@ -362,7 +362,7 @@ Clojuredocs.org有大多数核心函数的文档,还带了示例哦: [http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core) 4Clojure是个很赞的用来练习clojure/FP技能的地方: -[http://www.4clojure.com/](http://www.4clojure.com/) +[https://4clojure.oxal.org/](https://4clojure.oxal.org/) Clojure-doc.org (你没看错)有很多入门级的文章: [http://clojure-doc.org/](http://clojure-doc.org/) diff --git a/zh-cn/clojure-macro-cn.html.markdown b/zh-cn/clojure-macro-cn.html.markdown index 23b2f203..85b966e9 100644 --- a/zh-cn/clojure-macro-cn.html.markdown +++ b/zh-cn/clojure-macro-cn.html.markdown @@ -131,7 +131,7 @@ lang: zh-cn ; 然而,如果我们希望它在编译期执行,就需要创建宏 (defmacro inline-2 [form] - (inline-2-helper form))) + (inline-2-helper form)) (macroexpand '(inline-2 (1 + (3 / 2) - (1 / 2) + 1))) ; -> (+ (- (+ 1 (/ 3 2)) (/ 1 2)) 1) diff --git a/zh-cn/cmake-cn.html.markdown b/zh-cn/cmake-cn.html.markdown new file mode 100644 index 00000000..dd9961ca --- /dev/null +++ b/zh-cn/cmake-cn.html.markdown @@ -0,0 +1,168 @@ +--- +category: tool +tool: cmake +contributors: + - ["Bruno Alano", "https://github.com/brunoalano"] +translators: + - ["tx23", "https://github.com/tx23"] +filename: CMake-cn +lang: zh-cn +--- + +CMake 是一个跨平台且开源的自动化构建系统工具。通过该工具你可以对你的源代码进行测试、编译或创建安装包。 + +CMake 试图去解决Makefile 跨平台的自动配置问题(不同的make解释器有不同的命令),以及链接第三方库时的易用性问题。 + +CMake是一个可扩展的开源系统,它以操作系统和与编译器无关的方式管理构建过程。与其他许多跨平台系统不同的是, +CMake被设计为与本机的构建环境结合使用。它通过被放置于每个源文件目录下的简单配置文件(名为 CMakeLists.txt 的文件) +来生成常规使用的标准构建文件(比如:Unix 下的 makefiles 文件或 Windows MSVC 中的 projects/workspaces)。 + +```cmake +# 在 CMake 中, 这是一条命令 + +# 要运行我们的代码,请执行以下命令: +# - mkdir build && cd build +# - cmake .. +# - make +# +# 通过上述命令,我们将遵循最佳实践在子目录中进行编译 +# 在第二行命令中我们请求Cmake 生成新的依赖于系统的Makefile文件。 +# 最后,我们运行本地的make 命令。 + +#------------------------------------------------------------------------------ +# 基础部分 +#------------------------------------------------------------------------------ +# +# Cmake文件必须被命令为 “CMakeLists.txt” 。 + +# 设置生成Makefile的CMake所需最低版本要求 +cmake_minimum_required (VERSION 2.8) + +# 当版本小于2.8时,需要加入关键字 FATAL_ERROR。 +cmake_minimum_required (VERSION 2.8 FATAL_ERROR) + +# 在这里定义了项目的名称,同时会影响Cmake 生成的目录命名约定。 +# 我们可以将代码的语言作为第二个参数传入。 +project (learncmake C) + +# 设置项目的源目录(仅仅是由于惯例) +set( LEARN_CMAKE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} ) +set( LEARN_CMAKE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} ) + +# 在构建系统中用“semver”风格为我们代码设置当前版本是很有用的。 +set (LEARN_CMAKE_VERSION_MAJOR 1) +set (LEARN_CMAKE_VERSION_MINOR 0) +set (LEARN_CMAKE_VERSION_PATCH 0) + +# 将变量(版本号)发送到源代码头 +configure_file ( + "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" + "${PROJECT_BINARY_DIR}/TutorialConfig.h" +) + +# 包含目录 +# 在 GCC中, 该语句等同于 "-I" 命令 +include_directories( include ) + +# 在哪里安装其他库?注意:在此处提供includes路径,后续检查将解决所有其他问题 +set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/modules/" ) + +# 条件 +if ( CONDITION ) + # 输出! + + # 附带信息 + message(STATUS "My message") + + # CMake 警告,继续处理 + message(WARNING "My message") + + # CMake 警告 (dev),继续处理 + message(AUTHOR_WARNING "My message") + + # CMake 错误,继续处理但是会跳过生成 + message(SEND_ERROR "My message") + + # CMake 错误,停止处理和生成 + message(FATAL_ERROR "My message") +endif() + +if( CONDITION ) + +elseif( CONDITION ) + +else( CONDITION ) + +endif( CONDITION ) + +# 循环 +foreach(loop_var arg1 arg2 ...) + COMMAND1(ARGS ...) + COMMAND2(ARGS ...) + ... +endforeach(loop_var) + +foreach(loop_var RANGE total) +foreach(loop_var RANGE start stop [step]) + +foreach(loop_var IN [LISTS [list1 [...]]] + [ITEMS [item1 [...]]]) + +while(condition) + COMMAND1(ARGS ...) + COMMAND2(ARGS ...) + ... +endwhile(condition) + + +# 逻辑运算 +if(FALSE AND (FALSE OR TRUE)) + message("Don't display!") +endif() + +# 将常规,缓存或环境变量设置为给定值。 +# 如果指定了PARENT_SCOPE选项,则将在当前作用域上的作用域中设置变量 +# `set(<variable> <value>... [PARENT_SCOPE])` + +# 如何在带引号和不带引号的参数中引用变量?How to reference variables inside quoted and unquoted arguments? +# 如果未设置变量,变量引用由变量值或空字符串替换。 +${variable_name} + +# 清单 +# 设置源文件列表 +set( LEARN_CMAKE_SOURCES + src/main.c + src/imagem.c + src/pather.c +) + +# 调用编译器 +# +# ${PROJECT_NAME} 即 Learn_CMake +add_executable( ${PROJECT_NAME} ${LEARN_CMAKE_SOURCES} ) + +# 链接库 +target_link_libraries( ${PROJECT_NAME} ${LIBS} m ) + +# 在哪里安装其他库?注意:在此处提供includes路径,后续检查将解决所有其他问题 +set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/modules/" ) + +# 编译条件 (gcc ; g++) +if ( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" ) + message( STATUS "Setting the flags for ${CMAKE_C_COMPILER_ID} compiler" ) + add_definitions( --std=c99 ) +endif() + +# 检查 OS +if( UNIX ) + set( LEARN_CMAKE_DEFINITIONS + "${LEARN_CMAKE_DEFINITIONS} -Wall -Wextra -Werror -Wno-deprecated-declarations -Wno-unused-parameter -Wno-comment" ) +endif() +``` + +### 资源 + ++ [CMake tutorial](https://cmake.org/cmake-tutorial/) ++ [CMake documentation](https://cmake.org/documentation/) ++ [Mastering CMake](http://amzn.com/1930934319/) ++ [An Introduction to Modern CMake](https://cliutils.gitlab.io/modern-cmake/) diff --git a/zh-cn/cobol-cn.html.markdown b/zh-cn/cobol-cn.html.markdown new file mode 100644 index 00000000..08eb36fb --- /dev/null +++ b/zh-cn/cobol-cn.html.markdown @@ -0,0 +1,192 @@ +--- +language: COBOL +filename: learn-cn.COB +contributors: + - ["Hyphz", "http://github.com/hyphz/"] +translators: + - ["GOLGO11", "https://github.com/GOLGO11/"] +lang: zh-cn +--- +COBOL是一门面向商业的语言,它从1960年最初设计以来被修订过数次。它被宣称仍然有超过80%的机构在使用它。 + +```cobol + *COBOL. 最好是按照它1985年的标准来编程。 + *用附带GnuCOBOL编译器的OpenCobolIDE 4.7.6来编译。 + + *COBOL在老版(COBOL-85)和新版(COBOL-2002以及COBOL-2014)之间有明显的差别。 + *老版COBOL要求编码的前一到六列是空着的(它们被用来存储穿孔卡片的序列号... + *第七列的一个“*”符号表示注释的开始。 + *在老版COBOL中,一条以*开头的注释最长只能占一行, + *新版COBOL不需要额外的列来补序列号,并且用“*>" 来注释,允许在行中开始注释 + *老版COBOL也强加了对最大行长度的限制 + *关键字在老版COBOL中必须大写, + *但在新版COBOL中不区分大小写 + *虽然新版COBOL允许你编写大小写混合的代码 + *但是在大多数情况下编写COBOL代码时全用大写字符 + *大多数专业的COBOL开发者都是这么做的。 + *COBOL语句以句点结尾。 + + *COBOL代码被拆成了四个部。 + *各部按顺序,它们是: + *IDENTIFICATION DIVSION.(标识部) + *ENVIRONMENT DIVISION.(环境部) + *DATA DIVISION.(数据部) + *PROCEDURE DIVISION.(过程部) + + *第一步,我们必须给我们的程序一个ID。 + *Identification division 也能包含其他的值, + *但它们都只是程序的元数据。Program-id是唯一一个必须给出的值。 + IDENTIFICATION DIVISION. + PROGRAM-ID. LEARN. + AUTHOR. JOHN DOE. + DATE-WRITTEN. 05/02/2020. + + *让我们来声明一些变量。 + *我们要在DATA DIVISION的WORKING-STORAGE节来完成这个事情。 + *每个数据项(又名变量)从一个级别编号开始。 + *然后是数据项的名字,后边再跟着一个picture关键字, + *来描述这个变量将要包含的数据的类型。 + *几乎所有的COBOL开发者都会把PICTURE简写为PIC。 + *A代表字母,X代表字母和数字,9代表数字。 + + *举例: + 01 MYNAME PIC xxxxxxxxxx. *> 十个字符的字符串。 + + *但是逐个数那些x会导致错误, + *所以以上代码可以,并且应该 + *这样重写: + 01 MYNAME PIC X(10). + + *这是几个更多的例子: + 01 AGE PIC 9(3). *> 数字最多三位 + 01 LAST_NAME PIC X(10). *> 字符串最多十个字符 + + *在COBOL里,一行中多个空格和一个空格的效果是一样的, 所以通常 + *情况下都用多个空格排列代码来便于 + *其他的开发者阅读。 + 01 inyear picture s9(7). *> S 使数字为正数. + *> 括号里意思是重复7次9, + *> 即六位数字(不是数组) + + *现在让我们来写一点儿代码。这是一个简单的Hello World程序。 + IDENTIFICATION DIVISION. + PROGRAM-ID. HELLO. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 THE-MESSAGE PIC X(20). + PROCEDURE DIVSION. + DISPLAY "STARTING PROGRAM". + MOVE "HELLO WORLD" TO THE-MESSAGE. + DISPLAY THE-MESSAGE. + STOP RUN. + + *以上的代码会输出: + *STARTING PROGRAM + *HELLO WORLD + + + + ********用COBOL可以做数学运算*************** + ADD 1 TO AGE GIVING NEW-AGE. + SUBTRACT 1 FROM COUNT. + DIVIDE VAR-1 INTO VAR-2 GIVING VAR-3. + COMPUTE TOTAL-COUNT = COUNT1 PLUS COUNT2. + + + *********PERFORM******************** + *PERFORM关键字允许你跳到代码中其他特殊的代码段, + *当这段特殊的代码被执行完后继续回来执行下面的可执行语句。 + *你必须把PERFORM这个词写完整,不可以缩写它。 + + IDENTIFICATION DIVISION. + PROGRAM-ID. HELLOCOBOL. + + PROCEDURE DIVISION. + FIRST-PARA. + DISPLAY 'THIS IS IN FIRST-PARA'. + PERFORM THIRD-PARA THRU FOURTH-PARA. *>跳过second-para,执行3rd&4th + *> 之后当third和fourth执行完, + *> 回到这里继续往下执行直到遇到STOP RUN. + + SECOND-PARA. + DISPLAY 'THIS IS IN SECOND-PARA'. + STOP RUN. + + THIRD-PARA. + DISPLAY 'THIS IS IN THIRD-PARA'. + + FOURTH-PARA. + DISPLAY 'THIS IS IN FOURTH-PARA'. + + + *当你编译执行以上程序时,它会生成以下结果: + THIS IS IN FIRST-PARA + THIS IS IN THIRD-PARA + THIS IS IN FOURTH-PARA + THIS IS IN SECOND-PARA + + + **********用STRING关键字把变量组合到一起************ + + *现在是时候学习两个类似的COBOL动词了:string和unstring。. + + *string动词经常被用来连接两个或多个字符串(把它们拼在一起)。 + *没什么特别的,Unstring被用来把一个字符串拆分成两个或多个更小的字符串。 + *当你在程序中使用string或unstring时不要忘记使用”delimited by“,这个很重要。 + + IDENTIFICATION DIVISION. + PROGRAM-ID. LEARNING. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 FULL-NAME PIC X(20). + 01 FIRST-NAME PIC X(13) VALUE "BOB GIBBERISH". + 01 LAST-NAME PIC X(5) VALUE "COBB". + PROCEDURE DIVISION. + STRING FIRST-NAME DELIMITED BY SPACE + " " + LAST-NAME DELIMITED BY SIZE + INTO FULL-NAME + END-STRING. + DISPLAY "THE FULL NAME IS: "FULL-NAME. + STOP RUN. + + + *以上代码将会输出: + THE FULL NAME IS: BOB COBB + + + *让我们来看看为什么是这样。 + + *首先,我们在DATA DIVISION声明了所有的变量, + *包括我们想存储string命令生成的新字符串用到的的变量。 + + *这个操作过程在PROCEDURE DIVISION完成。 + *我们从STRING 关键字开始,到END-STRING结束。 + *在它们之间我们列出我们想要组合变量形成更大的主变量的过程。 + *这里,我们组合了FIRST-NAME, 一个空格和LAST-NAME。 + + *跟在FIRST-NAME和LAST-NAME后面的DELIMITED BY短语告诉程序我们想要在各自变量上截取字符的规则。 + *DELIMITED BY SPACE告诉程序从最开始截取字符直到遇到一个空格。 + *DELIMITED BY SIZE告诉程序截取字符的完整长度。 + *我们在FIRST-NAME后面使用DELIMITED BY SPACE,字符串中的GIBBERISH部分就被忽略了。 + + *为了更清楚,改变代码中的第10行如下: + + STRING FIRST-NAME DELIMITED BY SIZE + + *然后重新执行程序. 这次的输出变成: + + THE FULL NAME IS: BOB GIBBERISH COBB + + + + + + +``` + +## 想了解更多吗? + +* [GnuCOBOL](https://sourceforge.net/projects/open-cobol/) + diff --git a/zh-cn/crystal-cn.html.markdown b/zh-cn/crystal-cn.html.markdown index 14805114..b69ce688 100644 --- a/zh-cn/crystal-cn.html.markdown +++ b/zh-cn/crystal-cn.html.markdown @@ -564,4 +564,4 @@ ex #=> "ex2" - [官方网站](https://crystal-lang.org/) - [官方文档](https://crystal-lang.org/docs/overview/) - [在线运行代码](https://play.crystal-lang.org/#/cr) -- [Github仓库](https://github.com/crystal-lang/crystal) +- [GitHub仓库](https://github.com/crystal-lang/crystal) diff --git a/zh-cn/csharp-cn.html.markdown b/zh-cn/csharp-cn.html.markdown index 971c1be9..b6cc70c0 100644 --- a/zh-cn/csharp-cn.html.markdown +++ b/zh-cn/csharp-cn.html.markdown @@ -1,49 +1,68 @@ --- -language: c# +language: C# contributors: - ["Irfan Charania", "https://github.com/irfancharania"] - ["Max Yankov", "https://github.com/golergka"] - ["Melvyn Laïly", "http://x2a.yt"] - ["Shaun McCarthy", "http://www.shaunmccarthy.com"] + - ["Wouter Van Schandevijl", "http://github.com/laoujin"] + - ["Jo Pearce", "http://github.com/jdpearce"] + - ["Chris Zimmerman", "https://github.com/chriszimmerman"] + - ["Shawn McGuire", "https://github.com/bigbash"] translators: - ["Jakukyo Friel", "http://weakish.github.io"] + - ["CatfishWen", "http://catfishwen.github.io"] filename: LearnCSharp-cn.cs lang: zh-cn --- +C#是一种优雅且类型安全的面向对象的语言,使开发人员能够构建运行在跨平台的.NET框架上,安全且健壮的应用程序。 -C#是一个优雅的、类型安全的面向对象语言。使用C#,开发者可以在.NET框架下构建安全、健壮的应用程序。 - -[更多关于C#的介绍](http://msdn.microsoft.com/en-us/library/vstudio/z1zx9t92.aspx) +[更多关于C#的介绍](https://learn.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/) ```c# // 单行注释以 // 开始 /* 多行注释是这样的 */ + /// <summary> -/// XML文档注释 +/// 这是 XML文档注释 +/// 可用于生成外部文档或在 IDE 中提供上下文帮助 /// </summary> +/// <param name="firstParam">这是 firstParam 参数的文档</param> +/// <returns>这是函数返回值的信息</returns> +public void MethodOrClassOrOtherWithParsableHelp(string firstParam) { } -// 声明应用用到的命名空间 +// 声明这段源码使用到的命名空间 +// 下面的命名空间都是标准 .NET Framework 类库的一部分 using System; using System.Collections.Generic; -using System.Data.Entity; using System.Dynamic; using System.Linq; -using System.Linq.Expressions; using System.Net; using System.Threading.Tasks; using System.IO; -// 定义作用域,将代码组织成包 +// 但是这个并不是标准类库: +using System.Data.Entity; +// 为了在下方使用它,你需要添加一个 dll 引用 +// 你可以使用 NuGet 包管理器进行安装 +// `Install-Package EntityFramework` + +// 命名空间 Namespaces 可用于将一定的代码组织为 “包” 或者 “模块” +// 你可以在其他文件中这样引用:using Learning.CSharp; + +// 在 C# 10 以后,你也可以这样定义命名空间。这被称为 file-scoped namespaces(文件范围的命名空间). +// namespace Learning.CSharp; + namespace Learning { // 每个 .cs 文件至少需要包含一个和文件名相同的类 - // 你可以不这么干,但是这样不好。 + // 你可以不这么干,但是这样并不推荐。 public class LearnCSharp { - // 基本语法 - 如果你以前用过 Java 或 C++ 的话,可以直接跳到后文「有趣的特性」 + // 基本语法 - 如果你以前用过 Java 或 C++ 的话,可以直接跳到后文「有趣的特性」 public static void Syntax() { // 使用 Console.WriteLine 打印信息 @@ -53,7 +72,7 @@ namespace Learning " Double: " + 3.14 + " Boolean: " + true); - // 使用 Console.Write 打印,不带换行符号 + // 使用 Console.Write 打印将不会换行 Console.Write("Hello "); Console.Write("World"); @@ -113,7 +132,7 @@ namespace Learning char charFromString = fooString[1]; // => 'e' // 字符串不可修改: fooString[1] = 'X' 是行不通的; - // 根据当前的locale设定比较字符串,大小写不敏感 + // 根据当前的区域格式设置比较字符串,大小写不敏感 string.Compare(fooString, "x", StringComparison.CurrentCultureIgnoreCase); // 基于sprintf的字符串格式化 @@ -123,12 +142,13 @@ namespace Learning DateTime fooDate = DateTime.Now; Console.WriteLine(fooDate.ToString("hh:mm, dd MMM yyyy")); - // 使用 @ 符号可以创建跨行的字符串。使用 "" 来表示 " + // 逐字字符串 + // 使用 @ 符号可以创建跨行的字符串。使用 "" 来表示 " string bazString = @"Here's some stuff on a new line! ""Wow!"", the masses cried"; - // 使用const或read-only定义常量 - // 常量在编译期演算 + // 使用 const 或 read-only 定义常量 + // 常量在编译阶段演算 const int HOURS_I_WORK_PER_WEEK = 9001; /////////////////////////////////////////////////// @@ -136,7 +156,7 @@ on a new line! ""Wow!"", the masses cried"; /////////////////////////////////////////////////// // 数组 - 从0开始计数 - // 声明数组时需要确定数组长度 + // 声明数组时需要指定数组长度 // 声明数组的格式如下: // <datatype>[] <var name> = new <datatype>[<array size>]; int[] intArray = new int[10]; @@ -155,8 +175,8 @@ on a new line! ""Wow!"", the masses cried"; // List<datatype> <var name> = new List<datatype>(); List<int> intList = new List<int>(); List<string> stringList = new List<string>(); - List<int> z = new List<int> { 9000, 1000, 1337 }; // i - // <>用于泛型 - 参考下文 + List<int> z = new List<int> { 9000, 1000, 1337 }; // 初始化 + // <> 用于泛型 - 参考下文 // 列表无默认值 // 访问列表元素时必须首先添加元素 @@ -164,18 +184,18 @@ on a new line! ""Wow!"", the masses cried"; Console.WriteLine("intList @ 0: " + intList[0]); // 其他数据结构: - // 堆栈/队列 - // 字典 (哈希表的实现) - // 哈希集合 - // 只读集合 - // 元组 (.Net 4+) + // Stack 堆栈 / Queue 队列 + // Dictionary 字典 (哈希表的实现) + // HashSet 哈希集合 + // Read-only Collections 只读集合 + // Tuple 元组 (.Net 4+) /////////////////////////////////////// // 操作符 /////////////////////////////////////// Console.WriteLine("\n->Operators"); - int i1 = 1, i2 = 2; // 多重声明的简写形式 + int i1 = 1, i2 = 2; // 声明多个变量的简写形式 // 算术直截了当 Console.WriteLine(i1 + i2 - i1 * 3 / 7); // => 3 @@ -214,7 +234,7 @@ on a new line! ""Wow!"", the masses cried"; /////////////////////////////////////// Console.WriteLine("\n->Control Structures"); - // 类似C的if语句 + // 类似 C 的 if 语句 int j = 10; if (j == 10) { @@ -239,7 +259,7 @@ on a new line! ""Wow!"", the masses cried"; int fooWhile = 0; while (fooWhile < 100) { - //迭代 100 次, fooWhile 0->99 + // 迭代 100 次, fooWhile 0->99 fooWhile++; } @@ -247,14 +267,21 @@ on a new line! ""Wow!"", the masses cried"; int fooDoWhile = 0; do { - //迭代 100 次, fooDoWhile 0->99 + // 迭代 100 次, fooDoWhile 0->99 + if (false) + continue; // 跳过本次迭代 + fooDoWhile++; + + if (fooDoWhile == 50) + break; // 结束整个循环 + } while (fooDoWhile < 100); - //for 循环结构 => for(<初始条件>; <条件>; <步>) + // for 循环结构 => for(<初始条件>; <条件>; <步>) for (int fooFor = 0; fooFor < 10; fooFor++) { - //迭代10次, fooFor 0->9 + // 迭代10次, fooFor 0->9 } // foreach循环 @@ -269,7 +296,7 @@ on a new line! ""Wow!"", the masses cried"; } // Switch 语句 - // switch 适用于 byte、short、char和int 数据类型。 + // switch 适用于 byte、short、char 和 int 数据类型。 // 同样适用于可枚举的类型 // 包括字符串类, 以及一些封装了原始值的类: // Character、Byte、Short和Integer。 @@ -307,46 +334,63 @@ on a new line! ""Wow!"", the masses cried"; // 转换字符串为整数 // 转换失败会抛出异常 - int.Parse("123");//返回整数类型的"123" + int.Parse("123"); // 返回整数类型的"123" - // TryParse会尝试转换类型,失败时会返回缺省类型 + // TryParse 会尝试转换类型,失败时会返回缺省类型 // 例如 0 int tryInt; if (int.TryParse("123", out tryInt)) // Funciton is boolean Console.WriteLine(tryInt); // 123 // 转换整数为字符串 - // Convert类提供了一系列便利转换的方法 + // Convert 类提供了一系列方便转换类型的方法 + + // 比如 字符串 与 int 之间 + + // 最佳方法 + bool result = int.TryParse(string, out var integer) + int.Parse(string); + + // 不推荐 Convert.ToString(123); - // or + + // Int 到字符串 tryInt.ToString(); + + // 转换 + // 显式转换 decimal 类型的 15 为 int 类型 + // 然后隐式转换为 long 类型 + long x = (int) 15M; } /////////////////////////////////////// - // 类 + // 类 - 请参阅文件末尾的定义 /////////////////////////////////////// public static void Classes() { // 参看文件尾部的对象声明 - // 使用new初始化对象 + // 使用 new 初始化对象 Bicycle trek = new Bicycle(); // 调用对象的方法 - trek.SpeedUp(3); // 你应该一直使用setter和getter方法 + trek.SpeedUp(3); // 你应该一直使用 setter 和 getter 方法 trek.Cadence = 100; // 查看对象的信息. Console.WriteLine("trek info: " + trek.Info()); - // 实例化一个新的Penny Farthing + // 实例化一个新的 Penny Farthing 对象 PennyFarthing funbike = new PennyFarthing(1, 10); Console.WriteLine("funbike info: " + funbike.Info()); Console.Read(); } // 结束main方法 - // 终端程序 终端程序必须有一个main方法作为入口 + // record 在 C# 9 及以后可用, 这基本上是类的语法糖. record 对象是不可变的 immutable*. + public record ARecord(string Csharp); + + // 终端程序入口 终端程序必须有一个 main 方法作为入口 public static void Main(string[] args) { OtherInterestingFeatures(); @@ -359,11 +403,11 @@ on a new line! ""Wow!"", the masses cried"; // 默认方法签名 public // 可见性 - static // 允许直接调用类,无需先创建实例 - int, //返回值 + static // 允许从类直接调用,无需先创建实例 + int // 返回值类型 MethodSignatures( - int maxCount, // 第一个变量,类型为整型 - int count = 0, // 如果没有传入值,则缺省值为0 + int maxCount, // 第一个参数,类型为整型 + int count = 0, // 如果没有传入值,则缺省值为 0 int another = 3, params string[] otherParams // 捕获其他参数 ) @@ -371,17 +415,22 @@ on a new line! ""Wow!"", the masses cried"; return -1; } - // 方法可以重名,只要签名不一样 - public static void MethodSignature(string maxCount) + // 方法可以重名,只要方法签名不一样 + // 一个只有返回值类型不同的方法 + public static void MethodSignatures( + ref int maxCount, // 通过引用传递 + out int count) { + // 通过 'count' 参数传入的值将在该方法外保留值 15 + count = 15; // 必须在离开方法之前为 out 参数赋值 } - //泛型 - // TKey和TValue类由用用户调用函数时指定。 - // 以下函数模拟了Python的SetDefault + // 泛型 + // TKey 和 TValue 由用户调用方法时指定 + // 以下函数模拟了 Python 的 SetDefault public static TValue SetDefault<TKey, TValue>( - IDictionary<TKey, TValue> dictionary, - TKey key, + IDictionary<TKey, TValue> dictionary, + TKey key, TValue defaultItem) { TValue result; @@ -393,55 +442,125 @@ on a new line! ""Wow!"", the masses cried"; // 你可以限定传入值的范围 public static void IterateAndPrint<T>(T toPrint) where T: IEnumerable<int> { - // 我们可以进行迭代,因为T是可枚举的 + // 我们可以进行迭代,因为 T 是可枚举的 foreach (var item in toPrint) - // ittm为整数 + // item 为整数 Console.WriteLine(item.ToString()); } + // YIELD + // 使用 "yield" 关键字表明它出现的方法是一个迭代器 Iterator + // (这意味着你可以在 foreach 循环中使用它) + public static IEnumerable<int> YieldCounter(int limit = 10) + { + for (var i = 0; i < limit; i++) + yield return i; + } + + // 你可以这样调用它 + public static void PrintYieldCounterToConsole() + { + foreach (var counter in YieldCounter()) + Console.WriteLine(counter); + } + + // 你可以在一个方法中使用多个 "yield return" + public static IEnumerable<int> ManyYieldCounter() + { + yield return 0; + yield return 1; + yield return 2; + yield return 3; + } + + // 你也可以使用 "yield break" 停止该迭代器 + // 此方法只会返回从 0 到 limit 的一半值。 + public static IEnumerable<int> YieldCounterWithBreak(int limit = 10) + { + for (var i = 0; i < limit; i++) + { + if (i > limit / 2) yield break; + yield return i; + } + } + public static void OtherInterestingFeatures() { - // 可选参数 + // 可选参数 MethodSignatures(3, 1, 3, "Some", "Extra", "Strings"); MethodSignatures(3, another: 3); // 显式指定参数,忽略可选参数 + // 使用 ref 和 out 参数 + int maxCount = 0, count; // ref 参数必须有值 + MethodSignatures(ref maxCount, out count); + // 扩展方法 int i = 3; - i.Print(); // 参见下面的定义 + i.Print(); // 参见下面的定义 - // 可为null的类型 对数据库交互、返回值很有用 + // 可为 null 的类型 对数据库交互、返回值很有用 // 任何值类型 (i.e. 不为类) 添加后缀 ? 后会变为可为null的值 // <类型>? <变量名> = <值> int? nullable = null; // Nullable<int> 的简写形式 Console.WriteLine("Nullable variable: " + nullable); bool hasValue = nullable.HasValue; // 不为null时返回真 + // ?? 是用于指定默认值的语法糖 // 以防变量为null的情况 int notNullable = nullable ?? 0; // 0 + // ?. 是另一个可空类型的操作符 - 简写 null 检查 + nullable?.Print(); // 当可空类型值不为 null 的时候调用 Print() 拓展方法 + // 变量类型推断 - 你可以让编译器推断变量类型: var magic = "编译器确定magic是一个字符串,所以仍然是类型安全的"; - // magic = 9; // 不工作,因为magic是字符串,而不是整数。 + // magic = 9; // 不工作,因为magic是字符串,而不是整数。 // 泛型 // - var phonebook = new Dictionary<string, string>() { + var phonebook = new Dictionary<string, string>() { {"Sarah", "212 555 5555"} // 在电话簿中加入新条目 }; - // 调用上面定义为泛型的SETDEFAULT - Console.WriteLine(SetDefault<string,string>(phonebook, "Shaun", "No Phone")); // 没有电话 - // 你不用指定TKey、TValue,因为它们会被隐式地推导出来 + // 调用上面定义为泛型的 SETDEFAULT + Console.WriteLine(SetDefault<string, string>(phonebook, "Shaun", "No Phone")); // No Phone + // 你不用指定 TKey、TValue 的类型 + // 因为它们会被隐式地推导出来 Console.WriteLine(SetDefault(phonebook, "Sarah", "No Phone")); // 212 555 5555 // lambda表达式 - 允许你用一行代码搞定函数 Func<int, int> square = (x) => x * x; // 最后一项为返回值 Console.WriteLine(square(3)); // 9 - // 可抛弃的资源管理 - 让你很容易地处理未管理的资源 - // 大多数访问未管理资源 (文件操作符、设备上下文, etc.)的对象 - // 都实现了IDisposable接口。 - // using语句会为你清理IDisposable对象。 + // 错误处理 - 应对不确定的世界 + try + { + var funBike = PennyFarthing.CreateWithGears(6); + + // 将不再执行,因为 CreateWithGears 抛出异常 + string some = ""; + if (true) some = null; + some.ToLower(); // 抛出 NullReferenceException + } + catch (NotSupportedException) + { + Console.WriteLine("Not so much fun now!"); + } + catch (Exception ex) // 捕获所有其他异常 + { + throw new ApplicationException("It hit the fan", ex); + // throw; // 重新抛出异常并保留调用堆栈 + } + // catch { } // 捕获所有没有捕获的异常 + finally + { + // 在 try 或 catch 之后执行 + } + + // 可抛弃的资源管理 - 让你很容易地处理未托管的资源 + // 大多数访问未托管资源 (文件句柄、设备上下文, etc.) 的对象 + // 都实现了 IDisposable 接口。 + // using语句会为你清理 IDisposable 对象 using (StreamWriter writer = new StreamWriter("log.txt")) { writer.WriteLine("这里没有什么可疑的东西"); @@ -450,29 +569,23 @@ on a new line! ""Wow!"", the masses cried"; } // 并行框架 - // http://blogs.msdn.com/b/csharpfaq/archive/2010/06/01/parallel-programming-in-net-framework-4-getting-started.aspx - var websites = new string[] { - "http://www.google.com", "http://www.reddit.com", - "http://www.shaunmccarthy.com" - }; - var responses = new Dictionary<string, string>(); - - // 为每个请求新开一个线程 - // 在运行下一步前合并结果 - Parallel.ForEach(websites, - new ParallelOptions() {MaxDegreeOfParallelism = 3}, // max of 3 threads - website => - { - // Do something that takes a long time on the file - using (var r = WebRequest.Create(new Uri(website)).GetResponse()) + // https://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/data-parallelism-task-parallel-library + + var words = new List<string> {"dog", "cat", "horse", "pony"}; + + Parallel.ForEach(words, + new ParallelOptions() { MaxDegreeOfParallelism = 4 }, + word => { - responses[website] = r.ContentType; + Console.WriteLine(word); } - }); + ); - // 直到所有的请求完成后才会运行下面的代码 - foreach (var key in responses.Keys) - Console.WriteLine("{0}:{1}", key, responses[key]); + // 运行它会产生不同的输出 + // 因为每个线程在不同的时间完成 + // 一些可能的输出是: + // cat dog horse pony + // dog horse pony cat // 动态对象(配合其他语言使用很方便) dynamic student = new ExpandoObject(); @@ -486,10 +599,10 @@ on a new line! ""Wow!"", the masses cried"; // IQUERYABLE<T> - 几乎所有的集合都实现了它, // 带给你 Map / Filter / Reduce 风格的方法 var bikes = new List<Bicycle>(); - bikes.Sort(); // Sorts the array + bikes.Sort(); // 排序 array bikes.Sort((b1, b2) => b1.Wheels.CompareTo(b2.Wheels)); // 根据车轮数排序 var result = bikes - .Where(b => b.Wheels > 3) // 筛选 - 可以连锁使用 (返回IQueryable) + .Where(b => b.Wheels > 3) // 筛选 - 可以连锁使用 (返回 IQueryable) .Where(b => b.IsBroken && b.HasTassles) .Select(b => b.ToString()); // Map - 这里我们使用了select,所以结果是IQueryable<string> @@ -502,19 +615,19 @@ on a new line! ""Wow!"", the masses cried"; Console.WriteLine(bikeSummary.Name); // ASPARALLEL - // 邪恶的特性 —— 组合了linq和并行操作 + // 这就是事情开始棘手的地方 —— 组合了 linq 和并行操作 var threeWheelers = bikes.AsParallel().Where(b => b.Wheels == 3).Select(b => b.Name); // 以上代码会并发地运行。会自动新开线程,分别计算结果。 // 适用于多核、大数据量的场景。 - // LINQ - 将IQueryable<T>映射到存储,延缓执行 + // LINQ - 映射一组 IQueryable<T> 对象,并延迟执行 // 例如 LinqToSql 映射数据库, LinqToXml 映射XML文档 var db = new BikeRespository(); - // 执行被延迟了,这对于查询数据库来说很好 + // 执行被延迟了,这对于查询数据库来说非常好 var filter = db.Bikes.Where(b => b.HasTassles); // 不运行查询 if (42 > 6) // 你可以不断地增加筛选,包括有条件的筛选,例如用于“高级搜索”功能 - filter = filter.Where(b => b.IsBroken); // 不运行查询 + filter = filter.Where(b => b.IsBroken); // 不运行查询 var query = filter .OrderBy(b => b.Wheels) @@ -541,15 +654,64 @@ on a new line! ""Wow!"", the masses cried"; Console.WriteLine(obj.ToString()); } } + + + // 委托 和 事件 + public class DelegateTest + { + public static int count = 0; + public static int Increment() + { + // 增加 count 然后返回它 + return ++count; + } + + // 委托是一个方法的引用. + // 要引用 Increment 方法, + // 首先声明一个具有相同签名的委托, + // i.e. 不带参数并返回 int + public delegate int IncrementDelegate(); + + // 事件也可用于触发委托 + // 使用委托类型创建事件 + public static event IncrementDelegate MyEvent; + + static void Main(string[] args) + { + // 通过实例化委托来引用 Increment 方法 + // 并将方法本身作为参数传递 + IncrementDelegate inc = new IncrementDelegate(Increment); + Console.WriteLine(inc()); // => 1 + + // 委托可以用 + 运算符组合 + IncrementDelegate composedInc = inc; + composedInc += inc; + composedInc += inc; + + // composedInc 将执行 Increment 3 次 + Console.WriteLine(composedInc()); // => 4 + + + // 为事件订阅与委托 + MyEvent += new IncrementDelegate(Increment); + MyEvent += new IncrementDelegate(Increment); + + // 触发事件 + // ie. 运行这个事件所有的委托订阅 + Console.WriteLine(MyEvent()); // => 6 + } + } + + // 声明类的语法: // <public/private/protected/internal> class <类名>{ - // //数据字段, 构造器, 内部函数. - / // 在Java中函数被称为方法。 + // // 数据字段, 构造器, 内部函数 + // // 在Java中函数被称为方法 // } public class Bicycle { - // 自行车的字段、变量 + // 自行车的字段/变量 public int Cadence // Public: 任何地方都可以访问 { get // get - 定义获取属性的方法 @@ -558,30 +720,35 @@ on a new line! ""Wow!"", the masses cried"; } set // set - 定义设置属性的方法 { - _cadence = value; // value是被传递给setter的值 + _cadence = value; // value 是被传递给 setter 的值 } } private int _cadence; - protected virtual int Gear // 类和子类可以访问 + protected virtual int Gear // Protected: 类和子类可以访问 { get; // 创建一个自动属性,无需成员字段 set; } - internal int Wheels // Internal:在同一程序集内可以访问 + internal int Wheels // Internal: 在同一程序集内可以访问 { get; - private set; // 可以给get/set方法添加修饰符 + private set; // 可以给 get/set 方法添加修饰符 } - int _speed; // 默认为private: 只可以在这个类内访问,你也可以使用`private`关键词 + int _speed; // 类中的任何内容默认为 private: 只可以在这个类内访问 + // 你也可以使用 `private` 关键词显式指定 public string Name { get; set; } - // enum类型包含一组常量 + // 当您想要一个仅返回表达式结果的只读属性时, + // 属性还具有特殊的语法 + public string LongName => Name + " " + _speed + " speed"; + + // enum 枚举是一种值类型,由一组命名常量组成 // 它将名称映射到值(除非特别说明,是一个整型) - // enmu元素的类型可以是byte、sbyte、short、ushort、int、uint、long、ulong。 - // enum不能包含相同的值。 + // enmu 元素的类型可以是 byte、sbyte、short、ushort、int、uint、long、ulong + // enum 不能包含相同的值 public enum BikeBrand { AIST, @@ -589,13 +756,32 @@ on a new line! ""Wow!"", the masses cried"; Electra = 42, //你可以显式地赋值 Gitane // 43 } - // 我们在Bicycle类中定义的这个类型,所以它是一个内嵌类型。 - // 这个类以外的代码应当使用`Bicycle.Brand`来引用。 + // 我们在 Bicycle 类中定义的这个类型,所以它是一个内嵌类型 + // 这个类以外的代码应当使用 `Bicycle.Brand` 来引用 + + public BikeBrand Brand; // 声明一个 enum 类型之后,我们可以声明这个类型的字段 + + // 使用 FlagsAttribute 定义枚举,表示有多个值可以被匹配 + // 任何从 Attribute 派生的类都可以用来修饰类型、方法、参数等 + // 位运算符 & 和 | 可用于 和/或 操作 + + [Flags] + public enum BikeAccessories + { + None = 0, + Bell = 1, + MudGuards = 2, // 需要手动设定值! + Racks = 4, + Lights = 8, + FullPackage = Bell | MudGuards | Racks | Lights + } - public BikeBrand Brand; // 声明一个enum类型之后,我们可以声明这个类型的字段 + // 用法: aBike.Accessories.HasFlag(Bicycle.BikeAccessories.Bell) + // 在 .NET 4 之前: (aBike.Accessories & Bicycle.BikeAccessories.Bell) == Bicycle.BikeAccessories.Bell + public BikeAccessories Accessories { get; set; } - // 静态方法的类型为自身,不属于特定的对象。 - // 你无需引用对象就可以访问他们。 + // 静态方法属于类型自身,不属于特定的对象 + // 你无需通过对象就可以访问他们 // Console.WriteLine("Bicycles created: " + Bicycle.bicyclesCreated); static public int BicyclesCreated = 0; @@ -607,7 +793,7 @@ on a new line! ""Wow!"", the masses cried"; // 下面是一个默认的构造器 public Bicycle() { - this.Gear = 1; // 你可以使用关键词this访问对象的成员 + this.Gear = 1; // 你可以使用关键词 this 访问对象的成员 Cadence = 50; // 不过你并不总是需要它 _speed = 5; Name = "Bontrager"; @@ -618,7 +804,7 @@ on a new line! ""Wow!"", the masses cried"; // 另一个构造器的例子(包含参数) public Bicycle(int startCadence, int startSpeed, int startGear, string name, bool hasCardsInSpokes, BikeBrand brand) - : base() // 首先调用base + : base() // 显式调用基类的无参构造方法 { Gear = startGear; Cadence = startCadence; @@ -637,8 +823,9 @@ on a new line! ""Wow!"", the masses cried"; // 函数语法 // <public/private/protected> <返回值> <函数名称>(<参数>) - // 类可以为字段实现 getters 和 setters 方法 for their fields - // 或者可以实现属性(C#推荐使用这个) + // 类可以为其字段实现 getters 和 setters 方法 + // 或者可以使用属性封装字段(C#推荐使用这个) + // 方法的参数可以有默认值 // 在有默认值的情况下,调用方法的时候可以省略相应的参数 public void SpeedUp(int increment = 1) @@ -652,8 +839,8 @@ on a new line! ""Wow!"", the masses cried"; } // 属性可以访问和设置值 - // 当只需要访问数据的时候,考虑使用属性。 - // 属性可以定义get和set,或者是同时定义两者 + // 当只需要外部访问数据的时候,考虑使用属性。 + // 属性可以定义 get 和 set,或者是同时定义两者 private bool _hasTassles; // private variable public bool HasTassles // public accessor { @@ -663,7 +850,7 @@ on a new line! ""Wow!"", the masses cried"; // 你可以在一行之内定义自动属性 // 这个语法会自动创建后备字段 - // 你可以给getter或setter设置访问修饰符 + // 你可以给 getter 或 setter 设置访问修饰符 // 以便限制它们的访问 public bool IsBroken { get; private set; } @@ -671,12 +858,29 @@ on a new line! ""Wow!"", the masses cried"; public int FrameSize { get; - // 你可以给get或set指定访问修饰符 - // 以下代码意味着只有Bicycle类可以调用Framesize的set + // 你可以给 get 或 set 指定访问修饰符 + // 以下代码意味着只有 Bicycle 类可以调用 Framesize 的 set private set; } - //显示对象属性的方法 + // 还可以在对象上定义自定义索引器 + // 尽管这在本例中并不完全有用, you + // 你可以使用 bicycle[0] 返回 "chris" 来获得第一项 + // 或者 bicycle[1] = "lisa" 来设定值 + private string[] passengers = { "chris", "phil", "darren", "regina" }; + + public string this[int i] + { + get { + return passengers[i]; + } + + set { + passengers[i] = value; + } + } + + // 显示对象属性的方法 public virtual string Info() { return "Gear: " + Gear + @@ -698,7 +902,7 @@ on a new line! ""Wow!"", the masses cried"; } // Bicycle类结束 - // PennyFarthing是Bicycle的一个子类 + // PennyFarthing 是 Bicycle 的一个子类 class PennyFarthing : Bicycle { // (Penny Farthings是一种前轮很大的自行车。没有齿轮。) @@ -717,10 +921,17 @@ on a new line! ""Wow!"", the masses cried"; } set { - throw new ArgumentException("你不可能在PennyFarthing上切换齿轮"); + throw new ArgumentException("你不可能在 PennyFarthing 上切换齿轮"); } } + public static PennyFarthing CreateWithGears(int gears) + { + var penny = new PennyFarthing(1, 1); + penny.Gear = gears; // 你不能这样做! + return penny; + } + public override string Info() { string result = "PennyFarthing bicycle "; @@ -732,7 +943,7 @@ on a new line! ""Wow!"", the masses cried"; // 接口只包含成员的签名,而没有实现。 interface IJumpable { - void Jump(int meters); // 所有接口成员是隐式地公开的 + void Jump(int meters); // 所有接口成员是隐式地公开的 public } interface IBreakable @@ -741,6 +952,8 @@ on a new line! ""Wow!"", the masses cried"; } // 类只能继承一个类,但是可以实现任意数量的接口 + // 但是基类名称必须是列表中的第一个,所有接口都在后面 + class MountainBike : Bicycle, IJumpable, IBreakable { int damage = 0; @@ -759,41 +972,374 @@ on a new line! ""Wow!"", the masses cried"; } /// <summary> - /// 连接数据库,一个 LinqToSql的示例。 + /// 连接数据库,一个 LinqToSql 的示例。 /// EntityFramework Code First 很棒 (类似 Ruby的 ActiveRecord, 不过是双向的) - /// http://msdn.microsoft.com/en-us/data/jj193542.aspx + /// https://learn.microsoft.com/zh-cn/ef/ef6/modeling/code-first/workflows/new-database /// </summary> - public class BikeRespository : DbSet + public class BikeRepository : DbContext { - public BikeRespository() + public BikeRepository() : base() { } public DbSet<Bicycle> Bikes { get; set; } } + + // 一个类可以通过 partial 关键字分别写在多个 .cs 文件中 + // A1.cs + public partial class A + { + public static void A1() + { + Console.WriteLine("Method A1 in class A"); + } + } + + // A2.cs + public partial class A + { + public static void A2() + { + Console.WriteLine("Method A2 in class A"); + } + } + + // 使用 partial 类 "A" + public class Program + { + static void Main() + { + A.A1(); + A.A2(); + } + } + + // 通过在字符串前加上 $ 前缀来进行字符串插值 + // 并用 { 大括号 } 包裹要插值的表达式 + // 您还可以将插值字符串和逐字字符串与 $@ 组合起来 + public class Rectangle + { + public int Length { get; set; } + public int Width { get; set; } + } + + class Program + { + static void Main(string[] args) + { + Rectangle rect = new Rectangle { Length = 5, Width = 3 }; + Console.WriteLine($"The length is {rect.Length} and the width is {rect.Width}"); + + string username = "User"; + Console.WriteLine($@"C:\Users\{username}\Desktop"); + } + } + + // C# 6 新特性 + class GlassBall : IJumpable, IBreakable + { + // 自动属性设置初始值 + public int Damage { get; private set; } = 0; + + // 为仅有 getter 的自动属性设定初始值 + public string Name { get; } = "Glass ball"; + + // 在构造函数中初始化的仅有 getter 的自动属性 + public string GenieName { get; } + + public GlassBall(string genieName = null) + { + GenieName = genieName; + } + + public void Jump(int meters) + { + if (meters < 0) + // 新的 nameof() 表达式; 编译器将检查标识符是否存在 + // nameof(x) == "x" + // 预防 例如 参数名称已更改但错误消息中未更新 + throw new ArgumentException("Cannot jump negative amount!", nameof(meters)); + + Damage += meters; + } + + // 表达式主体 expression-bodied 属性 ... + public bool Broken + => Damage > 100; + + // ... 刚发 + public override string ToString() + // 插值字符串 + => $"{Name}. Damage taken: {Damage}"; + + public string SummonGenie() + // Null 条件运算符 + // x?.y 如果 x 为 null 将立即返回 null; y 将不会求值 + => GenieName?.ToUpper(); + } + + static class MagicService + { + private static bool LogException(Exception ex) + { + // 记录某处的异常 + return false; + } + + public static bool CastSpell(string spell) + { + try + { + // 假设我们在这里调用 API + throw new MagicServiceException("Spell failed", 42); + + // Spell succeeded + return true; + } + // 仅当 Code 为 42(Spell failed)时才捕获 + catch(MagicServiceException ex) when (ex.Code == 42) + { + // Spell failed + return false; + } + // 其他异常,或 MagicServiceException 的 Code 不是 42 + catch(Exception ex) when (LogException(ex)) + { + // 永远不会执行到这块代码 + // 堆栈未展开 + } + return false; + // 请注意,捕获 MagicServiceException + // 并在 Code 不是 42 或 117 时重新抛出是不同的 + // 因为最终的 catch-all 块将不会捕获重新抛出的异常 + } + } + + public class MagicServiceException : Exception + { + public int Code { get; } + + public MagicServiceException(string message, int code) : base(message) + { + Code = code; + } + } + + public static class PragmaWarning { + // 过时的属性 + [Obsolete("Use NewMethod instead", false)] + public static void ObsoleteMethod() + { + // obsolete code + } + + public static void NewMethod() + { + // new code + } + + public static void Main() + { + ObsoleteMethod(); // CS0618: 'ObsoleteMethod 已过时:使用 NewMethod 代替' +#pragma warning disable CS0618 + ObsoleteMethod(); // no warning +#pragma warning restore CS0618 + ObsoleteMethod(); // CS0618: 'ObsoleteMethod 已过时:使用 NewMethod 代替' + } + } } // 结束 Namespace + +using System; +// C# 6, 静态引用 +using static System.Math; + +namespace Learning.More.CSharp +{ + class StaticUsing + { + static void Main() + { + // 不使用静态引用时.. + Console.WriteLine("The square root of 4 is {}.", Math.Sqrt(4)); + // 使用时 + Console.WriteLine("The square root of 4 is {}.", Sqrt(4)); + } + } +} + +// C# 7 新特性 +// 使用 Nuget 安装 Microsoft.Net.Compilers 最新版 +// 使用 Nuget 安装 System.ValueTuple 最新版 +using System; +namespace Csharp7 +{ + // 元组 TUPLES, 析构 DECONSTRUCTION 和 弃元 DISCARDS + class TuplesTest + { + public (string, string) GetName() + { + // 元组中的字段默认命名为 Item1、Item2... + var names1 = ("Peter", "Parker"); + Console.WriteLine(names1.Item2); // => Parker + + // 字段可以显式命名 + // 第 1 种声明 + (string FirstName, string LastName) names2 = ("Peter", "Parker"); + + // 第 2 种声明 + var names3 = (First:"Peter", Last:"Parker"); + + Console.WriteLine(names2.FirstName); // => Peter + Console.WriteLine(names3.Last); // => Parker + + return names3; + } + + public string GetLastName() { + var fullName = GetName(); + + // 元组可以被析构 + (string firstName, string lastName) = fullName; + + // 析构获得的字段可以使用 弃元 _ 丢弃 + var (_, last) = fullName; + return last; + } + + // 通过指定析构方法, + // 可以以相同的方式解构任何类型 + public int randomNumber = 4; + public int anotherRandomNumber = 10; + + public void Deconstruct(out int randomNumber, out int anotherRandomNumber) + { + randomNumber = this.randomNumber; + anotherRandomNumber = this.anotherRandomNumber; + } + + static void Main(string[] args) + { + var tt = new TuplesTest(); + (int num1, int num2) = tt; + Console.WriteLine($"num1: {num1}, num2: {num2}"); // => num1: 4, num2: 10 + + Console.WriteLine(tt.GetLastName()); + } + } + + // 模式匹配 + class PatternMatchingTest + { + public static (string, int)? CreateLogMessage(object data) + { + switch(data) + { + // 使用 when 进行附加过滤 + case System.Net.Http.HttpRequestException h when h.Message.Contains("404"): + return (h.Message, 404); + case System.Net.Http.HttpRequestException h when h.Message.Contains("400"): + return (h.Message, 400); + case Exception e: + return (e.Message, 500); + case string s: + return (s, s.Contains("Error") ? 500 : 200); + case null: + return null; + default: + return (data.ToString(), 500); + } + } + } + + // Reference 变量 / ref 局部变量 + // 允许返回对象的引用而不仅仅是其值 + class RefLocalsTest + { + // 返回值前标明 ref + public static ref string FindItem(string[] arr, string el) + { + for(int i=0; i<arr.Length; i++) + { + if(arr[i] == el) { + // 返回引用 + return ref arr[i]; + } + } + throw new Exception("Item not found"); + } + + public static void SomeMethod() + { + string[] arr = {"this", "is", "an", "array"}; + + // 要在所有地方使用 ref + ref string item = ref FindItem(arr, "array"); + item = "apple"; + Console.WriteLine(arr[3]); // => apple + } + } + + // 本地函数 LOCAL FUNCTIONS + class LocalFunctionTest + { + private static int _id = 0; + public int id; + public LocalFunctionTest() + { + id = generateId(); + + // 这个本地函数只能在此作用域中被访问 + int generateId() + { + return _id++; + } + } + + public static void AnotherMethod() + { + var lf1 = new LocalFunctionTest(); + var lf2 = new LocalFunctionTest(); + Console.WriteLine($"{lf1.id}, {lf2.id}"); // => 0, 1 + + int id = generateId(); + // error CS0103: 当前上下文中不存在名称“generateId” + } + } +} + ``` ## 没有涉及到的主题 +✨ 新的, 👍 旧的, 🎈 长期支持的, 🔥 跨平台的, 🎁 只支持Windows的 + + * 特性 Attributes + + * 异步编程 + + * Web 开发 + * ASP.NET Core ✨ + + * 桌面应用开发 Development + * Windows Presentation Foundation (WPF) 👍 🎈 🎁 + * Universal Windows Platform (UWP) ✨ 🎁 + * Uno Platform 🔥 ✨ + * WinForms 👍 🎈 🎁 + * Avalonia 🔥 ✨ + * WinUI ✨ 🎁 - * Flags - * Attributes - * 静态属性 - * Exceptions, Abstraction - * ASP.NET (Web Forms/MVC/WebMatrix) - * Winforms - * Windows Presentation Foundation (WPF) + * 跨平台开发 + * Xamarin.Forms 👍 + * MAUI ✨ ## 扩展阅读 + * [C# language reference](https://docs.microsoft.com/dotnet/csharp/language-reference/) + * [Learn .NET](https://dotnet.microsoft.com/learn) + * [C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) * [DotNetPerls](http://www.dotnetperls.com) * [C# in Depth](http://manning.com/skeet2) - * [Programming C#](http://shop.oreilly.com/product/0636920024064.do) - * [LINQ](http://shop.oreilly.com/product/9780596519254.do) - * [MSDN Library](http://msdn.microsoft.com/en-us/library/618ayhy6.aspx) - * [ASP.NET MVC Tutorials](http://www.asp.net/mvc/tutorials) - * [ASP.NET Web Matrix Tutorials](http://www.asp.net/web-pages/tutorials) - * [ASP.NET Web Forms Tutorials](http://www.asp.net/web-forms/tutorials) + * [Programming C# 5.0](http://shop.oreilly.com/product/0636920024064.do) + * [LINQ Pocket Reference](http://shop.oreilly.com/product/9780596519254.do) * [Windows Forms Programming in C#](http://www.amazon.com/Windows-Forms-Programming-Chris-Sells/dp/0321116208) - * [C# Coding Conventions](http://msdn.microsoft.com/en-us/library/vstudio/ff926074.aspx) + * [freeCodeCamp - C# Tutorial for Beginners](https://www.youtube.com/watch?v=GhQdlIFylQ8) diff --git a/zh-cn/css-cn.html.markdown b/zh-cn/css-cn.html.markdown index dc6dcc4f..ec937dfb 100644 --- a/zh-cn/css-cn.html.markdown +++ b/zh-cn/css-cn.html.markdown @@ -132,7 +132,7 @@ div.some-parent.class-name {} font-family: Arial; font-family: "Courier New"; /* 使用双引号包裹含空格的字体名称 */ font-family: "Courier New", Trebuchet, Arial; /* 如果第一个 - 字体没找到,浏览器会使用第二个字体,一次类推 */ + 字体没找到,浏览器会使用第二个字体,以此类推 */ } ``` diff --git a/zh-cn/docker-cn.html.markdown b/zh-cn/docker-cn.html.markdown new file mode 100644 index 00000000..ff793ae0 --- /dev/null +++ b/zh-cn/docker-cn.html.markdown @@ -0,0 +1,150 @@ +--- +category: tool +tool: docker +lang: zh-cn +filename: docker-cn.bat +contributors: + - ["Ruslan López", "http://javapro.org/"] +translators: + - ["imba-tjd", "https://github.com/imba-tjd/"] +--- + +```bat +:: 下载、安装、运行 hello-world 镜像(image) +docker run hello-world + +:: :: 如果这是第一次运行,你应该能见到这些信息 +:: Unable to find image 'hello-world:latest' locally # 在本地找不到镜像xxx +:: latest: Pulling from library/hello-world +:: 1b930d010525: Pull complete +:: Digest: sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064 +:: Status: Downloaded newer image for hello-world:latest +:: +:: Hello from Docker! # 来自Docker的欢迎 +:: This message shows that your installation appears to be working correctly. # 此信息表明你的安装似乎成功了 +:: +:: To generate this message, Docker took the following steps: # Docker进行了如下步骤来产生此信息 +:: 1. The Docker client contacted the Docker daemon. # Docker客户端联系Docker守护程序 +:: 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. # Docker守护程序从Docker Hub拉取镜像 +:: (amd64) +:: 3. The Docker daemon created a new container from that image which runs the # Docker守护程序从镜像中创建了一个容器 +:: executable that produces the output you are currently reading. # 运行了产生你正在读的输出的可执行文件 +:: 4. The Docker daemon streamed that output to the Docker client, which sent it # Docker守护程序把输出流式传输给Docker客户端,后者发送到你的终端上 +:: to your terminal. +:: +:: To try something more ambitious, you can run an Ubuntu container with: # 若要尝试更强大的东西,你可以用该命令运行Ubuntu容器 +:: $ docker run -it ubuntu bash +:: +:: Share images, automate workflows, and more with a free Docker ID: # 使用免费的Docker ID来分享镜像,自动化工作流等 +:: https://hub.docker.com/ +:: +:: For more examples and ideas, visit: # 欲获取更多例子和想法,访问 +:: https://docs.docker.com/get-started/ + +:: 现在来看看当前正运行的镜像 +docker ps +:: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +:: NAMES + +:: 看看之前运行过的镜像 +docker ps -a + +:: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +:: NAMES +:: 4a76281f9c53 hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago +:: happy_poincare +:: 名字(name)是自动生成的,因此它会和你的不同 + +:: 移除(remove)我们之前生成的镜像 +docker rm happy_poincare + +:: 测试是否真的删除了 +docker ps -a +:: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +:: NAMES + +:: 为容器(container)指定自定义名字 +docker run --name test_container hello-world +:: Hello from Docker! +:: This message shows that your installation appears to be working correctly. +:: +:: To generate this message, Docker took the following steps: +:: 1. The Docker client contacted the Docker daemon. +:: 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. +:: (amd64) +:: 3. The Docker daemon created a new container from that image which runs the +:: executable that produces the output you are currently reading. +:: 4. The Docker daemon streamed that output to the Docker client, which sent it +:: to your terminal. +:: +:: To try something more ambitious, you can run an Ubuntu container with: +:: $ docker run -it ubuntu bash +:: +:: Share images, automate workflows, and more with a free Docker ID: +:: https://hub.docker.com/ +:: +:: For more examples and ideas, visit: +:: https://docs.docker.com/get-started/ + +docker ps -a +:: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +:: NAMES +:: d345fe1a4f41 hello-world "/hello" About a minute ago Exited (0) About a minute ago +:: test_container +:: 如你所见,名字现在是我们指定的了 + +:: 从命名过的容器中获取日志(logs) +docker logs test_container +:: Hello from Docker! +:: This message shows that your installation appears to be working correctly. +:: +:: To generate this message, Docker took the following steps: +:: 1. The Docker client contacted the Docker daemon. +:: 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. +:: (amd64) +:: 3. The Docker daemon created a new container from that image which runs the +:: executable that produces the output you are currently reading. +:: 4. The Docker daemon streamed that output to the Docker client, which sent it +:: to your terminal. +:: +:: To try something more ambitious, you can run an Ubuntu container with: +:: $ docker run -it ubuntu bash +:: +:: Share images, automate workflows, and more with a free Docker ID: +:: https://hub.docker.com/ +:: +:: For more examples and ideas, visit: +:: https://docs.docker.com/get-started/ + +docker rm test_container + +docker run ubuntu +:: Unable to find image 'ubuntu:latest' locally +:: latest: Pulling from library/ubuntu +:: 2746a4a261c9: Pull complete +:: 4c1d20cdee96: Pull complete 0d3160e1d0de: Pull complete c8e37668deea: Pull complete Digest: sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4 +:: Status: Downloaded newer image for ubuntu:latest + +docker ps -a +:: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +:: NAMES +:: c19e9e5b000a ubuntu "/bin/bash" 5 seconds ago Exited (0) 4 seconds ago +:: relaxed_nobel + +:: 在交互模式(interactive mode)下运行容器 +docker run -it ubuntu +:: root@e2cac48323d2:/# uname +:: Linux +:: root@e2cac48323d2:/# exit +:: exit + +docker rm relaxed_nobel + +docker ps -a +:: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS +:: NAMES +:: e2cac48323d2 ubuntu "/bin/bash" 2 minutes ago Exited (0) About a minute ago +:: nifty_goldwasser + +docker rm nifty_goldwasser +``` diff --git a/zh-cn/dynamic-programming-cn.html.markdown b/zh-cn/dynamic-programming-cn.html.markdown index b75c5404..7864c22a 100644 --- a/zh-cn/dynamic-programming-cn.html.markdown +++ b/zh-cn/dynamic-programming-cn.html.markdown @@ -15,7 +15,7 @@ translators: 动态规划是一种实用的技巧,它可以用来解决一系列特定问题。它的思路很简单,如果你对某个给定的输入解决了一个问题,那么你可以保存已有信息,以避免重复计算,节约计算时间。 -记住,只有那些没有办法记住历史的才被迫做更多的苦力。(Fibonacci就是一个显然的例子) +记住,如果忘记历史,就要被迫做更多的苦力。斐波那契数列就是一个显然的例子。 ## 解决问题的方式 diff --git a/zh-cn/elisp-cn.html.markdown b/zh-cn/elisp-cn.html.markdown index 3f6ccbcf..a429fcbc 100644 --- a/zh-cn/elisp-cn.html.markdown +++ b/zh-cn/elisp-cn.html.markdown @@ -1,345 +1,345 @@ ----
-language: elisp
-contributors:
- - ["Bastien Guerry", "http://bzg.fr"]
-translators:
- - ["Chenbo Li", "http://binarythink.net"]
-filename: learn-emacs-lisp-zh.el
-lang: zh-cn
----
-
-```scheme
-;; 15分钟学会Emacs Lisp (v0.2a)
-;;(作者:bzg,https://github.com/bzg
-;; 译者:lichenbo,http://douban.com/people/lichenbo)
-;;
-;; 请先阅读Peter Norvig的一篇好文:
-;; http://norvig.com/21-days.html
-;; (译者注:中文版请见http://blog.youxu.info/21-days/)
-;;
-;; 之后安装GNU Emacs 24.3:
-;;
-;; Debian: apt-get install emacs (视具体发行版而定)
-;; MacOSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg
-;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip
-;;
-;; 更多信息可以在这里找到:
-;; http://www.gnu.org/software/emacs/#Obtaining
-
-;; 很重要的警告:
-;;
-;; 按照这个教程来学习并不会对你的电脑有任何损坏
-;; 除非你自己在学习的过程中愤怒地把它砸了
-;; 如果出现了这种情况,我不会承担任何责任
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; 打开emacs
-;;
-;; 按'q'消除欢迎界面
-;;
-;; 现在请注意窗口底部的那一个灰色长条
-;;
-;; "*scratch*" 是你现在编辑界面的名字。
-;; 这个编辑界面叫做一个"buffer"。
-;;
-;; 每当你打开Emacs时,都会默认打开这个scratch buffer
-;; 此时你并没有在编辑任何文件,而是在编辑一个buffer
-;; 之后你可以将这个buffer保存到一个文件中。
-;;
-;; 之后的"Lisp interaction" 则是表明我们可以用的某组命令
-;;
-;; Emacs在每个buffer中都有一组内置的命令
-;; 而当你激活某种特定的模式时,就可以使用相应的命令
-;; 这里我们使用`lisp-interaction-mode',
-;; 这样我们就可以使用内置的Emacs Lisp(以下简称Elisp)命令了。
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; 分号是注释开始的标志
-;;
-;; Elisp 是由符号表达式构成的 (即"s-表达式"或"s式"):
-(+ 2 2)
-
-;; 这个s式的意思是 "对2进行加2操作".
-
-;; s式周围有括号,而且也可以嵌套:
-(+ 2 (+ 1 1))
-
-;; 一个s式可以包含原子符号或者其他s式
-;; 在上面的例子中,1和2是原子符号
-;; (+ 2 (+ 1 1)) 和 (+ 1 1) 是s式.
-
-;; 在 `lisp-interaction-mode' 中你可以计算s式.
-;; 把光标移到闭括号后,之后按下ctrl+j(以后简写为'C-j')
-
-(+ 3 (+ 1 2))
-;; ^ 光标放到这里
-;; 按下`C-j' 就会输出 6
-
-;; `C-j' 会在buffer中插入当前运算的结果
-
-;; 而`C-xC-e' 则会在emacs最底部显示结果,也就是被称作"minibuffer"的区域
-;; 为了避免把我们的buffer填满无用的结果,我们以后会一直用`C-xC-e'
-
-;; `setq' 可以将一个值赋给一个变量
-(setq my-name "Bastien")
-;; `C-xC-e' 输出 "Bastien" (在 mini-buffer 中显示)
-
-;; `insert' 会在光标处插入字符串:
-(insert "Hello!")
-;; `C-xC-e' 输出 "Hello!"
-
-;; 在这里我们只传给了insert一个参数"Hello!", 但是
-;; 我们也可以传给它更多的参数,比如2个:
-
-(insert "Hello" " world!")
-;; `C-xC-e' 输出 "Hello world!"
-
-;; 你也可以用变量名来代替字符串
-(insert "Hello, I am " my-name)
-;; `C-xC-e' 输出 "Hello, I am Bastien"
-
-;; 你可以把s式嵌入函数中
-(defun hello () (insert "Hello, I am " my-name))
-;; `C-xC-e' 输出 hello
-
-;; 现在执行这个函数
-(hello)
-;; `C-xC-e' 输出 Hello, I am Bastien
-
-;; 函数中空括号的意思是我们不需要接受任何参数
-;; 但是我们不能一直总是用my-name这个变量
-;; 所以我们现在使我们的函数接受一个叫做"name"的参数
-
-(defun hello (name) (insert "Hello " name))
-;; `C-xC-e' 输出 hello
-
-;; 现在我们调用这个函数,并且将"you"作为参数传递
-
-(hello "you")
-;; `C-xC-e' 输出 "Hello you"
-
-;; 成功!
-
-;; 现在我们可以休息一下
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; 下面我们在新的窗口中新建一个名为 "*test*" 的buffer:
-
-(switch-to-buffer-other-window "*test*")
-;; `C-xC-e' 这时屏幕上会显示两个窗口,而光标此时位于*test* buffer内
-
-;; 用鼠标单击上面的buffer就会使光标移回。
-;; 或者你可以使用 `C-xo' 使得光标跳到另一个窗口中
-
-;; 你可以用 `progn'命令将s式结合起来:
-(progn
- (switch-to-buffer-other-window "*test*")
- (hello "you"))
-;; `C-xC-e' 此时屏幕分为两个窗口,并且在*test* buffer中显示"Hello you"
-
-;; 现在为了简洁,我们需要在每个s式后面都使用`C-xC-e'来执行,后面就不再说明了
-
-;; 记得可以用过鼠标或者`C-xo'回到*scratch*这个buffer。
-
-;; 清除当前buffer也是常用操作之一:
-(progn
- (switch-to-buffer-other-window "*test*")
- (erase-buffer)
- (hello "there"))
-
-;; 也可以回到其他的窗口中
-(progn
- (switch-to-buffer-other-window "*test*")
- (erase-buffer)
- (hello "you")
- (other-window 1))
-
-;; 你可以用 `let' 将一个值和一个局部变量绑定:
-(let ((local-name "you"))
- (switch-to-buffer-other-window "*test*")
- (erase-buffer)
- (hello local-name)
- (other-window 1))
-
-;; 这里我们就不需要使用 `progn' 了, 因为 `let' 也可以将很多s式组合起来。
-
-;; 格式化字符串的方法:
-(format "Hello %s!\n" "visitor")
-
-;; %s 是字符串占位符,这里被"visitor"替代.
-;; \n 是换行符。
-
-;; 现在我们用格式化的方法再重写一下我们的函数:
-(defun hello (name)
- (insert (format "Hello %s!\n" name)))
-
-(hello "you")
-
-;; 我们再用`let'新建另一个函数:
-(defun greeting (name)
- (let ((your-name "Bastien"))
- (insert (format "Hello %s!\n\nI am %s."
- name ; the argument of the function
- your-name ; the let-bound variable "Bastien"
- ))))
-
-;; 之后执行:
-(greeting "you")
-
-;; 有些函数可以和用户交互:
-(read-from-minibuffer "Enter your name: ")
-
-;; 这个函数会返回在执行时用户输入的信息
-
-;; 现在我们让`greeting'函数显示你的名字:
-(defun greeting (from-name)
- (let ((your-name (read-from-minibuffer "Enter your name: ")))
- (insert (format "Hello!\n\nI am %s and you are %s."
- from-name ; the argument of the function
- your-name ; the let-bound var, entered at prompt
- ))))
-
-(greeting "Bastien")
-
-;; 我们让结果在另一个窗口中显示:
-(defun greeting (from-name)
- (let ((your-name (read-from-minibuffer "Enter your name: ")))
- (switch-to-buffer-other-window "*test*")
- (erase-buffer)
- (insert (format "Hello %s!\n\nI am %s." your-name from-name))
- (other-window 1)))
-
-;; 测试一下:
-(greeting "Bastien")
-
-;; 第二节结束,休息一下吧。
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; 我们将一些名字存到列表中:
-(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
-
-;; 用 `car'来取得第一个名字:
-(car list-of-names)
-
-;; 用 `cdr'取得剩下的名字:
-(cdr list-of-names)
-
-;; 用 `push'把名字添加到列表的开头:
-(push "Stephanie" list-of-names)
-
-;; 注意: `car' 和 `cdr' 并不修改列表本身, 但是 `push' 却会对列表本身进行操作.
-;; 这个区别是很重要的: 有些函数没有任何副作用(比如`car')
-;; 但还有一些却是有的 (比如 `push').
-
-;; 我们来对`list-of-names'列表中的每一个元素都使用hello函数:
-(mapcar 'hello list-of-names)
-
-;; 将 `greeting' 改进,使的我们能够对`list-of-names'中的所有名字执行:
-(defun greeting ()
- (switch-to-buffer-other-window "*test*")
- (erase-buffer)
- (mapcar 'hello list-of-names)
- (other-window 1))
-
-(greeting)
-
-;; 记得我们之前定义的 `hello' 函数吗? 这个函数接受一个参数,名字。
-;; `mapcar' 调用 `hello', 并将`list-of-names'作为参数先后传给`hello'
-
-;; 现在我们对显示的buffer中的内容进行一些更改:
-
-(defun replace-hello-by-bonjour ()
- (switch-to-buffer-other-window "*test*")
- (goto-char (point-min))
- (while (search-forward "Hello")
- (replace-match "Bonjour"))
- (other-window 1))
-
-;; (goto-char (point-min)) 将光标移到buffer的开始
-;; (search-forward "Hello") 查找字符串"Hello"
-;; (while x y) 当x返回某个值时执行y这个s式
-;; 当x返回`nil' (空), 退出循环
-
-(replace-hello-by-bonjour)
-
-;; 你会看到所有在*test* buffer中出现的"Hello"字样都被换成了"Bonjour"
-
-;; 你也会得到以下错误提示: "Search failed: Hello".
-;;
-;; 如果要避免这个错误, 你需要告诉 `search-forward' 这个命令是否在
-;; buffer的某个地方停止查找, 并且在什么都没找到时是否应该不给出错误提示
-
-;; (search-forward "Hello" nil t) 可以达到这个要求:
-
-;; `nil' 参数的意思是 : 查找并不限于某个范围内
-;; `t' 参数的意思是: 当什么都没找到时,不给出错误提示
-
-;; 在下面的函数中,我们用到了s式,并且不给出任何错误提示:
-
-(defun hello-to-bonjour ()
- (switch-to-buffer-other-window "*test*")
- (erase-buffer)
- ;; 为`list-of-names'中的每个名字调用hello
- (mapcar 'hello list-of-names)
- (goto-char (point-min))
- ;; 将"Hello" 替换为"Bonjour"
- (while (search-forward "Hello" nil t)
- (replace-match "Bonjour"))
- (other-window 1))
-
-(hello-to-bonjour)
-
-;; 给这些名字加粗:
-
-(defun boldify-names ()
- (switch-to-buffer-other-window "*test*")
- (goto-char (point-min))
- (while (re-search-forward "Bonjour \\(.+\\)!" nil t)
- (add-text-properties (match-beginning 1)
- (match-end 1)
- (list 'face 'bold)))
- (other-window 1))
-
-;; 这个函数使用了 `re-search-forward':
-;; 和查找一个字符串不同,你用这个命令可以查找一个模式,即正则表达式
-
-;; 正则表达式 "Bonjour \\(.+\\)!" 的意思是:
-;; 字符串 "Bonjour ", 之后跟着
-;; 一组 | \\( ... \\) 结构
-;; 任意字符 | . 的含义
-;; 有可能重复的 | + 的含义
-;; 之后跟着 "!" 这个字符串
-
-;; 准备好了?试试看。
-
-(boldify-names)
-
-;; `add-text-properties' 可以添加文字属性, 比如文字样式
-
-;; 好的,我们成功了!
-
-;; 如果你想对一个变量或者函数有更多的了解:
-;;
-;; C-h v 变量 回车
-;; C-h f 函数 回车
-;;
-;; 阅读Emacs Lisp官方文档:
-;;
-;; C-h i m elisp 回车
-;;
-;; 在线阅读Emacs Lisp文档:
-;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html
-
-;; 感谢以下同学的建议和反馈:
-;; - Wes Hardaker
-;; - notbob
-;; - Kevin Montuori
-;; - Arne Babenhauserheide
-;; - Alan Schmitt
-;; - spacegoing
-```
-
+--- +language: elisp +contributors: + - ["Bastien Guerry", "http://bzg.fr"] +translators: + - ["Chenbo Li", "http://binarythink.net"] +filename: learn-emacs-lisp-zh.el +lang: zh-cn +--- + +```scheme +;; 15分钟学会Emacs Lisp (v0.2a) +;;(作者:bzg,https://github.com/bzg +;; 译者:lichenbo,http://douban.com/people/lichenbo) +;; +;; 请先阅读Peter Norvig的一篇好文: +;; http://norvig.com/21-days.html +;; (译者注:中文版请见http://blog.youxu.info/21-days/) +;; +;; 之后安装GNU Emacs 24.3: +;; +;; Debian: apt-get install emacs (视具体发行版而定) +;; MacOSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg +;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip +;; +;; 更多信息可以在这里找到: +;; http://www.gnu.org/software/emacs/#Obtaining + +;; 很重要的警告: +;; +;; 按照这个教程来学习并不会对你的电脑有任何损坏 +;; 除非你自己在学习的过程中愤怒地把它砸了 +;; 如果出现了这种情况,我不会承担任何责任 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 打开emacs +;; +;; 按'q'消除欢迎界面 +;; +;; 现在请注意窗口底部的那一个灰色长条 +;; +;; "*scratch*" 是你现在编辑界面的名字。 +;; 这个编辑界面叫做一个"buffer"。 +;; +;; 每当你打开Emacs时,都会默认打开这个scratch buffer +;; 此时你并没有在编辑任何文件,而是在编辑一个buffer +;; 之后你可以将这个buffer保存到一个文件中。 +;; +;; 之后的"Lisp interaction" 则是表明我们可以用的某组命令 +;; +;; Emacs在每个buffer中都有一组内置的命令 +;; 而当你激活某种特定的模式时,就可以使用相应的命令 +;; 这里我们使用`lisp-interaction-mode', +;; 这样我们就可以使用内置的Emacs Lisp(以下简称Elisp)命令了。 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 分号是注释开始的标志 +;; +;; Elisp 是由符号表达式构成的 (即"s-表达式"或"s式"): +(+ 2 2) + +;; 这个s式的意思是 "对2进行加2操作". + +;; s式周围有括号,而且也可以嵌套: +(+ 2 (+ 1 1)) + +;; 一个s式可以包含原子符号或者其他s式 +;; 在上面的例子中,1和2是原子符号 +;; (+ 2 (+ 1 1)) 和 (+ 1 1) 是s式. + +;; 在 `lisp-interaction-mode' 中你可以计算s式. +;; 把光标移到闭括号后,之后按下ctrl+j(以后简写为'C-j') + +(+ 3 (+ 1 2)) +;; ^ 光标放到这里 +;; 按下`C-j' 就会输出 6 + +;; `C-j' 会在buffer中插入当前运算的结果 + +;; 而`C-xC-e' 则会在emacs最底部显示结果,也就是被称作"minibuffer"的区域 +;; 为了避免把我们的buffer填满无用的结果,我们以后会一直用`C-xC-e' + +;; `setq' 可以将一个值赋给一个变量 +(setq my-name "Bastien") +;; `C-xC-e' 输出 "Bastien" (在 mini-buffer 中显示) + +;; `insert' 会在光标处插入字符串: +(insert "Hello!") +;; `C-xC-e' 输出 "Hello!" + +;; 在这里我们只传给了insert一个参数"Hello!", 但是 +;; 我们也可以传给它更多的参数,比如2个: + +(insert "Hello" " world!") +;; `C-xC-e' 输出 "Hello world!" + +;; 你也可以用变量名来代替字符串 +(insert "Hello, I am " my-name) +;; `C-xC-e' 输出 "Hello, I am Bastien" + +;; 你可以把s式嵌入函数中 +(defun hello () (insert "Hello, I am " my-name)) +;; `C-xC-e' 输出 hello + +;; 现在执行这个函数 +(hello) +;; `C-xC-e' 输出 Hello, I am Bastien + +;; 函数中空括号的意思是我们不需要接受任何参数 +;; 但是我们不能一直总是用my-name这个变量 +;; 所以我们现在使我们的函数接受一个叫做"name"的参数 + +(defun hello (name) (insert "Hello " name)) +;; `C-xC-e' 输出 hello + +;; 现在我们调用这个函数,并且将"you"作为参数传递 + +(hello "you") +;; `C-xC-e' 输出 "Hello you" + +;; 成功! + +;; 现在我们可以休息一下 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 下面我们在新的窗口中新建一个名为 "*test*" 的buffer: + +(switch-to-buffer-other-window "*test*") +;; `C-xC-e' 这时屏幕上会显示两个窗口,而光标此时位于*test* buffer内 + +;; 用鼠标单击上面的buffer就会使光标移回。 +;; 或者你可以使用 `C-xo' 使得光标跳到另一个窗口中 + +;; 你可以用 `progn'命令将s式结合起来: +(progn + (switch-to-buffer-other-window "*test*") + (hello "you")) +;; `C-xC-e' 此时屏幕分为两个窗口,并且在*test* buffer中显示"Hello you" + +;; 现在为了简洁,我们需要在每个s式后面都使用`C-xC-e'来执行,后面就不再说明了 + +;; 记得可以用过鼠标或者`C-xo'回到*scratch*这个buffer。 + +;; 清除当前buffer也是常用操作之一: +(progn + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello "there")) + +;; 也可以回到其他的窗口中 +(progn + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello "you") + (other-window 1)) + +;; 你可以用 `let' 将一个值和一个局部变量绑定: +(let ((local-name "you")) + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello local-name) + (other-window 1)) + +;; 这里我们就不需要使用 `progn' 了, 因为 `let' 也可以将很多s式组合起来。 + +;; 格式化字符串的方法: +(format "Hello %s!\n" "visitor") + +;; %s 是字符串占位符,这里被"visitor"替代. +;; \n 是换行符。 + +;; 现在我们用格式化的方法再重写一下我们的函数: +(defun hello (name) + (insert (format "Hello %s!\n" name))) + +(hello "you") + +;; 我们再用`let'新建另一个函数: +(defun greeting (name) + (let ((your-name "Bastien")) + (insert (format "Hello %s!\n\nI am %s." + name ; the argument of the function + your-name ; the let-bound variable "Bastien" + )))) + +;; 之后执行: +(greeting "you") + +;; 有些函数可以和用户交互: +(read-from-minibuffer "Enter your name: ") + +;; 这个函数会返回在执行时用户输入的信息 + +;; 现在我们让`greeting'函数显示你的名字: +(defun greeting (from-name) + (let ((your-name (read-from-minibuffer "Enter your name: "))) + (insert (format "Hello!\n\nI am %s and you are %s." + from-name ; the argument of the function + your-name ; the let-bound var, entered at prompt + )))) + +(greeting "Bastien") + +;; 我们让结果在另一个窗口中显示: +(defun greeting (from-name) + (let ((your-name (read-from-minibuffer "Enter your name: "))) + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (insert (format "Hello %s!\n\nI am %s." your-name from-name)) + (other-window 1))) + +;; 测试一下: +(greeting "Bastien") + +;; 第二节结束,休息一下吧。 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 我们将一些名字存到列表中: +(setq list-of-names '("Sarah" "Chloe" "Mathilde")) + +;; 用 `car'来取得第一个名字: +(car list-of-names) + +;; 用 `cdr'取得剩下的名字: +(cdr list-of-names) + +;; 用 `push'把名字添加到列表的开头: +(push "Stephanie" list-of-names) + +;; 注意: `car' 和 `cdr' 并不修改列表本身, 但是 `push' 却会对列表本身进行操作. +;; 这个区别是很重要的: 有些函数没有任何副作用(比如`car') +;; 但还有一些却是有的 (比如 `push'). + +;; 我们来对`list-of-names'列表中的每一个元素都使用hello函数: +(mapcar 'hello list-of-names) + +;; 将 `greeting' 改进,使的我们能够对`list-of-names'中的所有名字执行: +(defun greeting () + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (mapcar 'hello list-of-names) + (other-window 1)) + +(greeting) + +;; 记得我们之前定义的 `hello' 函数吗? 这个函数接受一个参数,名字。 +;; `mapcar' 调用 `hello', 并将`list-of-names'作为参数先后传给`hello' + +;; 现在我们对显示的buffer中的内容进行一些更改: + +(defun replace-hello-by-bonjour () + (switch-to-buffer-other-window "*test*") + (goto-char (point-min)) + (while (search-forward "Hello") + (replace-match "Bonjour")) + (other-window 1)) + +;; (goto-char (point-min)) 将光标移到buffer的开始 +;; (search-forward "Hello") 查找字符串"Hello" +;; (while x y) 当x返回某个值时执行y这个s式 +;; 当x返回`nil' (空), 退出循环 + +(replace-hello-by-bonjour) + +;; 你会看到所有在*test* buffer中出现的"Hello"字样都被换成了"Bonjour" + +;; 你也会得到以下错误提示: "Search failed: Hello". +;; +;; 如果要避免这个错误, 你需要告诉 `search-forward' 这个命令是否在 +;; buffer的某个地方停止查找, 并且在什么都没找到时是否应该不给出错误提示 + +;; (search-forward "Hello" nil t) 可以达到这个要求: + +;; `nil' 参数的意思是 : 查找并不限于某个范围内 +;; `t' 参数的意思是: 当什么都没找到时,不给出错误提示 + +;; 在下面的函数中,我们用到了s式,并且不给出任何错误提示: + +(defun hello-to-bonjour () + (switch-to-buffer-other-window "*test*") + (erase-buffer) + ;; 为`list-of-names'中的每个名字调用hello + (mapcar 'hello list-of-names) + (goto-char (point-min)) + ;; 将"Hello" 替换为"Bonjour" + (while (search-forward "Hello" nil t) + (replace-match "Bonjour")) + (other-window 1)) + +(hello-to-bonjour) + +;; 给这些名字加粗: + +(defun boldify-names () + (switch-to-buffer-other-window "*test*") + (goto-char (point-min)) + (while (re-search-forward "Bonjour \\(.+\\)!" nil t) + (add-text-properties (match-beginning 1) + (match-end 1) + (list 'face 'bold))) + (other-window 1)) + +;; 这个函数使用了 `re-search-forward': +;; 和查找一个字符串不同,你用这个命令可以查找一个模式,即正则表达式 + +;; 正则表达式 "Bonjour \\(.+\\)!" 的意思是: +;; 字符串 "Bonjour ", 之后跟着 +;; 一组 | \\( ... \\) 结构 +;; 任意字符 | . 的含义 +;; 有可能重复的 | + 的含义 +;; 之后跟着 "!" 这个字符串 + +;; 准备好了?试试看。 + +(boldify-names) + +;; `add-text-properties' 可以添加文字属性, 比如文字样式 + +;; 好的,我们成功了! + +;; 如果你想对一个变量或者函数有更多的了解: +;; +;; C-h v 变量 回车 +;; C-h f 函数 回车 +;; +;; 阅读Emacs Lisp官方文档: +;; +;; C-h i m elisp 回车 +;; +;; 在线阅读Emacs Lisp文档: +;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html + +;; 感谢以下同学的建议和反馈: +;; - Wes Hardaker +;; - notbob +;; - Kevin Montuori +;; - Arne Babenhauserheide +;; - Alan Schmitt +;; - spacegoing +``` + diff --git a/zh-cn/elixir-cn.html.markdown b/zh-cn/elixir-cn.html.markdown index daee8d3c..0ed1d823 100644 --- a/zh-cn/elixir-cn.html.markdown +++ b/zh-cn/elixir-cn.html.markdown @@ -1,5 +1,5 @@ --- -language: elixir +language: Elixir contributors: - ["Joao Marques", "http://github.com/mrshankly"] translators: diff --git a/zh-cn/fortran-cn.html.markdown b/zh-cn/fortran-cn.html.markdown new file mode 100644 index 00000000..ab521e0a --- /dev/null +++ b/zh-cn/fortran-cn.html.markdown @@ -0,0 +1,444 @@ +--- +language: Fortran +filename: learnfortran-cn.f90 +contributors: + - ["Robert Steed", "https://github.com/robochat"] +translators: + - ["Corvusnest", "https://github.com/Corvusnest"] +lang: zh-cn +--- + +Fortran 是最古老的计算机语言之一。它由 IBM 开发于 1950 年用于数值运算(Fortran 为 "Formula +Translation" 的缩写)。虽然该语言已年代久远,但目前仍用于高性能计算,如天气预报。 +该语言仍在持续发展,并且基本保持向下兼容。知名的版本为 Fortran 77, Fortran 90, +Fortran 95, Fortran 2008, Fortran 2015 和 Fortran 2023。 + +这篇概要将讨论 Fortran 2008 的一些特征。因为它是目前所广泛采用的标准版本,并且与最新版本的内容 +也基本相同(而 Fortran 77 则是一个非常不同的版本)。 + +```fortran +! 这是一个注释 + +program example ! 声明一个名为 example 的程序 + + ! 代码只能存在于程序、函数、子程序或模块中 + ! 使用缩进不是必需的,但推荐使用 + + ! 声明变量 + ! ========= + + ! 所有的声明必须在语句和表达式之前 + + implicit none ! 防止动态声明变量(推荐!) + ! implicit none 推荐在每个函数/程序/模块中重新声明... + + ! 注意 - Fortran 中对大小写不敏感 + real z + REAL Z2 + + real :: v, x ! 警告:默认的初始值取决于编译器! + real :: a = 3, b = 2E12, c = 0.01 + integer :: i, j, k = 1, m + real, parameter :: PI = 3.1415926535897931 ! 声明一个常数 + logical :: y = .TRUE., n = .FALSE. ! 布尔类型 + complex :: w = (0, 1) ! 单位虚数 + character(len=3) :: month ! 字符串,长度为 3 个字符 + + real :: array(6) ! 声明一个包含 6 个实数的数组 + real, dimension(4) :: arrayb ! 另一种声明数组的方式 + integer :: arrayc(-10:10) ! 具有自定义索引的数组 + real :: array2d(3, 2) ! 多维数组 + + ! 这些分隔符 '::' 并不总是必需的,但推荐使用 + + ! 还有许多其他的变量属性: + real, pointer :: p ! 声明一个指针 + + integer, parameter :: LP = selected_real_kind(20) + real(kind=LP) :: d ! 长精度变量 + + ! 警告:在声明过程中初始化变量会在函数中造成问题, + ! 因为这会自动暗示 'save' 属性, + ! 在函数调用之间保存变量的值一般情况下, + ! 除了常量外,声明和初始化的代码应该分开! + + ! 字符串 + ! ======= + + character :: a_char = 'i' + character(len=6) :: a_str = "qwerty" + character(len=30) :: str_b + character(len=*), parameter :: a_long_str = "This is a long string." + ! 使用 (len=*) 可以自动计算长度,但只适用于常量 + + str_b = a_str//" keyboard" ! 使用 // 运算符连接字符串 + + ! 赋值和算术 + ! ============= + + Z = 1 ! 对上面声明的变量 z 进行赋值(对大小写不敏感) + j = 10 + 2 - 3 + a = 11.54/(2.3*3.1) + b = 2**3 ! 幂运算 + + ! 流程控制语句和操作符 + ! =================== + + ! 单行 if 语句 + if (z == a) b = 4 ! 条件始终需要在括号中 + + if (z /= a) then ! z 不等于 a + ! 其他的比较操作符包括 < > <= >= == /= + b = 4 + else if (z .GT. a) then ! z 大于 a + ! 文本等价于符号操作符中的 .LT. .GT. .LE. .GE. .EQ. .NE. + b = 6 + else if (z < a) then ! 'then' 必须在本行上 + b = 5 ! 执行块必须在新的一行上 + else + b = 10 + end if ! end 语句后需要 'if'(或可以使用 'endif') + + if (.NOT. (x < c .AND. v >= a .OR. z == z)) then ! 布尔运算符 + inner: if (.TRUE.) then ! 可以对 if 结构命名 + b = 1 + end if inner ! then 必须命名对应的 endif 语句 + end if + + i = 20 + select case (i) + case (0, 1) ! 当 i == 0 或 i == 1 时 + j = 0 + case (2:10) ! 当 i 在 2 到 10 之间(包括边界)时 + j = 1 + case (11:) ! 当 i >= 11 时 + j = 2 + case default + j = 3 + end select + + month = 'jan' + ! 条件可以是整数、逻辑、或字符类型 + ! Select 结构也可以命名 + monthly:select case(month) + case ("jan") + j = 0 + case default + j = -1 + end select monthly + + do i = 2, 10, 2 ! 循环从 2 到 10(包括)以 2 为步长 + innerloop: do j = 1, 3 ! 循环也可以命名 + exit ! 退出循环 + end do innerloop + cycle ! 跳到下一个循环迭代 + end do + + ! 虽然存在 Goto 语句,但它被强烈不推荐 + goto 10 + stop 1 ! 立即停止代码(并返回指定的条件代码) +10 j = 201 ! 这一行被标记为 10 行 + + ! 数组 + ! ===== + array = (/1, 2, 3, 4, 5, 6/) + array = [1, 2, 3, 4, 5, 6] ! 使用 Fortran 2003 的表示法 + arrayb = [10.2, 3e3, 0.41, 4e-5] + array2d = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [3, 2]) + + ! Fortran 数组索引从 1 开始 + ! (默认情况下,但对于特定数组可以定义不同的索引) + v = array(1) ! 取数组的第一个元素 + v = array2d(2, 2) + + print *, array(3:5) ! 打印从第三个到第五个元素(包括) + print *, array2d(1, :) ! 打印二维数组的第一列 + + array = array*3 + 2 ! 可以对数组应用数学表达式 + array = array*array ! 数组操作是逐元素进行的 + ! array = array*array2d ! 这两个数组是不兼容的 + + ! 有许多内置函数可用于数组 + c = dot_product(array, array) ! 这是点积 + ! 使用 matmul() 进行矩阵运算 + c = sum(array) + c = maxval(array) + print *, minloc(array) + c = size(array) + print *, shape(array) + m = count(array > 0) + + ! 循环数组(通常使用 product() 函数) + v = 1 + do i = 1, size(array) + v = v*array(i) + end do + + ! 条件性地执行逐元素赋值 + array = [1, 2, 3, 4, 5, 6] + where (array > 3) + array = array + 1 + elsewhere(array == 2) + array = 1 + elsewhere + array = 0 + end where + + ! 隐含 do 循环是创建数组的紧凑方式 + array = [(i, i=1, 6)] ! 创建一个数组 [1,2,3,4,5,6] + array = [(i, i=1, 12, 2)] ! 创建一个数组 [1,3,5,7,9,11] + array = [(i**2, i=1, 6)] ! 创建一个数组 [1,4,9,16,25,36] + array = [(4, 5, i=1, 3)] ! 创建一个数组 [4,5,4,5,4,5] + + ! 输入/输出 + ! ========= + + print *, b ! 将变量 'b' 打印到命令行 + + ! 可以对打印的输出进行格式化 + print "(I6)", 320 ! 打印 ' 320' + print "(I6.4)", 3 ! 打印 ' 0003' + print "(F6.3)", 4.32 ! 打印 ' 4.320' + + ! 字母表示预期的类型,后面的数字表示用于打印值的字符数 + ! 字母可以是 I(整数),F(实数),E(工程表示法), + ! L(逻辑),A(字符串)... + print "(I3)", 3200 ! 打印 '***',因为该数字不适合 + + ! 可以有多个格式规范 + print "(I5,F6.2,E6.2)", 120, 43.41, 43.41 + print "(3I5)", 10, 20, 30 ! 整数的三次重复(字段宽度为 5) + print "(2(I5,F6.2))", 120, 43.42, 340, 65.3 ! 格式重复组合 + + ! 我们还可以从终端读取输入 + read (*, *) v + read (*, "(2F6.2)") v, x ! 读取两个数字 + + ! 写入文件 + open (unit=12, file="records.txt", status="replace") + ! 文件通过 'unit number' 引用,这个数字可以在 9:99 范围内选择 + ! Status 可以是 {'old','replace','new'} 中的一个 + write (12, "(F10.2,F10.2,F10.2)") c, b, a + close (12) + + ! 读取文件 + open (newunit=m, file="records.txt", status="old") + ! 文件通过 'new unit number' 引用,编译器为您选择一个整数 + read (unit=m, fmt="(3F10.2)") a, b, c + close (m) + + ! 还有更多功能可用,超出了本文所讨论的范围, + ! 还有由于与旧版本的 Fortran 的向后兼容性而存在的替代方案 + + ! 内置函数 + ! =========== + + ! Fortran 大约有 200 个语言内部的函数/子程序 + ! 例如 - + call cpu_time(v) ! 将 'v' 设置为以秒为单位的时间 + k = ior(i, j) ! 两个整数的位 OR 运算 + v = log10(x) ! 以 10 为底的对数 + i = floor(b) ! 返回小于或等于 x 的最接近的整数 + v = aimag(w) ! 复数的虚部 + + ! 函数和子程序 + ! ============== + + ! 子程序运行一些代码,并可以对输入值产生副作用或修改输入值 + + call routine(a, c, v) ! 子程序调用 + + ! 函数采用一系列输入参数,并返回一个单个值 + ! 不过,输入参数可能仍会被修改,并且会执行副作用 + + m = func(3, 2, k) ! 函数调用 + + ! 函数调用还可以在表达式中使用 + print *, func2(3, 2, k) + + ! 一个纯函数是一个不修改其输入参数, + ! 也不会引起任何副作用的函数 + m = func3(3, 2, k) + +contains ! 包含程序内部定义的子程序的区域 + + ! Fortran 有几种稍微不同的方式来定义函数 + + integer function func(a, b, c) ! 函数返回一个整数值 + ! implicit none ! 子变量域可以不再声明 implicit none + integer, intent(in) :: a, b, c ! 在函数内部定义输入参数的类型 + + if (a >= 2) then + func = a + b + c ! 返回变量默认为函数名 + return ! 随时可以从函数返回当前值 + end if + func = a + c + + ! 在函数的末尾不需要 return 语句 + end function func + + function func2(a, b, c) result(f) ! 返回变量声明为 'f' + integer, intent(in) :: a, b ! 可以声明和强制约定变量 + ! 不会被函数修改 + integer, intent(inout) :: c + integer :: f ! 函数返回类型在函数内部声明 + integer :: cnt = 0 ! 注意:初始化暗示变量在函数调用之间保存 + ! + + f = a + b - c + c = 4 ! 修改输入变量的值 + cnt = cnt + 1 ! 计算函数调用的次数 + + end function func2 + + pure function func3(a, b, c) ! 纯函数不能有副作用 + integer, intent(in) :: a, b, c + integer :: func3 + + func3 = a*b*c + + end function func3 + + subroutine routine(d, e, f) + real, intent(inout) :: f + real, intent(in) :: d, e + + f = 2*d + 3*e + f + + end subroutine routine + +end program example ! 程序定义结束-------------------------- + +! 函数和子程序在程序列表之外声明,在程序之间以及模块中声明时,需要使用 interface 声明(即使它们在同一源文件中)(见下面)将它们定义在模块或程序的 'contains' 部分更容易 + +elemental real function func4(a) result(res) +! elemental 函数是一个纯函数,它采用标量输入变量, +! 但也可以在数组上独立应用,并返回一个新的数组 + real, intent(in) :: a + + res = a**2 + 1.0 + +end function func4 + +! 模块 +! ======= + +! 模块是在可重用性中将相关的声明、函数和子程序结合在一起的有用方式 + +module fruit + + real :: apple + real :: pear + real :: orange + +end module fruit + +module fruity + ! 声明的顺序必须是:模块、接口、变量 + !(也可以在程序中声明模块和接口) + + use fruit, only: apple, pear ! 使用 fruit 模块中的 apple 和 pear + implicit none ! 导入模块之后 + + private ! 将一些内容私有化(默认为公共) + ! 显式将一些变量/函数声明为公共 + public :: apple, mycar, create_mycar + ! 将一些变量/函数声明为模块私有(本例中是多余的) + private :: func4 + + ! 接口 + ! ======== + ! 在模块内部(最好放在 'contains' 部分)显式声明外部函数/过程 + interface + elemental real function func4(a) result(res) + real, intent(in) :: a + end function func4 + end interface + + ! 可以使用命名接口定义重载函数 + interface myabs + ! 可以使用 'module procedure' 关键字包括模块内已经定义的函数 + module procedure real_abs, complex_abs + end interface + + ! 派生数据类型 + ! ================== + ! 可以创建自定义的结构化数据集合 + type car + character(len=100) :: model + real :: weight !(千克) + real :: dimensions(3) ! 即,长度-宽度-高度(米) + character :: colour + contains + procedure :: info ! 将过程绑定到类型 + end type car + + type(car) :: mycar ! 声明自定义类型的变量 + ! 请查看 create_mycar() 程序的用法 + + ! 注意:模块中没有可以执行的语句 + +contains + + subroutine create_mycar(mycar) + ! 演示派生数据类型的用法 + type(car), intent(out) :: mycar + + ! 使用 '%' 运算符访问类型元素 + mycar%model = "Ford Prefect" + mycar%colour = 'r' + mycar%weight = 1400 + mycar%dimensions(1) = 5.0 ! 默认索引从 1 开始! + mycar%dimensions(2) = 3.0 + mycar%dimensions(3) = 1.5 + + end subroutine create_mycar + + subroutine info(self) + class(car), intent(in) :: self + ! 使用 'class' 关键字将过程绑定到类型 + + print *, "Model : ", self%model + print *, "Colour : ", self%colour + print *, "Weight : ", self%weight + print *, "Dimensions: ", self%dimensions + + end subroutine info + + real pure function real_abs(x) + real, intent(in) :: x + + if (x < 0) then + real_abs = -x + else + real_abs = x + end if + + end function real_abs + + real pure function complex_abs(z) + complex, intent(in) :: z + ! 长行可以使用继续字符 '&' 进行延续 + + complex_abs = sqrt(real(z)**2 + & + aimag(z)**2) + + end function complex_abs + +end module fruity + +``` + +### 更多资源 + +了解更多的 Fortran 信息: + ++ [wikipedia](https://en.wikipedia.org/wiki/Fortran) ++ [Fortran-lang Organization](https://fortran-lang.org/) ++ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features) ++ [fortranwiki.org](http://fortranwiki.org) ++ [www.fortran90.org/](http://www.fortran90.org) ++ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/) ++ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran) ++ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf) ++ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html) diff --git a/zh-cn/fortran95-cn.html.markdown b/zh-cn/fortran95-cn.html.markdown deleted file mode 100644 index e28d309f..00000000 --- a/zh-cn/fortran95-cn.html.markdown +++ /dev/null @@ -1,435 +0,0 @@ ---- -language: Fortran -filename: learnfortran-cn.f95 -contributors: - - ["Robert Steed", "https://github.com/robochat"] -translators: - - ["Corvusnest", "https://github.com/Corvusnest"] -lang: zh-cn ---- - -Fortran 是最古老的计算机语言之一。它由IBM开发于1950年用于数值运算(Fortran 为 "Formula -Translation" 的缩写)。虽然该语言已年代久远,但目前仍用于高性能计算,如天气预报。 -该语言仍在持续发展,并且基本保持向下兼容。知名的版本为 Fortran 77, Fortran 90, -Fortran 95, Fortran 2003, Fortran 2008 与 Fortran 2015。 - -这篇概要将讨论 Fortran 95 的一些特征。因为它是目前所广泛采用的标准版本,并且与最新版本的内容 -也基本相同(而 Fortran 77 则是一个非常不同的版本)。 - -```fortran - -! 这是一行注释 - - -program example !声明一个叫做 example 的程序 - - ! 代码只能放在程序、函数、子程序或者模块内部 - ! 推荐使用缩进,但不是必须的。 - - ! 声明变量 - ! =================== - - ! 所有的声明必须放在语句与表达式之前 - - implicit none !阻止变量的隐式声明 (推荐!) - ! Implicit none 必须在每一个 函数/程序/模块 中进行声明 - - ! 重要 - Fortran 对大小写不敏感 - real z - REAL Z2 - - real :: v,x ! 警告: 默认值取决于编译器! - real :: a = 3, b=2E12, c = 0.01 - integer :: i, j, k=1, m - real, parameter :: PI = 3.1415926535897931 !声明一个常量 - logical :: y = .TRUE. , n = .FALSE. !布尔值 - complex :: w = (0,1) !sqrt(-1) (译注: 定义复数,此为-1的平方根) - character (len=3) :: month !长度为3的字符串 - - real :: array(6) !声明长度为6的浮点数数组 - real, dimension(4) :: arrayb !声明数组的另一种方法 - integer :: arrayc(-10:10) !有着自定义索引的数组 - real :: array2d(3,2) !多维数组 - - ! 分隔符 '::' 并不总是必要的,但推荐使用 - - ! 还存在很多其他的变量特征: - real, pointer :: p !声明一个指针 - - integer, parameter :: LP = selected_real_kind(20) - real (kind = LP) :: d !长精度变量 - - ! 警告:在声明期间初始化变量将导致在函数内发生问题,因为这将自动具备了 “save” 属性, - ! 因此变量的值在函数的多次调用期间将被存储。一般来说,除了常量,应分开声明与初始化! - - ! 字符串 - ! ======= - - character :: a_char = 'i' - character (len = 6) :: a_str = "qwerty" - character (len = 30) :: str_b - character (len = *), parameter :: a_long_str = "This is a long string." - !可以通过使用 (len=*) 来自动判断长度,但只对常量有效 - - str_b = a_str // " keyboard" !通过 // 操作符来连接字符串 - - - ! 任务与计算 - ! ======================= - - Z = 1 !向之前声明的变量 z 赋值 (大小写不敏感). - j = 10 + 2 - 3 - a = 11.54 / (2.3 * 3.1) - b = 2**3 !幂 - - - ! 控制流程语句 与 操作符 - ! =================================== - - !单行 if 语句 - if (z == a) b = 4 !判别句永远需要放在圆括号内 - - if (z /= a) then !z 不等于 a - ! 其他的比较运算符: < > <= >= == /= - b = 4 - else if (z .GT. a) then !z 大于(Greater) a - ! 文本形式的比较运算符: .LT. .GT. .LE. .GE. .EQ. .NE. - b = 6 - else if (z < a) then !'then' 必须放在该行 - b = 5 !执行部分必须放在新的一行里 - else - b = 10 - end if !结束语句需要 'if' (也可以用 'endif'). - - - if (.NOT. (x < c .AND. v >= a .OR. z == z)) then !布尔操作符 - inner: if (.TRUE.) then !可以为 if 结构命名 - b = 1 - endif inner !接下来必须命名 endif 语句. - endif - - - i = 20 - select case (i) - case (0) !当 i == 0 - j=0 - case (1:10) !当 i 为 1 到 10 之内 ( 1 <= i <= 10 ) - j=1 - case (11:) !当 i>=11 - j=2 - case default - j=3 - end select - - - month = 'jan' - ! 状态值可以为整数、布尔值或者字符类型 - ! Select 结构同样可以被命名 - monthly: select case (month) - case ("jan") - j = 0 - case default - j = -1 - end select monthly - - do i=2,10,2 !从2到10(包含2和10)以2为步进值循环 - innerloop: do j=1,3 !循环同样可以被命名 - exit !跳出循环 - end do innerloop - cycle !重复跳入下一次循环 - enddo - - - ! Goto 语句是存在的,但强烈不建议使用 - goto 10 - stop 1 !立即停止程序 (返回一个设定的状态码). -10 j = 201 !这一行被标注为 10 行 (line 10) - - - ! 数组 - ! ====== - array = (/1,2,3,4,5,6/) - array = [1,2,3,4,5,6] !当使用 Fortran 2003 版本. - arrayb = [10.2,3e3,0.41,4e-5] - array2d = reshape([1.0,2.0,3.0,4.0,5.0,6.0], [3,2]) - - ! Fortran 数组索引起始于 1 - ! (默认下如此,也可以为数组定义不同的索引起始) - v = array(1) !获取数组的第一个元素 - v = array2d(2,2) - - print *, array(3:5) !打印从第3到第五5之内的所有元素 - print *, array2d(1,:) !打印2维数组的第一列 - - array = array*3 + 2 !可为数组设置数学表达式 - array = array*array !数组操作支持元素级(操作) (element-wise) - !array = array*array2d !这两类数组并不是同一个维度的 - - ! 有很多内置的数组操作函数 - c = dot_product(array,array) !点乘 (点积) - ! 用 matmul() 来进行矩阵运算. - c = sum(array) - c = maxval(array) - print *, minloc(array) - c = size(array) - print *, shape(array) - m = count(array > 0) - - ! 遍历一个数组 (一般使用 Product() 函数). - v = 1 - do i = 1, size(array) - v = v*array(i) - end do - - ! 有条件地执行元素级操作 - array = [1,2,3,4,5,6] - where (array > 3) - array = array + 1 - elsewhere (array == 2) - array = 1 - elsewhere - array = 0 - end where - - ! 隐式DO循环可以很方便地创建数组 - array = [ (i, i = 1,6) ] !创建数组 [1,2,3,4,5,6] - array = [ (i, i = 1,12,2) ] !创建数组 [1,3,5,7,9,11] - array = [ (i**2, i = 1,6) ] !创建数组 [1,4,9,16,25,36] - array = [ (4,5, i = 1,3) ] !创建数组 [4,5,4,5,4,5] - - - ! 输入/输出 - ! ============ - - print *, b !向命令行打印变量 'b' - - ! 我们可以格式化输出 - print "(I6)", 320 !打印 ' 320' - print "(I6.4)", 3 !打印 ' 0003' - print "(F6.3)", 4.32 !打印 ' 4.320' - - - ! 该字母与数值规定了给定的数值与字符所用于打印输出的类型与格式 - ! 字母可为 I (整数), F (浮点数), E (工程格式), - ! L (逻辑/布尔值), A (字符) ... - print "(I3)", 3200 !如果数值无法符合格式将打印 '***' - - ! 可以同时设定多种格式 - print "(I5,F6.2,E6.2)", 120, 43.41, 43.41 - print "(3I5)", 10, 20, 30 !连续打印3个整数 (字段宽度 = 5). - print "(2(I5,F6.2))", 120, 43.42, 340, 65.3 !连续分组格式 - - ! 我们也可以从终端读取输入 - read *, v - read "(2F6.2)", v, x !读取2个数值 - - ! 读取文件 - open(unit=11, file="records.txt", status="old") - ! 文件被引用带有一个单位数 'unit', 为一个取值范围在9-99的整数 - ! 'status' 可以为 {'old','replace','new'} 其中之一 - read(unit=11, fmt="(3F10.2)") a, b, c - close(11) - - ! 写入一个文件 - open(unit=12, file="records.txt", status="replace") - write(12, "(F10.2,F10.2,F10.2)") c, b, a - close(12) - ! 在讨论范围之外的还有更多的细节与可用功能,并于老版本的 Fortran 保持兼容 - - - ! 内置函数 - ! ================== - - ! Fortran 拥有大约 200 个内置函数/子程序 - ! 例子 - call cpu_time(v) !以秒为单位设置时间 - k = ior(i,j) !2个整数的位或运算 - v = log10(x) !以10为底的log运算 - i = floor(b) !返回一个最接近的整数小于或等于x (地板数) - v = aimag(w) !复数的虚数部分 - - - ! 函数与子程序 - ! ======================= - - ! 一个子程序会根据输入值运行一些代码并会导致副作用 (side-effects) 或修改输入值 - ! (译者注: 副作用是指对子程序/函数外的环境产生影响,如修改变量) - - call routine(a,c,v) !调用子程序 - - ! 一个函数会根据输入的一系列数值来返回一个单独的值 - ! 但输入值仍然可能被修改以及产生副作用 - - m = func(3,2,k) !调用函数 - - ! 函数可以在表达式内被调用 - Print *, func2(3,2,k) - - ! 一个纯函数不会去修改输入值或产生副作用 - m = func3(3,2,k) - - -contains ! 用于定义程序内部的副程序(sub-programs)的区域 - - ! Fortran 拥有一些不同的方法去定义函数 - - integer function func(a,b,c) !一个返回一个整数的函数 - implicit none !最好也在函数内将含蓄模式关闭 (implicit none) - integer :: a,b,c !输入值类型定义在函数内部 - if (a >= 2) then - func = a + b + c !返回值默认为函数名 - return !可以在函数内任意时间返回当前值 - endif - func = a + c - ! 在函数的结尾不需要返回语句 - end function func - - - function func2(a,b,c) result(f) !将返回值声明为 'f' - implicit none - integer, intent(in) :: a,b !可以声明让变量无法被函数修改 - integer, intent(inout) :: c - integer :: f !函数的返回值类型在函数内声明 - integer :: cnt = 0 !注意 - 隐式的初始化变量将在函数的多次调用间被存储 - f = a + b - c - c = 4 !变动一个输入变量的值 - cnt = cnt + 1 !记录函数的被调用次数 - end function func2 - - - pure function func3(a,b,c) !一个没有副作用的纯函数 - implicit none - integer, intent(in) :: a,b,c - integer :: func3 - func3 = a*b*c - end function func3 - - - subroutine routine(d,e,f) - implicit none - real, intent(inout) :: f - real, intent(in) :: d,e - f = 2*d + 3*e + f - end subroutine routine - - -end program example ! 函数定义完毕 ----------------------- - -! 函数与子程序的外部声明对于生成程序清单来说,需要一个接口声明(即使它们在同一个源文件内)(见下) -! 使用 'contains' 可以很容易地在模块或程序内定义它们 - -elemental real function func4(a) result(res) -! 一个元函数(elemental function) 为一个纯函数使用一个标量输入值 -! 但同时也可以用在一个数组并对其中的元素分别处理,之后返回一个新的数组 - real, intent(in) :: a - res = a**2 + 1.0 -end function func4 - - -! 模块 -! ======= - -! 模块十分适合于存放与复用相关联的一组声明、函数与子程序 - -module fruit - real :: apple - real :: pear - real :: orange -end module fruit - - -module fruity - - ! 声明必须按照顺序: 模块、接口、变量 - ! (同样可在程序内声明模块和接口) - - use fruit, only: apple, pear ! 使用来自于 fruit 模块的 apple 和 pear - implicit none !在模块导入后声明 - - private !使得模块内容为私有(private)(默认为公共 public) - ! 显式声明一些变量/函数为公共 - public :: apple,mycar,create_mycar - ! 声明一些变量/函数为私有(在当前情况下没必要)(译注: 因为前面声明了模块全局 private) - private :: func4 - - ! 接口 - ! ========== - ! 在模块内显式声明一个外部函数/程序 - ! 一般最好将函数/程序放进 'contains' 部分内 - interface - elemental real function func4(a) result(res) - real, intent(in) :: a - end function func4 - end interface - - ! 重载函数可以通过已命名的接口来定义 - interface myabs - ! 可以通过使用 'module procedure' 关键词来包含一个已在模块内定义的函数 - module procedure real_abs, complex_abs - end interface - - ! 派生数据类型 - ! ================== - ! 可创建自定义数据结构 - type car - character (len=100) :: model - real :: weight !(公斤 kg) - real :: dimensions(3) !例: 长宽高(米) - character :: colour - end type car - - type(car) :: mycar !声明一个自定义类型的变量 - ! 用法具体查看 create_mycar() - - ! 注: 模块内没有可执行的语句 - -contains - - subroutine create_mycar(mycar) - ! 展示派生数据类型的使用 - implicit none - type(car),intent(out) :: mycar - - ! 通过 '%' 操作符来访问(派生数据)类型的元素 - mycar%model = "Ford Prefect" - mycar%colour = 'r' - mycar%weight = 1400 - mycar%dimensions(1) = 5.0 !索引默认起始值为 1 ! - mycar%dimensions(2) = 3.0 - mycar%dimensions(3) = 1.5 - - end subroutine - - real function real_abs(x) - real :: x - if (x<0) then - real_abs = -x - else - real_abs = x - end if - end function real_abs - - real function complex_abs(z) - complex :: z - ! 过长的一行代码可通过延续符 '&' 来换行 - complex_abs = sqrt(real(z)**2 + & - aimag(z)**2) - end function complex_abs - - -end module fruity - -``` - -### 更多资源 - -了解更多的 Fortran 信息: - -+ [wikipedia](https://en.wikipedia.org/wiki/Fortran) -+ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features) -+ [fortranwiki.org](http://fortranwiki.org) -+ [www.fortran90.org/](http://www.fortran90.org) -+ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/) -+ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran) -+ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf) -+ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html) diff --git a/zh-cn/gdscript-cn.html.markdown b/zh-cn/gdscript-cn.html.markdown new file mode 100644 index 00000000..7c731533 --- /dev/null +++ b/zh-cn/gdscript-cn.html.markdown @@ -0,0 +1,317 @@ +--- +language: GDScript +contributors: + - ["Wichamir", "https://github.com/Wichamir/"] +translators: + - ["ShiftWatchOut", "https://github.com/ShiftWatchOut"] +filename: learngdscript-cn.gd +lang: zh-cn +--- + +GDScript 是一种动态类型的脚本语言,专门为免费开源游戏引擎 Godot 制作。 GDScript 的语法类似 Python。 +它的主要优点是易于使用和与引擎深度集成。 它非常适合游戏开发。 + +## 基础 + +```nim +# 单行注释使用 # 号书写。 +""" + 多行 + 注释 + 是 + 使用 + 文档字符串(docstring) + 书写。 +""" + +# 脚本文件本身默认是一个类,文件名为类名,您也可以为其定义其他名称。 +class_name MyClass + +# 继承 +extends Node2D + +# 成员变量 +var x = 8 # 整型 +var y = 1.2 # 浮点型 +var b = true # 布尔型 +var s = "Hello World!" # 字符串 +var a = [1, false, "brown fox"] # 数组(Array) - 类似于 Python 的列表(list), + # 它可以同时保存不同类型的变量。 +var d = { + "key" : "value", + 42 : true +} # 字典包含键值对。 +var p_arr = PoolStringArray(["Hi", "there", "!"]) # 池数组只能包含单一类型。 + # 放入其他类型会被转换为目标类型 + +# 内置向量类型: +var v2 = Vector2(1, 2) +var v3 = Vector3(1, 2, 3) + +# 常量 +const ANSWER_TO_EVERYTHING = 42 +const BREAKFAST = "Spam and eggs!" + +# 枚举 +enum { ZERO, ONE , TWO, THREE } +enum NamedEnum { ONE = 1, TWO, THREE } + +# 导出的变量将在检查器中可见。 +export(int) var age +export(float) var height +export var person_name = "Bob" # 如果设置了默认值,则不需要类型注解。 + +# 函数 +func foo(): + pass # pass 关键字是未书写的代码的占位符 + +func add(first, second): + return first + second + +# 打印值 +func printing(): + print("GDScript ", "简直", "棒呆了") + prints("这", "些", "字", "被", "空", "格", "分", "割") + printt("这", "些", "字", "被", "制", "表", "符", "分", "割") + printraw("这句话将被打印到系统控制台。") + +# 数学 +func doing_math(): + var first = 8 + var second = 4 + print(first + second) # 12 + print(first - second) # 4 + print(first * second) # 32 + print(first / second) # 2 + print(first % second) # 0 + # 还有 +=, -=, *=, /=, %= 等操作符,但并没有 ++ 和 -- . + print(pow(first, 2)) # 64 + print(sqrt(second)) # 2 + printt(PI, TAU, INF, NAN) # 内置常量 + +# 控制流 +func control_flow(): + x = 8 + y = 2 # y 最初被设为一个浮点数, + # 但我们可以利用语言提供的动态类型能力将它的类型变为整型! + + if x < y: + print("x 小于 y") + elif x > y: + print("x 大于 y") + else: + print("x 等于 y") + + var a = true + var b = false + var c = false + if a and b or not c: # 你也可以用 &&, || 和 ! + print("看到这句说明上面的条件判断为真!") + + for i in range(20): # GDScript 有类似 Python 的 range 函数 + print(i) # 所以这句将打印从 0 到 19 的数字 + + for i in 20: # 与 Python 略有不同的是,你可以直接用一个整型数开始循环 + print(i) # 所以这行代码也将打印从 0 到 19 的数字 + + for i in ["two", 3, 1.0]: # 遍历数组 + print(i) + + while x > y: + printt(x, y) + y += 1 + + x = 2 + y = 10 + while x < y: + x += 1 + if x == 6: + continue # continue 语句使 x 等于 6 时,程序跳过这次循环后面的代码,不会打印 6。 + prints("x 等于:", x) + if x == 7: + break # 循环将在 x 等于 7 处跳出,后续所有循环不再执行,因此不会打印 8、9 和 10 + + match x: + 1: + print("match 很像其他语言中的 switch.") + 2: + print("但是,您不需要在每个值之前写一个 case 关键字。") + 3: + print("此外,每种情况都会默认跳出。") + break # 错误!不要在 match 里用 break 语句! + 4: + print("如果您需要跳过后续代码,这里也使用 continue 关键字。") + continue + _: + print("下划线分支,在其他分支都不满足时,在这里书写默认的逻辑。") + + # 三元运算符 (写在一行的 if-else 语句) + prints("x 是", "正值" if x >= 0 else "负值") + +# 类型转换 +func casting_examples(): + var i = 42 + var f = float(42) # 使用变量构造函数强制转换 + var b = i as bool # 或使用 as 关键字 + +# 重载函数 +# 通常,我们只会重载以下划线开头的内置函数, +# 但实际上您可以重载几乎任何函数。 + +# _init 在对象初始化时被调用。 +# 这是对象的构造函数。 +func _init(): + # 在此处初始化对象的内部属性。 + pass + +# _ready 在脚本节点及其子节点进入场景树时被调用。 +func _ready(): + pass + +# _process 在每一帧上都被调用。 +func _process(delta): + # 传递给此函数的 delta 参数是时间,即从上一帧到当前帧经过的秒数。 + print("Delta 时间为:", delta) + +# _physics_process 在每个物理帧上都被调用。 +# 这意味着 delta 应该是恒定的。 +func _physics_process(delta): + # 使用向量加法和乘法进行简单移动。 + var direction = Vector2(1, 0) # 或使用 Vector2.RIGHT + var speed = 100.0 + self.global_position += direction * speed * delta + # self 指向当前类的实例 + +# 重载函数时,您可以使用 . 运算符调用父函数 +# like here: +func get_children(): + # 在这里做一些额外的事情。 + var r = .get_children() # 调用父函数的实现 + return r + +# 内部类 +class InnerClass: + extends Object + + func hello(): + print("来自内部类的 Hello!") + +func use_inner_class(): + var ic = InnerClass.new() + ic.hello() + ic.free() # 可以自行释放内存 +``` + +## 访问场景树中其他节点 + +```nim +extends Node2D + +var sprite # 该变量将用来保存引用。 + +# 您可以在 _ready 中获取对其他节点的引用。 +func _ready() -> void: + # NodePath 对于访问节点很有用。 + # 将 String 传递给其构造函数来创建 NodePath: + var path1 = NodePath("path/to/something") + # 或者使用 NodePath 字面量: + var path2 = @"path/to/something" + # NodePath 示例: + var path3 = @"Sprite" # 相对路径,当前节点的直接子节点 + var path4 = @"Timers/Firerate" # 相对路径,子节点的子节点 + var path5 = @".." # 当前节点的父节点 + var path6 = @"../Enemy" # 当前节点的兄弟节点 + var path7 = @"/root" # 绝对路径,等价于 get_tree().get_root() + var path8 = @"/root/Main/Player/Sprite" # Player 的 Sprite 的绝对路径 + var path9 = @"Timers/Firerate:wait_time" # 访问属性 + var path10 = @"Player:position:x" # 访问子属性 + + # 最后,获取节点引用可以使用以下方法: + sprite = get_node(@"Sprite") as Sprite # 始终转换为您期望的类型 + sprite = get_node("Sprite") as Sprite # 这里 String 被隐式转换为 NodePath + sprite = get_node(path3) as Sprite + sprite = get_node_or_null("Sprite") as Sprite + sprite = $Sprite as Sprite + +func _process(delta): + # 现在我们就可以在别处使用 sprite 里保存的引用了。 + prints("Sprite 有一个全局位置 ", sprite.global_position) + +# 在 _ready 执行之前,使用 onready 关键字为变量赋值。 +# 这是一种常用的语法糖。 +onready var tween = $Tween as Tween + +# 您可以导出这个 NodePath,以便在检查器中给它赋值。 +export var nodepath = @"" +onready var reference = get_node(nodepath) as Node +``` + +## 信号(Signals) + +信号系统是 Godot 对观察者编程模式的实现。例子如下: + +```nim +class_name Player extends Node2D + +var hp = 10 + +signal died() # 定义一个信号 +signal hurt(hp_old, hp_new) # 信号可以带参数 + +func apply_damage(dmg): + var hp_old = hp + hp -= dmg + emit_signal("hurt", hp_old, hp) # 发出信号并传递参数 + if hp <= 0: + emit_signal("died") + +func _ready(): + # 将信号 "died" 连接到 self 中定义的 _on_death 函数 + self.connect("died", self, "_on_death") + +func _on_death(): + self.queue_free() # 死亡时销毁 Player +``` + +## 类型注解 + +GDScript 可以选择性地使用静态类型。 + +```nim +extends Node + +var x: int # 定义带有类型的变量 +var y: float = 4.2 +var z := 1.0 # 使用 := 运算符根据默认值推断类型 + +onready var node_ref_typed := $Child as Node + +export var speed := 50.0 + +const CONSTANT := "Typed constant." + +func _ready() -> void: + # 此函数不返回任何东西 + x = "string" # 错误!不要更改类型! + return + +func join(arg1: String, arg2: String) -> String: + # 此函数接受两个 String 并返回一个 String。 + return arg1 + arg2 + +func get_child_at(index: int) -> Node: + # 此函数接受一个 int 并返回一个 Node + return get_children()[index] + +signal example(arg: int) # 错误!信号不能接受类型参数! +``` + +## 延展阅读 + +* [Godot's Website](https://godotengine.org/) +* [Godot Docs](https://docs.godotengine.org/en/stable/) +* [Getting started with GDScript](https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/index.html) +* [NodePath](https://docs.godotengine.org/en/stable/classes/class_nodepath.html) +* [Signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html) +* [GDQuest](https://www.gdquest.com/) +* [GDScript.com](https://gdscript.com/)
\ No newline at end of file diff --git a/zh-cn/git-cn.html.markdown b/zh-cn/git-cn.html.markdown index d471ab5d..9dfbbb93 100644 --- a/zh-cn/git-cn.html.markdown +++ b/zh-cn/git-cn.html.markdown @@ -234,7 +234,7 @@ $ git diff HEAD # 在搜索结果中显示行号 $ git config --global grep.lineNumber true -# 是搜索结果可读性更好 +# 使得搜索结果可读性更好 $ git config --global alias.g "grep --break --heading --line-number" ``` @@ -370,5 +370,3 @@ $ git rm /pather/to/the/file/HelloWorld.c * [Atlassian Git - 教程与工作流程](https://www.atlassian.com/git/) * [SalesForce Cheat Sheet](https://na1.salesforce.com/help/doc/en/salesforce_git_developer_cheatsheet.pdf) - -* [GitGuys](http://www.gitguys.com/) diff --git a/zh-cn/go-cn.html.markdown b/zh-cn/go-cn.html.markdown index 37b4b137..a2b71761 100644 --- a/zh-cn/go-cn.html.markdown +++ b/zh-cn/go-cn.html.markdown @@ -22,7 +22,7 @@ Go语言有非常棒的标准库,还有一个充满热情的社区。 注释 */ // 导入包的子句在每个源文件的开头。 -// Main比较特殊,它用来声明可执行文件,而不是一个库。 +// main比较特殊,它用来声明可执行文件,而不是一个库。 package main // Import语句声明了当前文件引用的包。 @@ -35,12 +35,12 @@ import ( "strconv" // 字符串转换 ) -// 函数声明:Main是程序执行的入口。 +// 函数声明:main是程序执行的入口。 // 不管你喜欢还是不喜欢,反正Go就用了花括号来包住函数体。 func main() { // 往标准输出打印一行。 // 用包名fmt限制打印函数。 - fmt.Println("天坑欢迎你!") + fmt.Println("你好世界") // 调用当前包的另一个函数。 beyondHello() @@ -78,10 +78,10 @@ func learnTypes() { // 非ascii字符。Go使用UTF-8编码。 g := 'Σ' // rune类型,int32的别名,使用UTF-8编码 - f := 3.14195 // float64类型,IEEE-754 64位浮点数 + f := 3.14159 // float64类型,IEEE-754 64位浮点数 c := 3 + 4i // complex128类型,内部使用两个float64表示 - // Var变量可以直接初始化。 + // var变量可以直接初始化。 var u uint = 7 // unsigned 无符号变量,但是实现依赖int型变量的长度 var pi float32 = 22. / 7 @@ -141,7 +141,7 @@ func learnTypes() { // 只用写return一个词就能将函数内指定名称的变量返回 func learnNamedReturns(x, y int) (z int) { z = x * y - return // z is implicit here, because we named it earlier. + return // 隐式返回z,因为前面指定了它。 } // Go全面支持垃圾回收。Go有指针,但是不支持指针运算。 @@ -161,7 +161,7 @@ func expensiveComputation() int { } func learnFlowControl() { - // If需要花括号,括号就免了 + // if需要花括号,括号就免了 if true { fmt.Println("这句话肯定被执行") } @@ -392,15 +392,15 @@ func requestServer() { ## 更进一步 -关于Go的一切你都可以在[Go官方网站](http://golang.org/)找到。 +关于Go的一切你都可以在[Go官方网站](https://go.dev/)找到。 在那里你可以获得教程参考,在线试用,和更多的资料。 -在简单的尝试过后,在[官方文档](https://golang.org/doc/)那里你会得到你所需要的所有资料、关于编写代码的规范、库和命令行工具的文档与Go的版本历史。 +在简单的尝试过后,在[官方文档](https://go.dev/doc/)那里你会得到你所需要的所有资料、关于编写代码的规范、库和命令行工具的文档与Go的版本历史。 强烈推荐阅读语言定义部分,很简单而且很简洁!(赶时髦!) -你还可以前往[Go在线体验中心](https://play.golang.org/p/tnWMjr16Mm)进,在浏览器里修改并运行这些代码,一定要试一试哦!你可以将[https://play.golang.org](https://play.golang.org)当作一个[REPL](https://en.wikipedia.org/wiki/Read-eval-print_loop),在那里体验语言特性或运行自己的代码,连环境都不用配! +你还可以前往[Go在线体验中心](https://go.dev/play/p/tnWMjr16Mm)进,在浏览器里修改并运行这些代码,一定要试一试哦!你可以将[https://go.dev/play/](https://go.dev/play/)当作一个[REPL](https://en.wikipedia.org/wiki/Read-eval-print_loop),在那里体验语言特性或运行自己的代码,连环境都不用配! -学习Go还要阅读Go[标准库的源代码](http://golang.org/src/),全部文档化了,可读性非常好,可以学到go,go style和go idioms。在[文档](http://golang.org/pkg/)中点击函数名,源代码就出来了! +学习Go还要阅读Go[标准库的源代码](https://go.dev/src/),全部文档化了,可读性非常好,可以学到go,go style和go idioms。在[文档](https://go.dev/pkg/)中点击函数名,源代码就出来了! [Go by example](https://gobyexample.com/)也是一个学习的好地方。 diff --git a/zh-cn/haskell-cn.html.markdown b/zh-cn/haskell-cn.html.markdown index c854169e..d653c58c 100644 --- a/zh-cn/haskell-cn.html.markdown +++ b/zh-cn/haskell-cn.html.markdown @@ -128,7 +128,7 @@ snd ("haskell", 1) -- 1 -- 一个接受两个变量的简单函数 add a b = a + b --- 注意,如果你使用 ghci (Hakell 解释器),你需要使用 `let`,也就是 +-- 注意,如果你使用 ghci (Haskell 解释器),你需要使用 `let`,也就是 -- let add a b = a + b -- 调用函数 diff --git a/zh-cn/java-cn.html.markdown b/zh-cn/java-cn.html.markdown index 27003f3e..53a423a8 100644 --- a/zh-cn/java-cn.html.markdown +++ b/zh-cn/java-cn.html.markdown @@ -190,7 +190,7 @@ public class LearnJava { { //System.out.println(fooWhile); //增加计数器 - //遍历99次, fooWhile 0->99 + //遍历100次, fooWhile 0->99 fooWhile++; } System.out.println("fooWhile Value: " + fooWhile); @@ -201,7 +201,7 @@ public class LearnJava { { //System.out.println(fooDoWhile); //增加计数器 - //遍历99次, fooDoWhile 0->99 + //遍历100次, fooDoWhile 0->99 fooDoWhile++; }while(fooDoWhile < 100); System.out.println("fooDoWhile Value: " + fooDoWhile); @@ -297,8 +297,8 @@ class Bicycle { // Bicycle 类的成员变量和方法 public int cadence; // Public: 任意位置均可访问 private int speed; // Private: 只在同类中可以访问 - protected int gear; // Protected: 可以在同类与子类中可以访问 - String name; // default: 可以在包内中可以访问 + protected int gear; // Protected: 可以在同类与子类中访问 + String name; // default: 可以在包内访问 // 构造函数是初始化一个对象的方式 // 以下是一个默认构造函数 diff --git a/zh-cn/javascript-cn.html.markdown b/zh-cn/javascript-cn.html.markdown index 360f7c65..45e30932 100644 --- a/zh-cn/javascript-cn.html.markdown +++ b/zh-cn/javascript-cn.html.markdown @@ -4,7 +4,7 @@ category: language name: javascript filename: javascript-zh.js contributors: - - ["Adam Brenecki", "http://adam.brenecki.id.au"] + - ["Leigh Brenecki", "https://leigh.net.au"] - ["Ariel Krakowski", "http://www.learneroo.com"] translators: - ["Chenbo Li", "http://binarythink.net"] @@ -17,8 +17,8 @@ Javascript 于 1995 年由网景公司的 Brendan Eich 发明。最初它作为 不过,Javascript 不仅用于网页浏览器,一个名为 Node.js 的项目提供了面向 Google Chrome V8 引擎的独立运行时环境,它正在变得越来越流行。 很欢迎来自您的反馈,您可以通过下列方式联系到我: -[@adambrenecki](https://twitter.com/adambrenecki), 或者 -[adam@brenecki.id.au](mailto:adam@brenecki.id.au). +[@ExcitedLeigh](https://twitter.com/ExcitedLeigh), 或者 +[l@leigh.net.au](mailto:l@leigh.net.au). ```js // 注释方式和C很像,这是单行注释 diff --git a/zh-cn/json-cn.html.markdown b/zh-cn/json-cn.html.markdown index 73d3eb57..f5842c07 100644 --- a/zh-cn/json-cn.html.markdown +++ b/zh-cn/json-cn.html.markdown @@ -8,24 +8,30 @@ filename: learnjson-cn.json lang: zh-cn --- -因为JSON是一个极其简单的数据交换格式,本教程最有可能成为有史以来最简单的 -Learn X in Y Minutes。 +JSON是一个极其简单的数据交换格式。按[json.org](https://json.org)说的,它对人类易读易写,对机器易解析易生成。 -纯正的JSON实际上没有注释,但是大多数解析器都 -接受C-风格(//, /\* \*/)的注释。为了兼容性,最好不要在其中写这样形式的注释。 +一段JSON可以是下文列出的类型的任意值,但实际一般按以下两种方式之一呈现: + +* 一个键值对的集合(`{ }`)。按不同语言,这可能被理解为对象/记录/结构体/字典/哈希表/有键列表/关联数组 +* 一个有序的值列表(`[ ]`)。按不同语言,这可能被理解为数组/向量/列表/序列 + +纯正的JSON实际上没有注释,但是大多数解析器都接受C-风格(//, /\* \*/)的注释。一些解析器还容许trailing comma,即最后一个数组元素或最后一个对象属性之后的逗号。不过为了兼容性最好避免。 因此,本教程的一切都会是100%有效的JSON。幸亏,它的表达能力很丰富。 支持的数据类型: -- 字符串: "hello", "\"A quote.\"", "\u0abe", "Newline.\n" -- 数字: 23, 0.11, 12e10, 3.141e-10, 1.23e+4 -- 对象: { "key": "value" } -- 数组: ["Values"] -- 其他: true, false, null +* 字符串:`"hello"`、`"\"A quote.\""`、`"\u0abe"`、`"Newline.\n"` +* 数字:`23`、`0.11`、`12e10`、`3.141e-10`、`1.23e+4` +* 对象:`{ "key": "value" }` +* 数组:`["Values"]` +* 其它:`true`、`false`、`null` ```json { + "key": "value", + + "keys": "must always be enclosed in double quotes", "numbers": 0, "strings": "Hellø, wørld. All unicode is allowed, along with \"escaping\".", "has bools?": true, @@ -55,6 +61,23 @@ Learn X in Y Minutes。 ] ], - "that was short": "And, you're done. You now know everything JSON has to offer." + "alternative style": { + "comment": "check this out!" + , "comma position": "doesn't matter, if it's before the next key, it's valid" + , "another comment": "how nice" + }, + + + + "whitespace": "Does not matter.", + + + + "that was short": "And done. You now know everything JSON has to offer." } ``` + +## 进一步阅读 + +* [JSON.org](https://www.json.org/json-zh.html) 完美图解JSON的一切 +* [JSON Tutorial](https://www.youtube.com/watch?v=wI1CWzNtE-M) 简要介绍 diff --git a/zh-cn/lua-cn.html.markdown b/zh-cn/lua-cn.html.markdown index 6736dc2a..6fd10a32 100644 --- a/zh-cn/lua-cn.html.markdown +++ b/zh-cn/lua-cn.html.markdown @@ -391,7 +391,8 @@ dofile('mod2') --> Hi! (再次运行,与require不同) f = loadfile('mod2') -- Calling f() runs mod2.lua. -- loadstring是loadfile的字符串版本。 -g = loadstring('print(343)') --返回一个函数。 +-- (loadstring已弃用, 使用load代替) +g = load('print(343)') --返回一个函数。 g() -- 打印343; 在此之前什么也不打印。 --]] diff --git a/zh-cn/make-cn.html.markdown b/zh-cn/make-cn.html.markdown index 76dde941..641714ef 100644 --- a/zh-cn/make-cn.html.markdown +++ b/zh-cn/make-cn.html.markdown @@ -23,7 +23,7 @@ Makefile 用于定义如何创建目标文件, 比如如何从源码到可执行 ```make # 这行表示注释 -# 文件名一定要交 Makefile, 大小写区分, 使用 `make <target>` 生成 target +# 文件名一定要叫 Makefile, 大小写区分, 使用 `make <target>` 生成 target # 如果想要取别的名字, 可以用 `make -f "filename" <target>`. # 重要的事情 - 只认识 TAB, 空格是不认的, 但是在 GNU Make 3.82 之后, 可以通过 @@ -87,7 +87,7 @@ ex0.txt ex1.txt: maker maker: touch ex0.txt ex1.txt -# 如果定义的 phony target 与文件名重名, 可以用 .PHONY 显示的指明哪些 targets 是 phony +# 如果定义的 phony target 与文件名重名, 可以用 .PHONY 显式地指明哪些 targets 是 phony .PHONY: all maker process # This is a special target. There are several others. @@ -116,7 +116,7 @@ process: ex1.txt file0.txt # 模式匹配 #----------------------------------------------------------------------- -# 可以让 make 知道如何转换某些文件到别格式 +# 可以让 make 知道如何转换某些文件到其他格式 # 比如 从 svg 到 png %.png: %.svg inkscape --export-png $^ @@ -149,7 +149,7 @@ echo: @echo $(name) @echo ${name2} @echo $name # 这个会被蠢蠢的解析成 $(n)ame. - @echo \"$(name3)\" # 为声明的变量或扩展成空字符串. + @echo \"$(name3)\" # 未声明的变量会被处理成空字符串. @echo $(name4) @echo $(name5) # 你可以通过4种方式设置变量. diff --git a/zh-cn/markdown-cn.html.markdown b/zh-cn/markdown-cn.html.markdown index 9b3d96ab..23f27dda 100644 --- a/zh-cn/markdown-cn.html.markdown +++ b/zh-cn/markdown-cn.html.markdown @@ -147,7 +147,6 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以 - 项目 - 项目 - 最后一个项目 - ``` 有序序列可由数字加上点 `.` 来实现 @@ -188,6 +187,7 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以 下面这个选择框将会是选中状态 - [x] 这个任务已经完成 ``` + - [ ] 你看完了这个任务(注:此选择框是无法直接更改的,即禁用状态。) ## 代码块 @@ -204,24 +204,25 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以 ```md my_array.each do |item| - puts item + puts item end ``` 内联代码可由反引号 ` 实现 ```md -John 甚至不知道 `go_to()` 方程是干嘛的! +John 甚至不知道 `go_to()` 函数是干嘛的! ``` 在GitHub的 Markdown(GitHub Flavored Markdown)解析器中,你可以使用特殊的语法表示代码块 -<pre> -<code class="highlight">```ruby +````md +```ruby def foobar - puts "Hello world!" + puts "Hello world!" end -```</code></pre> +``` +```` 以上代码不需要缩进,而且 GitHub 会根据\`\`\`后指定的语言来进行语法高亮显示 @@ -246,7 +247,6 @@ Markdown 最棒的地方就是便捷的书写链接。把链接文字放在中 ```md [点我点我!](http://test.com/) - ``` 你也可以在小括号内使用引号,为链接加上一个标题(title) @@ -345,6 +345,7 @@ Markdown同样支持引用形式的链接 | 我是左对齐 | 居个中 | 右对齐 | | 注意 | 冒 | 号 | ``` + 好吧,强行对齐字符是很难的。但是,至少比下面这种写法好一点—— ```md @@ -352,6 +353,7 @@ Markdown同样支持引用形式的链接 :-- | :-: | --: 这真的太丑了 | 药不能 | 停!!!! ``` + 真的是*看着令人头晕* diff --git a/zh-cn/matlab-cn.html.markdown b/zh-cn/matlab-cn.html.markdown index d215755c..bb1ab79a 100644 --- a/zh-cn/matlab-cn.html.markdown +++ b/zh-cn/matlab-cn.html.markdown @@ -1,504 +1,504 @@ ----
-language: Matlab
-filename: matlab-cn.m
-contributors:
- - ["mendozao", "http://github.com/mendozao"]
- - ["jamesscottbrown", "http://jamesscottbrown.com"]
-translators:
- - ["sunxb10", "https://github.com/sunxb10"]
-lang: zh-cn
-
----
-
-MATLAB 是 MATrix LABoratory(矩阵实验室)的缩写。
-它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
-
-如果您有任何需要反馈或交流的内容,请联系本教程作者:
-[@the_ozzinator](https://twitter.com/the_ozzinator)
-或 [osvaldo.t.mendoza@gmail.com](mailto:osvaldo.t.mendoza@gmail.com)。
-
-```matlab
-% 以百分号作为注释符
-
-%{
-多行注释
-可以
-这样
-表示
-%}
-
-% 指令可以随意跨行,但需要在跨行处用 '...' 标明:
- a = 1 + 2 + ...
- + 4
-
-% 可以在MATLAB中直接向操作系统发出指令
-!ping google.com
-
-who % 显示内存中的所有变量
-whos % 显示内存中的所有变量以及它们的类型
-clear % 清除内存中的所有变量
-clear('A') % 清除指定的变量
-openvar('A') % 在变量编辑器中编辑指定变量
-
-clc % 清除命令窗口中显示的所有指令
-diary % 将命令窗口中的内容写入本地文件
-ctrl-c % 终止当前计算
-
-edit('myfunction.m') % 在编辑器中打开指定函数或脚本
-type('myfunction.m') % 在命令窗口中打印指定函数或脚本的源码
-
-profile on % 打开 profile 代码分析工具
-profile off % 关闭 profile 代码分析工具
-profile viewer % 查看 profile 代码分析工具的分析结果
-
-help command % 在命令窗口中显示指定命令的帮助文档
-doc command % 在帮助窗口中显示指定命令的帮助文档
-lookfor command % 在所有 MATLAB 内置函数的头部注释块的第一行中搜索指定命令
-lookfor command -all % 在所有 MATLAB 内置函数的整个头部注释块中搜索指定命令
-
-
-% 输出格式
-format short % 浮点数保留 4 位小数
-format long % 浮点数保留 15 位小数
-format bank % 金融格式,浮点数只保留 2 位小数
-fprintf('text') % 在命令窗口中显示 "text"
-disp('text') % 在命令窗口中显示 "text"
-
-
-% 变量与表达式
-myVariable = 4 % 命令窗口中将新创建的变量
-myVariable = 4; % 加上分号可使命令窗口中不显示当前语句执行结果
-4 + 6 % ans = 10
-8 * myVariable % ans = 32
-2 ^ 3 % ans = 8
-a = 2; b = 3;
-c = exp(a)*sin(pi/2) % c = 7.3891
-
-
-% 调用函数有两种方式:
-% 标准函数语法:
-load('myFile.mat', 'y') % 参数放在括号内,以英文逗号分隔
-% 指令语法:
-load myFile.mat y % 不加括号,以空格分隔参数
-% 注意在指令语法中参数不需要加引号:在这种语法下,所有输入参数都只能是文本文字,
-% 不能是变量的具体值,同样也不能是输出变量
-[V,D] = eig(A); % 这条函数调用无法转换成等价的指令语法
-[~,D] = eig(A); % 如果结果中只需要 D 而不需要 V 则可以这样写
-
-
-
-% 逻辑运算
-1 > 5 % 假,ans = 0
-10 >= 10 % 真,ans = 1
-3 ~= 4 % 不等于 -> ans = 1
-3 == 3 % 等于 -> ans = 1
-3 > 1 && 4 > 1 % 与 -> ans = 1
-3 > 1 || 4 > 1 % 或 -> ans = 1
-~1 % 非 -> ans = 0
-
-% 逻辑运算可直接应用于矩阵,运算结果也是矩阵
-A > 5
-% 对矩阵中每个元素做逻辑运算,若为真,则在运算结果的矩阵中对应位置的元素就是 1
-A( A > 5 )
-% 如此返回的向量,其元素就是 A 矩阵中所有逻辑运算为真的元素
-
-% 字符串
-a = 'MyString'
-length(a) % ans = 8
-a(2) % ans = y
-[a,a] % ans = MyStringMyString
-b = '字符串' % MATLAB目前已经可以支持包括中文在内的多种文字
-length(b) % ans = 3
-b(2) % ans = 符
-[b,b] % ans = 字符串字符串
-
-
-% 元组(cell 数组)
-a = {'one', 'two', 'three'}
-a(1) % ans = 'one' - 返回一个元组
-a{1} % ans = one - 返回一个字符串
-
-
-% 结构体
-A.b = {'one','two'};
-A.c = [1 2];
-A.d.e = false;
-
-
-% 向量
-x = [4 32 53 7 1]
-x(2) % ans = 32,MATLAB中向量的下标索引从1开始,不是0
-x(2:3) % ans = 32 53
-x(2:end) % ans = 32 53 7 1
-
-x = [4; 32; 53; 7; 1] % 列向量
-
-x = [1:10] % x = 1 2 3 4 5 6 7 8 9 10
-
-
-% 矩阵
-A = [1 2 3; 4 5 6; 7 8 9]
-% 以分号分隔不同的行,以空格或逗号分隔同一行中的不同元素
-% A =
-
-% 1 2 3
-% 4 5 6
-% 7 8 9
-
-A(2,3) % ans = 6,A(row, column)
-A(6) % ans = 8
-% (隐式地将 A 的三列首尾相接组成一个列向量,然后取其下标为 6 的元素)
-
-
-A(2,3) = 42 % 将第 2 行第 3 列的元素设为 42
-% A =
-
-% 1 2 3
-% 4 5 42
-% 7 8 9
-
-A(2:3,2:3) % 取原矩阵中的一块作为新矩阵
-%ans =
-
-% 5 42
-% 8 9
-
-A(:,1) % 第 1 列的所有元素
-%ans =
-
-% 1
-% 4
-% 7
-
-A(1,:) % 第 1 行的所有元素
-%ans =
-
-% 1 2 3
-
-[A ; A] % 将两个矩阵上下相接构成新矩阵
-%ans =
-
-% 1 2 3
-% 4 5 42
-% 7 8 9
-% 1 2 3
-% 4 5 42
-% 7 8 9
-
-% 等价于
-vertcat(A, A);
-
-
-[A , A] % 将两个矩阵左右相接构成新矩阵
-
-%ans =
-
-% 1 2 3 1 2 3
-% 4 5 42 4 5 42
-% 7 8 9 7 8 9
-
-% 等价于
-horzcat(A, A);
-
-
-A(:, [3 1 2]) % 重新排布原矩阵的各列
-%ans =
-
-% 3 1 2
-% 42 4 5
-% 9 7 8
-
-size(A) % 返回矩阵的行数和列数,ans = 3 3
-
-A(1, :) =[] % 删除矩阵的第 1 行
-A(:, 1) =[] % 删除矩阵的第 1 列
-
-transpose(A) % 矩阵(非共轭)转置,等价于 A.' (注意!有个点)
-ctranspose(A) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复数),等价于 A'
-
-
-% 元素运算 vs. 矩阵运算
-% 单独运算符就是对矩阵整体进行矩阵运算
-% 在运算符加上英文句点就是对矩阵中的元素进行元素计算
-% 示例如下:
-A * B % 矩阵乘法,要求 A 的列数等于 B 的行数
-A .* B % 元素乘法,要求 A 和 B 形状一致,即两矩阵行列数完全一致
- % 元素乘法的结果是与 A 和 B 形状一致的矩阵
- % 其每个元素等于 A 对应位置的元素乘 B 对应位置的元素
-
-% 以下函数中,函数名以 m 结尾的执行矩阵运算,其余执行元素运算:
-exp(A) % 对矩阵中每个元素做指数运算
-expm(A) % 对矩阵整体做指数运算
-sqrt(A) % 对矩阵中每个元素做开方运算
-sqrtm(A) % 对矩阵整体做开方运算(即试图求出一个矩阵,该矩阵与自身的乘积等于 A 矩阵)
-
-
-% 绘图
-x = 0:0.1:2*pi; % 生成一向量,其元素从 0 开始,以 0.1 的间隔一直递增到 2*pi
- % 其中 pi 为圆周率
-y = sin(x);
-plot(x,y)
-xlabel('x axis')
-ylabel('y axis')
-title('Plot of y = sin(x)')
-axis([0 2*pi -1 1]) % x 轴范围是从 0 到 2*pi,y 轴范围是从 -1 到 1
-
-plot(x,y1,'-',x,y2,'--',x,y3,':') % 在同一张图中绘制多条曲线
-legend('Line 1 label', 'Line 2 label') % 为图片加注图例
-% 图例数量应当小于或等于实际绘制的曲线数目,从 plot 绘制的第一条曲线开始对应
-
-% 在同一张图上绘制多条曲线的另一种方法:
-% 使用 hold on,令系统保留前次绘图结果并在其上直接叠加新的曲线,
-% 如果没有 hold on,则每个 plot 都会首先清除之前的绘图结果再进行绘制。
-% 在 hold on 和 hold off 中可以放置任意多的 plot 指令,
-% 它们和 hold on 前最后一个 plot 指令的结果都将显示在同一张图中。
-plot(x, y1)
-hold on
-plot(x, y2)
-plot(x, y3)
-plot(x, y4)
-hold off
-
-loglog(x, y) % 对数—对数绘图
-semilogx(x, y) % 半对数(x 轴对数)绘图
-semilogy(x, y) % 半对数(y 轴对数)绘图
-
-fplot (@(x) x^2, [2,5]) % 绘制函数 x^2 在 [2, 5] 区间的曲线
-
-grid on % 在绘制的图中显示网格,使用 grid off 可取消网格显示
-axis square % 将当前坐标系设定为正方形(保证在图形显示上各轴等长)
-axis equal % 将当前坐标系设定为相等(保证在实际数值上各轴等长)
-
-scatter(x, y); % 散点图
-hist(x); % 直方图
-
-z = sin(x);
-plot3(x,y,z); % 绘制三维曲线
-
-pcolor(A) % 伪彩色图(热图)
-contour(A) % 等高线图
-mesh(A) % 网格曲面图
-
-h = figure % 创建新的图片对象并返回其句柄 h
-figure(h) % 将句柄 h 对应的图片作为当前图片
-close(h) % 关闭句柄 h 对应的图片
-close all % 关闭 MATLAB 中所用打开的图片
-close % 关闭当前图片
-
-shg % 显示图形窗口
-clf clear % 清除图形窗口中的图像,并重置图像属性
-
-% 图像属性可以通过图像句柄进行设定
-% 在创建图像时可以保存图像句柄以便于设置
-% 也可以用 gcf 函数返回当前图像的句柄
-h = plot(x, y); % 在创建图像时显式地保存图像句柄
-set(h, 'Color', 'r')
-% 颜色代码:
-% 'y' 黄色,'m' 洋红,'c' 青色
-% 'r' 红色,'g' 绿色,'b' 蓝色
-% 'w' 白色,'k' 黑色
-set(h, 'Color', [0.5, 0.5, 0.4])
-% 也可以使用 RGB 值指定颜色
-set(h, 'LineStyle', '--')
-% 线型代码:'--' 实线,'---' 虚线,':' 点线,'-.' 点划线,'none' 不划线
-get(h, 'LineStyle')
-% 获取当前句柄的线型
-
-
-% 用 gca 函数返回当前图像的坐标轴句柄
-set(gca, 'XDir', 'reverse'); % 令 x 轴反向
-
-% 用 subplot 指令创建平铺排列的多张子图
-subplot(2,3,1); % 选择 2 x 3 排列的子图中的第 1 张图
-plot(x1); title('First Plot') % 在选中的图中绘图
-subplot(2,3,2); % 选择 2 x 3 排列的子图中的第 2 张图
-plot(x2); title('Second Plot') % 在选中的图中绘图
-
-
-% 要调用函数或脚本,必须保证它们在你的当前工作目录中
-path % 显示当前工作目录
-addpath /path/to/dir % 将指定路径加入到当前工作目录中
-rmpath /path/to/dir % 将指定路径从当前工作目录中删除
-cd /path/to/move/into % 以制定路径作为当前工作目录
-
-
-% 变量可保存到 .mat 格式的本地文件
-save('myFileName.mat') % 保存当前工作空间中的所有变量
-load('myFileName.mat') % 将指定文件中的变量载入到当前工作空间
-
-
-% .m 脚本文件
-% 脚本文件是一个包含多条 MATLAB 指令的外部文件,以 .m 为后缀名
-% 使用脚本文件可以避免在命令窗口中重复输入冗长的指令
-
-
-% .m 函数文件
-% 与脚本文件类似,同样以 .m 作为后缀名
-% 但函数文件可以接受用户输入的参数并返回运算结果
-% 并且函数拥有自己的工作空间(变量域),不必担心变量名称冲突
-% 函数文件的名称应当与其所定义的函数的名称一致
-% 比如下面例子中函数文件就应命名为 double_input.m
-% 使用 'help double_input.m' 可返回函数定义中第一行注释信息
-function output = double_input(x)
- % double_input(x) 返回 x 的 2 倍
- output = 2*x;
-end
-double_input(6) % ans = 12
-
-
-% 同样还可以定义子函数和内嵌函数
-% 子函数与主函数放在同一个函数文件中,且只能被这个主函数调用
-% 内嵌函数放在另一个函数体内,可以直接访问被嵌套函数的各个变量
-
-
-% 使用匿名函数可以不必创建 .m 函数文件
-% 匿名函数适用于快速定义某函数以便传递给另一指令或函数(如绘图、积分、求根、求极值等)
-% 下面示例的匿名函数返回输入参数的平方根,可以使用句柄 sqr 进行调用:
-sqr = @(x) x.^2;
-sqr(10) % ans = 100
-doc function_handle % find out more
-
-
-% 接受用户输入
-a = input('Enter the value: ')
-
-
-% 从文件中读取数据
-fopen(filename)
-% 类似函数还有 xlsread(excel 文件)、importdata(CSV 文件)、imread(图像文件)
-
-
-% 输出
-disp(a) % 在命令窗口中打印变量 a 的值
-disp('Hello World') % 在命令窗口中打印字符串
-fprintf % 按照指定格式在命令窗口中打印内容
-
-% 条件语句(if 和 elseif 语句中的括号并非必需,但推荐加括号避免混淆)
-if (a > 15)
- disp('Greater than 15')
-elseif (a == 23)
- disp('a is 23')
-else
- disp('neither condition met')
-end
-
-% 循环语句
-% 注意:对向量或矩阵使用循环语句进行元素遍历的效率很低!!
-% 注意:只要有可能,就尽量使用向量或矩阵的整体运算取代逐元素循环遍历!!
-% MATLAB 在开发时对向量和矩阵运算做了专门优化,做向量和矩阵整体运算的效率高于循环语句
-for k = 1:5
- disp(k)
-end
-
-k = 0;
-while (k < 5)
- k = k + 1;
-end
-
-
-% 程序运行计时:'tic' 是计时开始,'toc' 是计时结束并打印结果
-tic
-A = rand(1000);
-A*A*A*A*A*A*A;
-toc
-
-
-% 链接 MySQL 数据库
-dbname = 'database_name';
-username = 'root';
-password = 'root';
-driver = 'com.mysql.jdbc.Driver';
-dburl = ['jdbc:mysql://localhost:8889/' dbname];
-javaclasspath('mysql-connector-java-5.1.xx-bin.jar'); % 此处 xx 代表具体版本号
-% 这里的 mysql-connector-java-5.1.xx-bin.jar 可从 http://dev.mysql.com/downloads/connector/j/ 下载
-conn = database(dbname, username, password, driver, dburl);
-sql = ['SELECT * from table_name where id = 22'] % SQL 语句
-a = fetch(conn, sql) % a 即包含所需数据
-
-
-% 常用数学函数
-sin(x)
-cos(x)
-tan(x)
-asin(x)
-acos(x)
-atan(x)
-exp(x)
-sqrt(x)
-log(x)
-log10(x)
-abs(x)
-min(x)
-max(x)
-ceil(x)
-floor(x)
-round(x)
-rem(x)
-rand % 均匀分布的伪随机浮点数
-randi % 均匀分布的伪随机整数
-randn % 正态分布的伪随机浮点数
-
-% 常用常数
-pi
-NaN
-inf
-
-% 求解矩阵方程(如果方程无解,则返回最小二乘近似解)
-% \ 操作符等价于 mldivide 函数,/ 操作符等价于 mrdivide 函数
-x=A\b % 求解 Ax=b,比先求逆再左乘 inv(A)*b 更加高效、准确
-x=b/A % 求解 xA=b
-
-inv(A) % 逆矩阵
-pinv(A) % 伪逆矩阵
-
-
-% 常用矩阵函数
-zeros(m, n) % m x n 阶矩阵,元素全为 0
-ones(m, n) % m x n 阶矩阵,元素全为 1
-diag(A) % 返回矩阵 A 的对角线元素
-diag(x) % 构造一个对角阵,对角线元素就是向量 x 的各元素
-eye(m, n) % m x n 阶单位矩阵
-linspace(x1, x2, n) % 返回介于 x1 和 x2 之间的 n 个等距节点
-inv(A) % 矩阵 A 的逆矩阵
-det(A) % 矩阵 A 的行列式
-eig(A) % 矩阵 A 的特征值和特征向量
-trace(A) % 矩阵 A 的迹(即对角线元素之和),等价于 sum(diag(A))
-isempty(A) % 测试 A 是否为空
-all(A) % 测试 A 中所有元素是否都非 0 或都为真(逻辑值)
-any(A) % 测试 A 中是否有元素非 0 或为真(逻辑值)
-isequal(A, B) % 测试 A 和 B是否相等
-numel(A) % 矩阵 A 的元素个数
-triu(x) % 返回 x 的上三角这部分
-tril(x) % 返回 x 的下三角这部分
-cross(A, B) % 返回 A 和 B 的叉积(矢量积、外积)
-dot(A, B) % 返回 A 和 B 的点积(数量积、内积),要求 A 和 B 必须等长
-transpose(A) % 矩阵(非共轭)转置,等价于 A.' (注意!有个点)
-fliplr(A) % 将一个矩阵左右翻转
-flipud(A) % 将一个矩阵上下翻转
-
-% 矩阵分解
-[L, U, P] = lu(A) % LU 分解:PA = LU,L 是下三角阵,U 是上三角阵,P 是置换阵
-[P, D] = eig(A) % 特征值分解:AP = PD
- % D 是由特征值构成的对角阵,P 的各列就是对应的特征向量
-[U, S, V] = svd(X) % 奇异值分解:XV = US
- % U 和 V 是酉矩阵,S 是由奇异值构成的半正定实数对角阵
-
-% 常用向量函数
-max % 最大值
-min % 最小值
-length % 元素个数
-sort % 按升序排列
-sum % 各元素之和
-prod % 各元素之积
-mode % 众数
-median % 中位数
-mean % 平均值
-std % 标准差
-perms(x) % x 元素的全排列
-
-```
-
-## 相关资料
-
-* 官方网页:[MATLAB - 技术计算语言 - MATLAB & Simulink](https://ww2.mathworks.cn/products/matlab.html)
-* 官方论坛:[MATLAB Answers - MATLAB Central](https://ww2.mathworks.cn/matlabcentral/answers/)
+--- +language: MATLAB +filename: matlab-cn.m +contributors: + - ["mendozao", "http://github.com/mendozao"] + - ["jamesscottbrown", "http://jamesscottbrown.com"] +translators: + - ["sunxb10", "https://github.com/sunxb10"] +lang: zh-cn + +--- + +MATLAB 是 MATrix LABoratory(矩阵实验室)的缩写。 +它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。 + +如果您有任何需要反馈或交流的内容,请联系本教程作者: +[@the_ozzinator](https://twitter.com/the_ozzinator) +或 [osvaldo.t.mendoza@gmail.com](mailto:osvaldo.t.mendoza@gmail.com)。 + +```matlab +% 以百分号作为注释符 + +%{ +多行注释 +可以 +这样 +表示 +%} + +% 指令可以随意跨行,但需要在跨行处用 '...' 标明: + a = 1 + 2 + ... + + 4 + +% 可以在MATLAB中直接向操作系统发出指令 +!ping google.com + +who % 显示内存中的所有变量 +whos % 显示内存中的所有变量以及它们的类型 +clear % 清除内存中的所有变量 +clear('A') % 清除指定的变量 +openvar('A') % 在变量编辑器中编辑指定变量 + +clc % 清除命令窗口中显示的所有指令 +diary % 将命令窗口中的内容写入本地文件 +ctrl-c % 终止当前计算 + +edit('myfunction.m') % 在编辑器中打开指定函数或脚本 +type('myfunction.m') % 在命令窗口中打印指定函数或脚本的源码 + +profile on % 打开 profile 代码分析工具 +profile off % 关闭 profile 代码分析工具 +profile viewer % 查看 profile 代码分析工具的分析结果 + +help command % 在命令窗口中显示指定命令的帮助文档 +doc command % 在帮助窗口中显示指定命令的帮助文档 +lookfor command % 在所有 MATLAB 内置函数的头部注释块的第一行中搜索指定命令 +lookfor command -all % 在所有 MATLAB 内置函数的整个头部注释块中搜索指定命令 + + +% 输出格式 +format short % 浮点数保留 4 位小数 +format long % 浮点数保留 15 位小数 +format bank % 金融格式,浮点数只保留 2 位小数 +fprintf('text') % 在命令窗口中显示 "text" +disp('text') % 在命令窗口中显示 "text" + + +% 变量与表达式 +myVariable = 4 % 命令窗口中将新创建的变量 +myVariable = 4; % 加上分号可使命令窗口中不显示当前语句执行结果 +4 + 6 % ans = 10 +8 * myVariable % ans = 32 +2 ^ 3 % ans = 8 +a = 2; b = 3; +c = exp(a)*sin(pi/2) % c = 7.3891 + + +% 调用函数有两种方式: +% 标准函数语法: +load('myFile.mat', 'y') % 参数放在括号内,以英文逗号分隔 +% 指令语法: +load myFile.mat y % 不加括号,以空格分隔参数 +% 注意在指令语法中参数不需要加引号:在这种语法下,所有输入参数都只能是文本文字, +% 不能是变量的具体值,同样也不能是输出变量 +[V,D] = eig(A); % 这条函数调用无法转换成等价的指令语法 +[~,D] = eig(A); % 如果结果中只需要 D 而不需要 V 则可以这样写 + + + +% 逻辑运算 +1 > 5 % 假,ans = 0 +10 >= 10 % 真,ans = 1 +3 ~= 4 % 不等于 -> ans = 1 +3 == 3 % 等于 -> ans = 1 +3 > 1 && 4 > 1 % 与 -> ans = 1 +3 > 1 || 4 > 1 % 或 -> ans = 1 +~1 % 非 -> ans = 0 + +% 逻辑运算可直接应用于矩阵,运算结果也是矩阵 +A > 5 +% 对矩阵中每个元素做逻辑运算,若为真,则在运算结果的矩阵中对应位置的元素就是 1 +A( A > 5 ) +% 如此返回的向量,其元素就是 A 矩阵中所有逻辑运算为真的元素 + +% 字符串 +a = 'MyString' +length(a) % ans = 8 +a(2) % ans = y +[a,a] % ans = MyStringMyString +b = '字符串' % MATLAB目前已经可以支持包括中文在内的多种文字 +length(b) % ans = 3 +b(2) % ans = 符 +[b,b] % ans = 字符串字符串 + + +% 元组(cell 数组) +a = {'one', 'two', 'three'} +a(1) % ans = 'one' - 返回一个元组 +a{1} % ans = one - 返回一个字符串 + + +% 结构体 +A.b = {'one','two'}; +A.c = [1 2]; +A.d.e = false; + + +% 向量 +x = [4 32 53 7 1] +x(2) % ans = 32,MATLAB中向量的下标索引从1开始,不是0 +x(2:3) % ans = 32 53 +x(2:end) % ans = 32 53 7 1 + +x = [4; 32; 53; 7; 1] % 列向量 + +x = [1:10] % x = 1 2 3 4 5 6 7 8 9 10 + + +% 矩阵 +A = [1 2 3; 4 5 6; 7 8 9] +% 以分号分隔不同的行,以空格或逗号分隔同一行中的不同元素 +% A = + +% 1 2 3 +% 4 5 6 +% 7 8 9 + +A(2,3) % ans = 6,A(row, column) +A(6) % ans = 8 +% (隐式地将 A 的三列首尾相接组成一个列向量,然后取其下标为 6 的元素) + + +A(2,3) = 42 % 将第 2 行第 3 列的元素设为 42 +% A = + +% 1 2 3 +% 4 5 42 +% 7 8 9 + +A(2:3,2:3) % 取原矩阵中的一块作为新矩阵 +%ans = + +% 5 42 +% 8 9 + +A(:,1) % 第 1 列的所有元素 +%ans = + +% 1 +% 4 +% 7 + +A(1,:) % 第 1 行的所有元素 +%ans = + +% 1 2 3 + +[A ; A] % 将两个矩阵上下相接构成新矩阵 +%ans = + +% 1 2 3 +% 4 5 42 +% 7 8 9 +% 1 2 3 +% 4 5 42 +% 7 8 9 + +% 等价于 +vertcat(A, A); + + +[A , A] % 将两个矩阵左右相接构成新矩阵 + +%ans = + +% 1 2 3 1 2 3 +% 4 5 42 4 5 42 +% 7 8 9 7 8 9 + +% 等价于 +horzcat(A, A); + + +A(:, [3 1 2]) % 重新排布原矩阵的各列 +%ans = + +% 3 1 2 +% 42 4 5 +% 9 7 8 + +size(A) % 返回矩阵的行数和列数,ans = 3 3 + +A(1, :) =[] % 删除矩阵的第 1 行 +A(:, 1) =[] % 删除矩阵的第 1 列 + +transpose(A) % 矩阵(非共轭)转置,等价于 A.' (注意!有个点) +ctranspose(A) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复数),等价于 A' + + +% 元素运算 vs. 矩阵运算 +% 单独运算符就是对矩阵整体进行矩阵运算 +% 在运算符加上英文句点就是对矩阵中的元素进行元素计算 +% 示例如下: +A * B % 矩阵乘法,要求 A 的列数等于 B 的行数 +A .* B % 元素乘法,要求 A 和 B 形状一致,即两矩阵行列数完全一致 + % 元素乘法的结果是与 A 和 B 形状一致的矩阵 + % 其每个元素等于 A 对应位置的元素乘 B 对应位置的元素 + +% 以下函数中,函数名以 m 结尾的执行矩阵运算,其余执行元素运算: +exp(A) % 对矩阵中每个元素做指数运算 +expm(A) % 对矩阵整体做指数运算 +sqrt(A) % 对矩阵中每个元素做开方运算 +sqrtm(A) % 对矩阵整体做开方运算(即试图求出一个矩阵,该矩阵与自身的乘积等于 A 矩阵) + + +% 绘图 +x = 0:0.1:2*pi; % 生成一向量,其元素从 0 开始,以 0.1 的间隔一直递增到 2*pi + % 其中 pi 为圆周率 +y = sin(x); +plot(x,y) +xlabel('x axis') +ylabel('y axis') +title('Plot of y = sin(x)') +axis([0 2*pi -1 1]) % x 轴范围是从 0 到 2*pi,y 轴范围是从 -1 到 1 + +plot(x,y1,'-',x,y2,'--',x,y3,':') % 在同一张图中绘制多条曲线 +legend('Line 1 label', 'Line 2 label') % 为图片加注图例 +% 图例数量应当小于或等于实际绘制的曲线数目,从 plot 绘制的第一条曲线开始对应 + +% 在同一张图上绘制多条曲线的另一种方法: +% 使用 hold on,令系统保留前次绘图结果并在其上直接叠加新的曲线, +% 如果没有 hold on,则每个 plot 都会首先清除之前的绘图结果再进行绘制。 +% 在 hold on 和 hold off 中可以放置任意多的 plot 指令, +% 它们和 hold on 前最后一个 plot 指令的结果都将显示在同一张图中。 +plot(x, y1) +hold on +plot(x, y2) +plot(x, y3) +plot(x, y4) +hold off + +loglog(x, y) % 对数—对数绘图 +semilogx(x, y) % 半对数(x 轴对数)绘图 +semilogy(x, y) % 半对数(y 轴对数)绘图 + +fplot (@(x) x^2, [2,5]) % 绘制函数 x^2 在 [2, 5] 区间的曲线 + +grid on % 在绘制的图中显示网格,使用 grid off 可取消网格显示 +axis square % 将当前坐标系设定为正方形(保证在图形显示上各轴等长) +axis equal % 将当前坐标系设定为相等(保证在实际数值上各轴等长) + +scatter(x, y); % 散点图 +hist(x); % 直方图 + +z = sin(x); +plot3(x,y,z); % 绘制三维曲线 + +pcolor(A) % 伪彩色图(热图) +contour(A) % 等高线图 +mesh(A) % 网格曲面图 + +h = figure % 创建新的图片对象并返回其句柄 h +figure(h) % 将句柄 h 对应的图片作为当前图片 +close(h) % 关闭句柄 h 对应的图片 +close all % 关闭 MATLAB 中所用打开的图片 +close % 关闭当前图片 + +shg % 显示图形窗口 +clf clear % 清除图形窗口中的图像,并重置图像属性 + +% 图像属性可以通过图像句柄进行设定 +% 在创建图像时可以保存图像句柄以便于设置 +% 也可以用 gcf 函数返回当前图像的句柄 +h = plot(x, y); % 在创建图像时显式地保存图像句柄 +set(h, 'Color', 'r') +% 颜色代码: +% 'y' 黄色,'m' 洋红,'c' 青色 +% 'r' 红色,'g' 绿色,'b' 蓝色 +% 'w' 白色,'k' 黑色 +set(h, 'Color', [0.5, 0.5, 0.4]) +% 也可以使用 RGB 值指定颜色 +set(h, 'LineStyle', '--') +% 线型代码:'--' 实线,'---' 虚线,':' 点线,'-.' 点划线,'none' 不划线 +get(h, 'LineStyle') +% 获取当前句柄的线型 + + +% 用 gca 函数返回当前图像的坐标轴句柄 +set(gca, 'XDir', 'reverse'); % 令 x 轴反向 + +% 用 subplot 指令创建平铺排列的多张子图 +subplot(2,3,1); % 选择 2 x 3 排列的子图中的第 1 张图 +plot(x1); title('First Plot') % 在选中的图中绘图 +subplot(2,3,2); % 选择 2 x 3 排列的子图中的第 2 张图 +plot(x2); title('Second Plot') % 在选中的图中绘图 + + +% 要调用函数或脚本,必须保证它们在你的当前工作目录中 +path % 显示当前工作目录 +addpath /path/to/dir % 将指定路径加入到当前工作目录中 +rmpath /path/to/dir % 将指定路径从当前工作目录中删除 +cd /path/to/move/into % 以制定路径作为当前工作目录 + + +% 变量可保存到 .mat 格式的本地文件 +save('myFileName.mat') % 保存当前工作空间中的所有变量 +load('myFileName.mat') % 将指定文件中的变量载入到当前工作空间 + + +% .m 脚本文件 +% 脚本文件是一个包含多条 MATLAB 指令的外部文件,以 .m 为后缀名 +% 使用脚本文件可以避免在命令窗口中重复输入冗长的指令 + + +% .m 函数文件 +% 与脚本文件类似,同样以 .m 作为后缀名 +% 但函数文件可以接受用户输入的参数并返回运算结果 +% 并且函数拥有自己的工作空间(变量域),不必担心变量名称冲突 +% 函数文件的名称应当与其所定义的函数的名称一致 +% 比如下面例子中函数文件就应命名为 double_input.m +% 使用 'help double_input.m' 可返回函数定义中第一行注释信息 +function output = double_input(x) + % double_input(x) 返回 x 的 2 倍 + output = 2*x; +end +double_input(6) % ans = 12 + + +% 同样还可以定义子函数和内嵌函数 +% 子函数与主函数放在同一个函数文件中,且只能被这个主函数调用 +% 内嵌函数放在另一个函数体内,可以直接访问被嵌套函数的各个变量 + + +% 使用匿名函数可以不必创建 .m 函数文件 +% 匿名函数适用于快速定义某函数以便传递给另一指令或函数(如绘图、积分、求根、求极值等) +% 下面示例的匿名函数返回输入参数的平方根,可以使用句柄 sqr 进行调用: +sqr = @(x) x.^2; +sqr(10) % ans = 100 +doc function_handle % find out more + + +% 接受用户输入 +a = input('Enter the value: ') + + +% 从文件中读取数据 +fopen(filename) +% 类似函数还有 xlsread(excel 文件)、importdata(CSV 文件)、imread(图像文件) + + +% 输出 +disp(a) % 在命令窗口中打印变量 a 的值 +disp('Hello World') % 在命令窗口中打印字符串 +fprintf % 按照指定格式在命令窗口中打印内容 + +% 条件语句(if 和 elseif 语句中的括号并非必需,但推荐加括号避免混淆) +if (a > 15) + disp('Greater than 15') +elseif (a == 23) + disp('a is 23') +else + disp('neither condition met') +end + +% 循环语句 +% 注意:对向量或矩阵使用循环语句进行元素遍历的效率很低!! +% 注意:只要有可能,就尽量使用向量或矩阵的整体运算取代逐元素循环遍历!! +% MATLAB 在开发时对向量和矩阵运算做了专门优化,做向量和矩阵整体运算的效率高于循环语句 +for k = 1:5 + disp(k) +end + +k = 0; +while (k < 5) + k = k + 1; +end + + +% 程序运行计时:'tic' 是计时开始,'toc' 是计时结束并打印结果 +tic +A = rand(1000); +A*A*A*A*A*A*A; +toc + + +% 链接 MySQL 数据库 +dbname = 'database_name'; +username = 'root'; +password = 'root'; +driver = 'com.mysql.jdbc.Driver'; +dburl = ['jdbc:mysql://localhost:8889/' dbname]; +javaclasspath('mysql-connector-java-5.1.xx-bin.jar'); % 此处 xx 代表具体版本号 +% 这里的 mysql-connector-java-5.1.xx-bin.jar 可从 http://dev.mysql.com/downloads/connector/j/ 下载 +conn = database(dbname, username, password, driver, dburl); +sql = ['SELECT * from table_name where id = 22'] % SQL 语句 +a = fetch(conn, sql) % a 即包含所需数据 + + +% 常用数学函数 +sin(x) +cos(x) +tan(x) +asin(x) +acos(x) +atan(x) +exp(x) +sqrt(x) +log(x) +log10(x) +abs(x) +min(x) +max(x) +ceil(x) +floor(x) +round(x) +rem(x) +rand % 均匀分布的伪随机浮点数 +randi % 均匀分布的伪随机整数 +randn % 正态分布的伪随机浮点数 + +% 常用常数 +pi +NaN +inf + +% 求解矩阵方程(如果方程无解,则返回最小二乘近似解) +% \ 操作符等价于 mldivide 函数,/ 操作符等价于 mrdivide 函数 +x=A\b % 求解 Ax=b,比先求逆再左乘 inv(A)*b 更加高效、准确 +x=b/A % 求解 xA=b + +inv(A) % 逆矩阵 +pinv(A) % 伪逆矩阵 + + +% 常用矩阵函数 +zeros(m, n) % m x n 阶矩阵,元素全为 0 +ones(m, n) % m x n 阶矩阵,元素全为 1 +diag(A) % 返回矩阵 A 的对角线元素 +diag(x) % 构造一个对角阵,对角线元素就是向量 x 的各元素 +eye(m, n) % m x n 阶单位矩阵 +linspace(x1, x2, n) % 返回介于 x1 和 x2 之间的 n 个等距节点 +inv(A) % 矩阵 A 的逆矩阵 +det(A) % 矩阵 A 的行列式 +eig(A) % 矩阵 A 的特征值和特征向量 +trace(A) % 矩阵 A 的迹(即对角线元素之和),等价于 sum(diag(A)) +isempty(A) % 测试 A 是否为空 +all(A) % 测试 A 中所有元素是否都非 0 或都为真(逻辑值) +any(A) % 测试 A 中是否有元素非 0 或为真(逻辑值) +isequal(A, B) % 测试 A 和 B是否相等 +numel(A) % 矩阵 A 的元素个数 +triu(x) % 返回 x 的上三角这部分 +tril(x) % 返回 x 的下三角这部分 +cross(A, B) % 返回 A 和 B 的叉积(矢量积、外积) +dot(A, B) % 返回 A 和 B 的点积(数量积、内积),要求 A 和 B 必须等长 +transpose(A) % 矩阵(非共轭)转置,等价于 A.' (注意!有个点) +fliplr(A) % 将一个矩阵左右翻转 +flipud(A) % 将一个矩阵上下翻转 + +% 矩阵分解 +[L, U, P] = lu(A) % LU 分解:PA = LU,L 是下三角阵,U 是上三角阵,P 是置换阵 +[P, D] = eig(A) % 特征值分解:AP = PD + % D 是由特征值构成的对角阵,P 的各列就是对应的特征向量 +[U, S, V] = svd(X) % 奇异值分解:XV = US + % U 和 V 是酉矩阵,S 是由奇异值构成的半正定实数对角阵 + +% 常用向量函数 +max % 最大值 +min % 最小值 +length % 元素个数 +sort % 按升序排列 +sum % 各元素之和 +prod % 各元素之积 +mode % 众数 +median % 中位数 +mean % 平均值 +std % 标准差 +perms(x) % x 元素的全排列 + +``` + +## 相关资料 + +* 官方网页:[MATLAB - 技术计算语言 - MATLAB & Simulink](https://ww2.mathworks.cn/products/matlab.html) +* 官方论坛:[MATLAB Answers - MATLAB Central](https://ww2.mathworks.cn/matlabcentral/answers/) diff --git a/zh-cn/mips-cn.html.markdown b/zh-cn/mips-cn.html.markdown new file mode 100644 index 00000000..4f9ea7da --- /dev/null +++ b/zh-cn/mips-cn.html.markdown @@ -0,0 +1,334 @@ +--- +language: "MIPS Assembly" +filename: MIPS-cn.asm +contributors: + - ["Stanley Lim", "https://github.com/Spiderpig86"] +translators: + - ["Liu Yihua", "https://github.com/yihuajack"] +lang: zh-cn +--- + +MIPS(Microprocessor without Interlocked Pipeline Stages)汇编语言是为了配合约翰·雷洛伊·亨尼西于1981年设计的 MIPS 微处理器范式而设计的,这些 RISC 处理器用于嵌入式系统,例如网关和路由器。 + +[阅读更多](https://en.wikipedia.org/wiki/MIPS_architecture) + +```asm +# 注释用一个 '#' 表示 + +# 一行中 '#' 之后的所有文本都会被汇编器的词法分析器忽略 + +# 程序通常包含 .data 和 .text 部分 + +.data # 数据存储在内存中(在RAM中分配) + # 类似于高级语言中的变量 + + # 声明遵循( 标签: .类型 值 )的声明形式 + hello_world: .asciiz "Hello World\n" # 声明一个 null 结束的字符串 + num1: .word 42 # 整数被视为字 + # (32位值) + + arr1: .word 1, 2, 3, 4, 5 # 字数组 + arr2: .byte 'a', 'b' # 字符数组(每个1字节) + buffer: .space 60 # 在 RAM 中分配空间 + # (不清除为0) + + # 数据类型的大小 + _byte: .byte 'a' # 1字节 + _halfword: .half 53 # 2字节 + _word: .word 3 # 4字节 + _float: .float 3.14 # 4字节 + _double: .double 7.0 # 8字节 + + .align 2 # 数据的内存对齐 + # 其中数字(应是2的幂)表示几字节对齐 + # .align 2 表示字对齐(因为 2^2 = 4 字节) + +.text # 这部分包括指令和程序逻辑 +.globl _main # 声明一个全局指令标签 + # 其他文件都可以访问 + + _main: # MIPS 程序按顺序执行指令 + # 这条标签下的代码将首先执行 + + # 打印 "hello world" + la $a0, hello_world # 加载存储在内存中的字符串地址 + li $v0, 4 # 加载 syscall 的值 + # (数字代表要执行哪个 syscall) + syscall # 使用给定的参数($a0)执行指定的 syscall + + # 寄存器(用于在程序执行期间保存数据) + # $t0 - $t9 # 临时寄存器,用于过程内部的中间计算 + # (过程调用时不保存) + + # $s0 - $s7 # 保留寄存器(被保留的寄存器,过程调用时保存) + # 通常保存在栈中 + + # $a0 - $a3 # 参数寄存器,用于传递过程的参数 + # $v0 - $v1 # 返回寄存器,用于向调用过程返回值 + + # 存取指令 + la $t0, label # 将内存中由 label 指定的值的地址复制到寄存器 $t0 中 + lw $t0, label # 从内存中复制一个字 + lw $t1, 4($s0) # 从寄存器中存储的地址复制一个字 + # 偏移量为4字节(地址 + 4) + lb $t2, label # 把一个字节复制到寄存器 $t2 的低阶部分 + lb $t2, 0($s0) # 从 $s0 的源地址复制一个字节 + # 偏移量为0 + # 同理也适用于 'lh' (取半字) + + sw $t0, label # 将字存储到由 label 映射的内存地址中 + sw $t0, 8($s0) # 将字存储到 $s0 指定的地址中 + # 偏移量为8字节 + # 同理也适用于 'sb' (存字)和 'sh' (存半字)。'sa'不存在 + +### 数学 ### + _math: + # 记住要将值加载到寄存器中 + lw $t0, num # 从数据部分 + li $t0, 5 # 或者从一个立即数(常数) + li $t1, 6 + add $t2, $t0, $t1 # $t2 = $t0 + $t1 + sub $t2, $t0, $t1 # $t2 = $t0 - $t1 + mul $t2, $t0, $t1 # $t2 = $t0 * $t1 + div $t2, $t0, $t1 # $t2 = $t0 / $t1 + # (MARS 的某些版本可能不支持) + div $t0, $t1 # 执行 $t0 / $t1。 + # 用 'mflo' 得商,用 'mfhi' 得余数 + + # 移位 + sll $t0, $t0, 2 # 按位左移立即数(常数值)2 + sllv $t0, $t1, $t2 # 根据一个寄存器中的变量值左移相应位 + srl $t0, $t0, 5 # 按位右移 + # (不保留符号,用0符号扩展) + srlv $t0, $t1, $t2 # 根据一个寄存器中的变量值右移相应位 + sra $t0, $t0, 7 # 按算术位右移(保留符号) + srav $t0, $t1, $t2 # 根据一个寄存器中的变量值右移相应算数位 + + # 按位运算符 + and $t0, $t1, $t2 # 按位与 + andi $t0, $t1, 0xFFF # 用立即数按位与 + or $t0, $t1, $t2 # 按位或 + ori $t0, $t1, 0xFFF # 用立即数按位或 + xor $t0, $t1, $t2 # 按位异或 + xori $t0, $t1, 0xFFF # 用立即数按位异或 + nor $t0, $t1, $t2 # 按位或非 + +## 分支 ## + _branching: + # 分支指令的基本格式通常遵循 <指令> <寄存器1> <寄存器2> <标签> + # 如果给定的条件求值为真,则跳转到标签 + # 有时向后编写条件逻辑更容易,如下面的简单的 if 语句示例所示 + + beq $t0, $t1, reg_eq # 如果 $t0 == $t1,则跳转到 reg_eq + # 否则执行下一行 + bne $t0, $t1, reg_neq # 当 $t0 != $t1 时跳转 + b branch_target # 非条件分支,总会执行 + beqz $t0, req_eq_zero # 当 $t0 == 0 时跳转 + bnez $t0, req_neq_zero # 当 $t0 != 0 时跳转 + bgt $t0, $t1, t0_gt_t1 # 当 $t0 > $t1 时跳转 + bge $t0, $t1, t0_gte_t1 # 当 $t0 >= $t1 时跳转 + bgtz $t0, t0_gt0 # 当 $t0 > 0 时跳转 + blt $t0, $t1, t0_gt_t1 # 当 $t0 < $t1 时跳转 + ble $t0, $t1, t0_gte_t1 # 当 $t0 <= $t1 时跳转 + bltz $t0, t0_lt0 # 当 $t0 < 0 时跳转 + slt $s0, $t0, $t1 # 当 $t0 < $t1 时结果为 $s0 (1为真) + + # 简单的 if 语句 + # if (i == j) + # f = g + h; + # f = f - i; + + # 让 $s0 = f, $s1 = g, $s2 = h, $s3 = i, $s4 = j + bne $s3, $s4, L1 # if (i !=j) + add $s0, $s1, $s2 # f = g + h + + L1: + sub $s0, $s0, $s3 # f = f - i + + # 下面是一个求3个数的最大值的例子 + # 从 Java 到 MIPS 逻辑的直接翻译: + # if (a > b) + # if (a > c) + # max = a; + # else + # max = c; + # else + # max = b; + # else + # max = c; + + # 让 $s0 = a, $s1 = b, $s2 = c, $v0 = 返回寄存器 + ble $s0, $s1, a_LTE_b # 如果 (a <= b) 跳转到 (a_LTE_b) + ble $s0, $s2, max_C # 如果 (a > b && a <= c) 跳转到 (max_C) + move $v0, $s0 # 否则 [a > b && a > c] max = a + j done # 跳转到程序结束 + + a_LTE_b: # 当 a <= b 时的标签 + ble $s1, $s2, max_C # 如果 (a <= b && b <= c) 跳转到 (max_C) + move $v0, $s1 # 如果 (a <= b && b > c) max = b + j done # 跳转到 done + + max_C: + move $v0, $s2 # max = c + + done: # 程序结束 + +## 循环 ## + _loops: + # 循环的基本结构是一个退出条件和一个继续执行的跳转指令 + li $t0, 0 + while: + bgt $t0, 10, end_while # 当 $t0 小于 10,不停迭代 + addi $t0, $t0, 1 # 累加值 + j while # 跳转回循环开始 + end_while: + + # 二维矩阵遍历 + # 假设 $a0 存储整数 3 × 3 矩阵的地址 + li $t0, 0 # 计数器 i + li $t1, 0 # 计数器 j + matrix_row: + bgt $t0, 3, matrix_row_end + + matrix_col: + bgt $t1, 3, matrix_col_end + + # 执行一些东西 + + addi $t1, $t1, 1 # 累加列计数器 + matrix_col_end: + + # 执行一些东西 + + addi $t0, $t0, 1 + matrix_row_end: + +## 函数 ## + _functions: + # 函数是可调用的过程,可以接受参数并返回所有用标签表示的值,如前所示 + + main: # 程序以 main 函数开始 + jal return_1 # jal 会把当前程序计数器(PC)存储在 $ra + # 并跳转到 return_1 + + # 如果我们想传入参数呢? + # 首先,我们必须将形参传递给参数寄存器 + li $a0, 1 + li $a1, 2 + jal sum # 现在我们可以调用函数了 + + # 递归怎么样? + # 这需要更多的工作 + # 由于 jal 会自动覆盖每次调用,我们需要确保在 $ra 中保存并恢复之前的程序计数器 + li $a0, 3 + jal fact + + li $v0, 10 + syscall + + # 这个函数返回1 + return_1: + li $v0, 1 # 将值取到返回寄存器 $v0 中 + jr $ra # 跳转回原先的程序计数器继续执行 + + + # 有2个参数的函数 + sum: + add $v0, $a0, $a1 + jr $ra # 返回 + + # 求阶乘的递归函数 + fact: + addi $sp, $sp, -8 # 在栈中分配空间 + sw $s0, ($sp) # 存储保存当前数字的寄存器 + sw $ra, 4($sp) # 存储先前的程序计数器 + + li $v0, 1 # 初始化返回值 + beq $a0, 0, fact_done # 如果参数为0则完成 + + # 否则继续递归 + move $s0, $a0 # 复制 $a0 到 $s0 + sub $a0, $a0, 1 + jal fact + + mul $v0, $s0, $v0 # 做乘法 + + fact_done: + lw $s0, ($sp) + lw $ra, ($sp) # 恢复程序计数器 + addi $sp, $sp, 8 + + jr $ra + +## 宏 ## + _macros: + # 宏可以实现用单个标签替换重复的代码块,这可以增强程序的可读性 + # 它们绝不是函数的替代品 + # 它们必须在使用之前声明 + + # 用于打印换行符的宏(这可以被多次重用) + .macro println() + la $a0, newline # 存储在这里的新行字符串 + li $v0, 4 + syscall + .end_macro + + println() # 汇编器会在运行前复制此代码块 + + # 参数可以通过宏传入。 + # 它们由 '%' 符号表示,可以选择起任意名字 + .macro print_int(%num) + li $v0, 1 + lw $a0, %num + syscall + .end_macro + + li $t0, 1 + print_int($t0) + + # 我们也可以给宏传递立即数 + .macro immediates(%a, %b) + add $t0, %a, %b + .end_macro + + immediates(3, 5) + + # 以及标签 + .macro print(%string) + la $a0, %string + li $v0, 4 + syscall + .end_macro + + print(hello_world) + +## 数组 ## +.data + list: .word 3, 0, 1, 2, 6 # 这是一个字数组 + char_arr: .asciiz "hello" # 这是一个字符数组 + buffer: .space 128 # 在内存中分配块,不会自动清除 + # 这些内存块彼此对齐 + +.text + la $s0, list # 取 list 的地址 + li $t0, 0 # 计数器 + li $t1, 5 # list 的长度 + + loop: + bgt $t0, $t1, end_loop + + lw $a0, ($s0) + li $v0, 1 + syscall # 打印数字 + + addi $s0, $s0, 4 # 字的大小为4字节 + addi $t0, $t0, 1 # 累加 + j loop + end_loop: + +## INCLUDE ## +# 使用 include 语句可以将外部文件导入到程序中 +# (它只是将文件中的代码放入 include 语句的位置) +.include "somefile.asm" + +``` diff --git a/zh-cn/nim-cn.html.markdown b/zh-cn/nim-cn.html.markdown new file mode 100644 index 00000000..fa7d8259 --- /dev/null +++ b/zh-cn/nim-cn.html.markdown @@ -0,0 +1,283 @@ +--- +language: Nim +filename: learnNim-cn.nim +contributors: + - ["Jason J. Ayala P.", "http://JasonAyala.com"] + - ["Dennis Felsing", "https://dennis.felsing.org"] +translators: + - ["lzw-723", "https://github.com/lzw-723"] +lang: zh-cn +--- + +Nim(原名Nimrod)是一种静态类型的命令式编程语言, +它能在不影响运行时效率的情况下为程序员提供强大的功能。 + +Nim语言高效、有表现力、优雅。 + +```nim +# 单行注释以一个#开头 + +#[ + 这是多行注释 + 在Nim语言中,多行注释可以嵌套,以#[开头,以]#结尾 +]# + +discard """ +这也可以作为多行注释使用。 +或者用于无法解析、损坏的代码 +""" + +var # 声明(和赋值)变量 + letter: char = 'n' # 带或不带类型批注 + lang = "N" & "im" + nLength: int = len(lang) + boat: float + truth: bool = false + + let # 使用let*一次性*声明和绑定变量。 + legs = 400 # legs是不可改变的。 + arms = 2_000 # _会被忽略,对long类型非常有用。 + aboutPi = 3.15 + +const # 常量在编译时计算。这确保了 + debug = true # 性能,在编译时表达式中很有用。 + compileBadCode = false + +when compileBadCode: # `when`是编译时的`if` + legs = legs + 1 # 这个错误永远不会被编译。 + const input = readline(stdin) # const在编译时必须是已知的。 + +discard 1 > 2 # 注意:如果表达式的结果未使用, + # 编译器会发出警告。`discard`绕过了这一点。 + + +# +# 数据结构 +# + +# 元组(Tuple) + +var + child: tuple[name: string, age: int] # 元组有*字段名* + today: tuple[sun: string, temp: float] # 和*顺序* + + +child = (name: "Rudiger", age: 2) # 使用字面值()一次性赋值全部 +today.sun = "Overcast" # 也可以单独赋值 +today.temp = 70.1 + +# 序列(Sequence) + +var + drinks: seq[string] + +drinks = @["Water", "Juice", "Chocolate"] # @[V1,..,Vn] 是序列的字面值 + +drinks.add("Milk") + +if "Milk" in drinks: + echo "We have Milk and ", drinks.len - 1, " other drinks" + +let myDrink = drinks[2] + +# +# 自定义类型 +# + +# 定义你自己的类型使得编译器为你工作。 +# 这使得静态类型变得强大和有用。 + +type + Name = string # 类型别名为你提供一个新类型, + Age = int # 该类型可与旧类型互换,但更具描述性。 + Person = tuple[name: Name, age: Age] # 也可以定义数据结构。 + AnotherSyntax = tuple + fieldOne: string + secondField: int + +var + john: Person = (name: "John B.", age: 17) + newage: int = 18 # 在这里使用Age比int要好 + +john.age = newage # 仍然有效,因为int和Age同义 + +type + Cash = distinct int # `distinct`使一个新类型与它的基本类型不兼容。 + Desc = distinct string + +var + money: Cash = 100.Cash # `.Cash`把int转换成我们的类型 + description: Desc = "Interesting".Desc + +when compileBadCode: + john.age = money # 错误!age是int类型、money是Cash类型 + john.name = description # 编译器说:“没门!” + +# +# 更多类型和数据结构 +# + +# 枚举类型只能具有有限数量的值之一 + +type + Color = enum cRed, cBlue, cGreen + Direction = enum # 可选格式 + dNorth + dWest + dEast + dSouth +var + orient = dNorth # `orient`的类型是Direction,值是`dNorth` + pixel = cGreen # `pixel`的类型是Color,值是`cGreen` + +discard dNorth > dEast # Enum通常是“序数”类型 + +# 子范围指定有限的有效范围 + +type + DieFaces = range[1..20] # 只有从1到20的int才是有效值 +var + my_roll: DieFaces = 13 + +when compileBadCode: + my_roll = 23 # 错误! + +# 数组(Array) + +type + RollCounter = array[DieFaces, int] # 数组长度固定 + DirNames = array[Direction, string] # 以任意有序类型索引 + Truths = array[42..44, bool] +var + counter: RollCounter + directions: DirNames + possible: Truths + +possible = [false, false, false] # 数组字面以[V1,..,Vn]表示 +possible[42] = true + +directions[dNorth] = "Ahh. The Great White North!" +directions[dWest] = "No, don't go there." + +my_roll = 13 +counter[my_roll] += 1 +counter[my_roll] += 1 + +var anotherArray = ["Default index", "starts at", "0"] + +# 可用的数据结构包括表、集合、列表、队列、压缩前缀树。 +# http://nim-lang.org/docs/lib.html#collections-and-algorithms + +# +# IO和控制流 +# + +# `case`, `readLine()` + +echo "Read any good books lately?" +case readLine(stdin) +of "no", "No": + echo "Go to your local library." +of "yes", "Yes": + echo "Carry on, then." +else: + echo "That's great; I assume." + +# `while`, `if`, `continue`, `break` + +import strutils as str # http://nim-lang.org/docs/strutils.html +echo "I'm thinking of a number between 41 and 43. Guess which!" +let number: int = 42 +var + raw_guess: string + guess: int +while guess != number: + raw_guess = readLine(stdin) + if raw_guess == "": continue # 跳出循环 + guess = str.parseInt(raw_guess) + if guess == 1001: + echo("AAAAAAGGG!") + break + elif guess > number: + echo("Nope. Too high.") + elif guess < number: + echo(guess, " is too low") + else: + echo("Yeeeeeehaw!") + +# +# 循环(Iteration) +# + +for i, elem in ["Yes", "No", "Maybe so"]: # 也可以是`for elem in` + echo(elem, " is at index: ", i) + +for k, v in items(@[(person: "You", power: 100), (person: "Me", power: 9000)]): + echo v + +let myString = """ +an <example> +`string` to +play with +""" # 多行字符串 + +for line in splitLines(myString): + echo(line) + +for i, c in myString: # 索引和字符。或使用'for j in'只有字符 + if i mod 2 == 0: continue # 紧凑的'if'形式 + elif c == 'X': break + else: echo(c) + +# +# 过程(Procedure) +# + +type Answer = enum aYes, aNo + +proc ask(question: string): Answer = + echo(question, " (y/n)") + while true: + case readLine(stdin) + of "y", "Y", "yes", "Yes": + return Answer.aYes # 枚举类型可以 + of "n", "N", "no", "No": + return Answer.aNo + else: echo("Please be clear: yes or no") + +proc addSugar(amount: int = 2) = # amount默认是2,不返回任何值 + assert(amount > 0 and amount < 9000, "Crazy Sugar") + for a in 1..amount: + echo(a, " sugar...") + +case ask("Would you like sugar in your tea?") +of aYes: + addSugar(3) +of aNo: + echo "Oh do take a little!" + addSugar() +# 这里不需要使用`else` 。只能是`yes`和`no`。 + +# +# 外部函数接口(FFI) +# + +# 因为Nim可以编译为C,使用外部函数接口(FFI)很简单: + +proc strcmp(a, b: cstring): cint {.importc: "strcmp", nodecl.} + +let cmp = strcmp("C?", "Easy!") +``` + +除此以外,Nim通过元编程、性能和编译时特性将自己与其他同类分离开来。 + +## 进阶阅读 + +* [主页](http://nim-lang.org) +* [下载](http://nim-lang.org/download.html) +* [社区](http://nim-lang.org/community.html) +* [常见问题](http://nim-lang.org/question.html) +* [文档](http://nim-lang.org/documentation.html) +* [参考手册](http://nim-lang.org/docs/manual.html) +* [标准库](http://nim-lang.org/docs/lib.html) +* [Rosetta Code](http://rosettacode.org/wiki/Category:Nim) diff --git a/zh-cn/opencv-cn.html.markdown b/zh-cn/opencv-cn.html.markdown new file mode 100644 index 00000000..06b997d5 --- /dev/null +++ b/zh-cn/opencv-cn.html.markdown @@ -0,0 +1,145 @@ +--- +category: tool +tool: OpenCV +filename: learnopencv.py +contributors: + - ["Yogesh Ojha", "http://github.com/yogeshojha"] +translators: + - ["GengchenXU", "https://github.com/GengchenXU"] +lang: zh-cn +--- +### Opencv + +Opencv(开源计算机视觉)是一个编程功能库,主要面向实时计算机视觉。最初由英特尔开发,后来由Willow Garage,然后Itseez(后来被英特尔收购)支持。Opencv 目前支持多种语言,如C++、Python、Java 等 + +#### 安装 +有关在计算机上安装 OpenCV,请参阅这些文章。 + +* Windows 安装说明: [https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_setup/py_setup_in_windows/py_setup_in_windows.html#install-opencv-python-in-windows]() +* Mac 安装说明 (High Sierra): [https://medium.com/@nuwanprabhath/installing-opencv-in-macos-high-sierra-for-python-3-89c79f0a246a]() +* Linux 安装说明 (Ubuntu 18.04): [https://www.pyimagesearch.com/2018/05/28/ubuntu-18-04-how-to-install-opencv]() + +### 在这里,我们将专注于 OpenCV 的 python 实现 + +```python +# OpenCV读取图片 +import cv2 +img = cv2.imread('cat.jpg') + +# 显示图片 +# imshow() 函数被用来显示图片 +cv2.imshow('Image',img) +# 第一个参数是窗口的标题,第二个参数是image +# 如果你得到错误,对象类型为None,你的图像路径可能是错误的。请重新检查图像包 +cv2.waitKey(0) +# waitKey() 是一个键盘绑定函数,参数以毫秒为单位。对于GUI事件,必须使用waitKey()函数。 + +# 保存图片 +cv2.imwrite('catgray.png',img) +# 第一个参数是文件名,第二个参数是图像 + +# 转换图像灰度 +gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + +# 从摄像头捕捉视频 +cap = cv2.VideoCapture(0) +#0 是你的相机,如果你有多台相机,你需要输入他们的id +while(True): + # 一帧一帧地获取 + _, frame = cap.read() + cv2.imshow('Frame',frame) + # 当用户按下q ->退出 + if cv2.waitKey(1) & 0xFF == ord('q'): + break +# 相机必须释放 +cap.release() + +# 在文件中播放视频 +cap = cv2.VideoCapture('movie.mp4') +while(cap.isOpened()): + _, frame = cap.read() + # 灰度播放视频 + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + cv2.imshow('frame',gray) + if cv2.waitKey(1) & 0xFF == ord('q'): + break +cap.release() + +# 在OpenCV中画线 +# cv2.line(img,(x,y),(x1,y1),(color->r,g,b->0 to 255),thickness)(注 color颜色rgb参数 thickness粗细) +cv2.line(img,(0,0),(511,511),(255,0,0),5) + +# 画矩形 +# cv2.rectangle(img,(x,y),(x1,y1),(color->r,g,b->0 to 255),thickness) +# 粗细= -1用于填充矩形 +cv2.rectangle(img,(384,0),(510,128),(0,255,0),3) + +# 画圆 +cv2.circle(img,(xCenter,yCenter), radius, (color->r,g,b->0 to 255), thickness) +cv2.circle(img,(200,90), 100, (0,0,255), -1) + +# 画椭圆 +cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1) + +# 在图像上增加文字 +cv2.putText(img,"Hello World!!!", (x,y), cv2.FONT_HERSHEY_SIMPLEX, 2, 255) + +# 合成图像 +img1 = cv2.imread('cat.png') +img2 = cv2.imread('openCV.jpg') +dst = cv2.addWeighted(img1,0.5,img2,0.5,0) + +# 阈值图像 +# 二进制阈值 +_,thresImg = cv2.threshold(img,127,255,cv2.THRESH_BINARY) +# Adaptive Thresholding +adapThres = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,11,2) + +# 模糊的形象 +# 高斯模糊 +blur = cv2.GaussianBlur(img,(5,5),0) +# 模糊中值 +medianBlur = cv2.medianBlur(img,5) + +# Canny 边缘检测 +img = cv2.imread('cat.jpg',0) +edges = cv2.Canny(img,100,200) + +# 用Haar Cascades进行人脸检测 +# 下载 Haar Cascades 在 https://github.com/opencv/opencv/blob/master/data/haarcascades/ +import cv2 +import numpy as np +face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') +eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml') + +img = cv2.imread('human.jpg') +gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + +aces = face_cascade.detectMultiScale(gray, 1.3, 5) +for (x,y,w,h) in faces: + cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) + roi_gray = gray[y:y+h, x:x+w] + roi_color = img[y:y+h, x:x+w] + eyes = eye_cascade.detectMultiScale(roi_gray) + for (ex,ey,ew,eh) in eyes: + cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) + +cv2.imshow('img',img) +cv2.waitKey(0) + +cv2.destroyAllWindows() +# destroyAllWindows() destroys all windows. +# 如果您希望销毁特定窗口,请传递您创建的窗口的确切名称。 +``` + +### 进一步阅读: + +* Download Cascade from [https://github.com/opencv/opencv/blob/master/data/haarcascades]() +* OpenCV 绘图函数 [https://docs.opencv.org/2.4/modules/core/doc/drawing_functions.html]() +* 最新的语言参考 [https://opencv.org]() +* 更多的资源 [https://en.wikipedia.org/wiki/OpenCV]() +* 优秀的的 OpenCV 教程 + * [https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html]() + * [https://realpython.com/python-opencv-color-spaces]() + * [https://pyimagesearch.com]() + * [https://www.learnopencv.com]() diff --git a/zh-cn/perl-cn.html.markdown b/zh-cn/perl-cn.html.markdown index 5b0d6179..46c54618 100644 --- a/zh-cn/perl-cn.html.markdown +++ b/zh-cn/perl-cn.html.markdown @@ -10,9 +10,9 @@ translators: lang: zh-cn --- -Perl 5是一个功能强大、特性齐全的编程语言,有25年的历史。 +Perl 是一个功能强大、特性齐全的编程语言,有25年的历史。 -Perl 5可以在包括便携式设备和大型机的超过100个平台上运行,既适用于快速原型构建,也适用于大型项目开发。 +Perl 可以在包括便携式设备和大型机的超过100个平台上运行,既适用于快速原型构建,也适用于大型项目开发。 ```perl # 单行注释以#号开头 @@ -146,7 +146,7 @@ perlfaq有很多常见问题和相应回答,也经常有对优秀CPAN模块的 #### 深入阅读 - - [perl-tutorial](http://perl-tutorial.org/) - - [www.perl.com的learn站点](http://www.perl.org/learn.html) - - [perldoc](http://perldoc.perl.org/) - - 以及 perl 内置的: `perldoc perlintro` +- [perl-tutorial](http://perl-tutorial.org/) +- [www.perl.com的learn站点](http://www.perl.org/learn.html) +- [perldoc](http://perldoc.perl.org/) +- 以及 perl 内置的: `perldoc perlintro` diff --git a/zh-cn/powershell-cn.html.markdown b/zh-cn/powershell-cn.html.markdown new file mode 100644 index 00000000..875d2e33 --- /dev/null +++ b/zh-cn/powershell-cn.html.markdown @@ -0,0 +1,325 @@ +--- +category: tool +tool: powershell +contributors: + - ["Wouter Van Schandevijl", "https://github.com/laoujin"] +translators: + - ["Feng Gao", "https://github.com/gaufung"] +filename: LearnPowershell-cn.ps1 +lang: zh-cn +--- + +PowerShell 是 Windows 平台下的脚本语言同时也是配置管理框架,它是建立在微软 .Net Framework 之上,Windows 7 以及之后版本都内置 Poweshell。下面的示例中都是 PoweShell 脚本的一部分或者直接能够在 Shell 交互窗口中执行。 + +与 Bash 最大的不同是你大部分操作的东西是对象而不是普通的文本。 + +[延伸阅读](https://docs.microsoft.com/zh-cn/powershell/scripting/overview) + +如果你不确定你的环境,执行如下操作: + +```powershell +Get-ExecutionPolicy -List +Set-ExecutionPolicy AllSigned +# Execution Policy 包含以下: +# - Restricted: 不会运行脚本。 +# - RemoteSigned: 只会运行受信任的发行商下载的脚本。 +# - AllSigned: 运行需要被信任发行商签名的脚本。 +# - Unrestricted: 运行所有脚本 +help about_Execution_Policies # 查看更多信息 + +# 当前 PowerShell 版本 +$PSVersionTable +``` + +获取帮助 + +```powershell +# 查找命令 +Get-Command about_* # 别名: gcm +Get-Command -Verb Add +Get-Alias ps +Get-Alias -Definition Get-Process + +Get-Help ps | less # 别名: help +ps | Get-Member # 别名: gm + +Show-Command Get-EventLog # GUI 填充参数 + +Update-Help # 管理员运行 +``` + +接下来是教程 + +```powershell +# 正如你看到的,每一行开头是 # 都是注释 + +# 简单的 Hello World 实例 +echo Hello world! +# echo 是 Write-Output (cmdlet) 的别名 +# 大部分 cmdlet 和函数都遵循 "动词-名词" 命名规则。 + +# 每个命令都从新的一行开始或者是一个分号 +echo 'This is the first line'; echo 'This is the second line' + +# 声明一个变量如下: +$aString="Some string" +# 或者像这样: +$aNumber = 5 -as [double] +$aList = 1,2,3,4,5 +$anEmptyList = @() +$aString = $aList -join '--' # 也包含 join 方法 +$aHashtable = @{name1='val1'; name2='val2'} + +# 使用变量: +echo $aString +echo "Interpolation: $aString" +echo "$aString has length of $($aString.Length)" +echo '$aString' +echo @" +This is a Here-String +$aString +"@ +# 注意 ' (单引号) 不是变量的一部分 +# 在这里字符串也可以是单引号 + +# 内置变量: +# 下面是一些有用的内置变量,比如: +echo "Booleans: $TRUE and $FALSE" +echo "Empty value: $NULL" +echo "Last program's return value: $?" +echo "Exit code of last run Windows-based program: $LastExitCode" +echo "The last token in the last line received by the session: $$" +echo "The first token: $^" +echo "Script's PID: $PID" +echo "Full path of current script directory: $PSScriptRoot" +echo 'Full path of current script: ' + $MyInvocation.MyCommand.Path +echo "FUll path of current directory: $Pwd" +echo "Bound arguments in a function, script or code block: $PSBoundParameters" +echo "Unbound arguments: $($Args -join ', ')." +# 更多的内置类型: `help about_Automatic_Variables` + +# 内联其他文件 (点操作符) +. .\otherScriptName.ps1 + + +### 控制流 +# 下面是条件判断结构 +if ($Age -is [string]) { + echo 'But.. $Age cannot be a string!' +} elseif ($Age -lt 12 -and $Age -gt 0) { + echo 'Child (Less than 12. Greater than 0)' +} else { + echo 'Adult' +} + +# Switch 语句比其他语言更强大 +$val = "20" +switch($val) { + { $_ -eq 42 } { "The answer equals 42"; break } + '20' { "Exactly 20"; break } + { $_ -like 's*' } { "Case insensitive"; break } + { $_ -clike 's*'} { "clike, ceq, cne for case sensitive"; break } + { $_ -notmatch '^.*$'} { "Regex matching. cnotmatch, cnotlike, ..."; break } + { 'x' -contains 'x'} { "FALSE! -contains is for lists!"; break } + default { "Others" } +} + +# 经典的 For 循环 +for($i = 1; $i -le 10; $i++) { + "Loop number $i" +} +# 或者可以更简洁 +1..10 | % { "Loop number $_" } + +# PowerShell 还提供其他循环方式 +foreach ($var in 'val1','val2','val3') { echo $var } +# while () {} +# do {} while () +# do {} until () + +# 异常处理 +try {} catch {} finally {} +try {} catch [System.NullReferenceException] { + echo $_.Exception | Format-List -Force +} + + +### Providers +# 列出当前目录下的文件和子目录 +ls # 或者 `dir` +cd ~ # 回到主目录 + +Get-Alias ls # -> Get-ChildItem +# 这些 cmdlet 有更加通用的名称,因为它不仅仅只操作当前目录,这一点和其他脚本语言不同。 +cd HKCU: # 跳转 HKEY_CURRENT_USER 注册表中的值 + +# 获取当前会话中的提供者 +Get-PSProvider + + +### 管道 +# Cmdlets 中的参数用来控制它们的行为: +Get-ChildItem -Filter *.txt -Name # 获取所有 txt 文件名。 +# 需要输入足够多的参数来确保没有歧义。 +ls -fi *.txt -n # -f 是不可以的因为 -Force 同样存在。 +# 使用 `Get-Help Get-ChildItem -Full` 来查看全部参数。 + +# 之前 cmdlet 获取的结果输出可以作为一下个输入。 +# `$_` 指代当前管道处理的对象。 +ls | Where-Object { $_.Name -match 'c' } | Export-CSV export.txt +ls | ? { $_.Name -match 'c' } | ConvertTo-HTML | Out-File export.html + +# 如果对管道的对象感到疑惑,使用 `Get-Member` 来查看该对象的可使用的方法和属性。 +ls | Get-Member +Get-Date | gm + +# ` 是行连续标识符,或者在每一行结尾添加一个 | +Get-Process | Sort-Object ID -Descending | Select-Object -First 10 Name,ID,VM ` + | Stop-Process -WhatIf + +Get-EventLog Application -After (Get-Date).AddHours(-2) | Format-List + +# 使用 % 作为 ForEach-Object 的简称。 +(a,b,c) | ForEach-Object ` + -Begin { "Starting"; $counter = 0 } ` + -Process { "Processing $_"; $counter++ } ` + -End { "Finishing: $counter" } + +# Get-Process 返回包含三列的表 +# 第三列是使用 2 位精度数值表示 VM 属性 +# 计算出来的列也可以表示更多的信息: +# `@{name='lbl';expression={$_}` +ps | Format-Table ID,Name,@{n='VM(MB)';e={'{0:n2}' -f ($_.VM / 1MB)}} -autoSize + + +### 函数 +# [string] 注记是可选的。 +function foo([string]$name) { + echo "Hey $name, have a function" +} + +# 调用你的函数 +foo "Say my name" + +# 函数可以包含命名参数、参数的注记和可解析的文档 +<# +.SYNOPSIS +Setup a new website +.DESCRIPTION +Creates everything your new website needs for much win +.PARAMETER siteName +The name for the new website +.EXAMPLE +New-Website -Name FancySite -Po 5000 +New-Website SiteWithDefaultPort +New-Website siteName 2000 # ERROR! Port argument could not be validated +('name1','name2') | New-Website -Verbose +#> +function New-Website() { + [CmdletBinding()] + param ( + [Parameter(ValueFromPipeline=$true, Mandatory=$true)] + [Alias('name')] + [string]$siteName, + [ValidateSet(3000,5000,8000)] + [int]$port = 3000 + ) + BEGIN { Write-Verbose 'Creating new website(s)' } + PROCESS { echo "name: $siteName, port: $port" } + END { Write-Verbose 'Website(s) created' } +} + + +### 都是 .NET +# PS 中的字符串事实上就是 .NET 的 System.String 类型 +# 所有 .NET 方法和属性都可用 +'string'.ToUpper().Replace('G', 'ggg') +# 或者更加 PowerShell 一点 +'string'.ToUpper() -replace 'G', 'ggg' + +# 不确定这样的话 .NET 方法如何调用 +'string' | gm + +# 调用静态 .NET 方法的语法: +[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') + +# 注意 .NET 方法调用必须使用括号,然而 PS 函数调用不能使用括号; +# 如果你调用 cmdlet/PS 函数使用了括号,就相当于传递了参数列表。 +$writer = New-Object System.IO.StreamWriter($path, $true) +$writer.Write([Environment]::NewLine) +$writer.Dispose() + +### IO +# 从输入读入一个值 +$Name = Read-Host "What's your name?" +echo "Hello, $Name!" +[int]$Age = Read-Host "What's your age?" + +# Test-Path, Split-Path, Join-Path, Resolve-Path +# Get-Content filename # 返回字符串数组 string[] +# Set-Content, Add-Content, Clear-Content +Get-Command ConvertTo-*,ConvertFrom-* + + +### 有用的东西 +# 更新 PATH +$env:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + +# 找到 Python 的 PATH +$env:PATH.Split(";") | Where-Object { $_ -like "*python*"} + +# 改变工作目录而不需要记住之前的路径 +Push-Location c:\temp # 改变工作目录至 c:\temp +Pop-Location # 改变到之前的工作目录 +# 别名: pushd 和 popd + +# 在下载之后解除目录阻塞 +Get-ChildItem -Recurse | Unblock-File + +# Windows 资源管理器打开当前目录 +ii . + +# 按任意键退出 +$host.UI.RawUI.ReadKey() +return + +# 创建快捷方式 +$WshShell = New-Object -comObject WScript.Shell +$Shortcut = $WshShell.CreateShortcut($link) +$Shortcut.TargetPath = $file +$Shortcut.WorkingDirectory = Split-Path $file +$Shortcut.Save() +``` + + +配置你的 PowerShell + +```powershell +# $Profile 是文件 `Microsoft.PowerShell_profile.ps1` 完整路径 +# 下面所有的代码都在 PS 会话开始的时候执行 +if (-not (Test-Path $Profile)) { + New-Item -Type file -Path $Profile -Force + notepad $Profile +} +# 更多信息: `help about_profiles` +# 更多关于 Shell 有用的信息,确保查看下面的 PSReadLine 项目。 +``` + +更多项目 + +* [Channel9](https://channel9.msdn.com/Search?term=powershell%20pipeline#ch9Search&lang-en=en) PowerShell 教程 +* [PSGet](https://github.com/psget/psget) PowerShell NuGet 包 +* [PSReadLine](https://github.com/lzybkr/PSReadLine/) 仿 bash 按行读取( Window10 默认包含) +* [Posh-Git](https://github.com/dahlbyk/posh-git/) Git 命令提示 (推荐!) +* [PSake](https://github.com/psake/psake) 自动构建工作 +* [Pester](https://github.com/pester/Pester) BDD 测试框架 +* [Jump-Location](https://github.com/tkellogg/Jump-Location) Poweshell 中 `cd` 来跳转目录 +* [PowerShell Community Extensions](http://pscx.codeplex.com/) (废弃) + +尚未涉及 + +* WMI: Windows 管理规范 (Get-CimInstance) +* 多任务: Start-Job -scriptBlock {...}, +* 代码签名 +* 远程 (Enter-PSSession/Exit-PSSession; Invoke-Command) diff --git a/zh-cn/pyqt-cn.html.markdown b/zh-cn/pyqt-cn.html.markdown new file mode 100644 index 00000000..04041c83 --- /dev/null +++ b/zh-cn/pyqt-cn.html.markdown @@ -0,0 +1,80 @@ +--- +category: tool +tool: PyQT +filename: learnpyqt.py +contributors: + - ["Nathan Hughes", "https://github.com/sirsharpest"] +translators: + - ["kdxcxs", "https://github.com/kdxcxs"] + - ["lsvih", "https://github.com/lsvih"] + - ["imlonghao", "https://github.com/imlonghao"] +lang: zh-cn +--- + +**Qt** 是一个用 C++ 实现的著名跨平台软件开发框架。只需少量更改有时候甚至不需要更改代码就能在多个软硬件平台上运行,同时拥有原生应用程序的功能和速度。 + + +以下内容改编自 [Aleksey Kholovchuk](https://github.com/vortexxx192) 编写的 C++ 版 QT 简介,并用 pyqt 重构原文代码,实现了部分相同的功能。 + +```python +import sys +from PyQt4 import QtGui + +def window(): + # 创建应用对象 + app = QtGui.QApplication(sys.argv) + # 创建一个 widget,作为 label 的父控件 + w = QtGui.QWidget() + # 在 widget 中添加一个 label 子控件 + b = QtGui.QLabel(w) + # 设置 label 的文字 + b.setText("Hello World!") + # 设置 widget 的尺寸和位置 + w.setGeometry(100, 100, 200, 50) + b.move(50, 20) + # 设置窗口的标题 + w.setWindowTitle("PyQt") + # 显示 widget 及其所有子控件 + w.show() + # 下面让程序跑起来,这行代码会启动事件循环并阻塞直到应用程序退出。 + sys.exit(app.exec_()) +if __name__ == '__main__': + window() +``` + +为了运用 pyqt 中一些更高级的功能,我们需要开始学习使用其他控件。下文演示了如何弹出对话框,该对话框在用户确认操作或输入信息等情况下经常用到。 + +```python +import sys +from PyQt4.QtGui import * +from PyQt4.QtCore import * +def window(): + app = QApplication(sys.argv) + w = QWidget() + # 创建一个按钮并添加到 widget 控件 w + b = QPushButton(w) + b.setText("Press me") + b.move(50, 50) + # 当按钮 b 被点击时调用 showdialog 函数 + # 注意函数调用时没有“()”,这样函数就能以对象的方式传入而非传入执行它所得到的返回值 + # 更多关于 pyqt 函数调用、传参等的内容见 pyqt 的信号机制 + b.clicked.connect(showdialog) + w.setWindowTitle("PyQt Dialog") + w.show() + sys.exit(app.exec_()) + +# 对话框窗口创建函数 +# 当窗口中的按钮被点击时退出本程序 +def showdialog(): + d = QDialog() + b1 = QPushButton("ok", d) + b1.move(50, 50) + d.setWindowTitle("Dialog") + # 这里的模态实现了在对话框弹出时阻塞程序同时屏蔽父窗口 + d.setWindowModality(Qt.ApplicationModal) + # 当按钮被点击时整个进程将会结束 + b1.clicked.connect(sys.exit) + d.exec_() +if __name__ == '__main__': + window() +``` diff --git a/zh-cn/python-cn.html.markdown b/zh-cn/python-cn.html.markdown index 65f125d1..127f7ad5 100644 --- a/zh-cn/python-cn.html.markdown +++ b/zh-cn/python-cn.html.markdown @@ -1,302 +1,476 @@ --- -language: python +language: Python contributors: - - ["Louie Dinh", "http://ldinh.ca"] + - ["Louie Dinh", "http://pythonpracticeprojects.com"] + - ["Steven Basart", "http://github.com/xksteven"] + - ["Andre Polykanine", "https://github.com/Oire"] translators: - - ["Chenbo Li", "http://binarythink.net"] -filename: learnpython-zh.py + - ["Geoff Liu", "http://geoffliu.me"] + - ["Maple", "https://github.com/mapleincode"] +filename: learnpython-cn.py lang: zh-cn + --- -Python 由 Guido Van Rossum 在90年代初创建。 它现在是最流行的语言之一 -我喜爱python是因为它有极为清晰的语法,甚至可以说,它就是可以执行的伪代码 +Python 是由吉多·范罗苏姆(Guido Van Rossum)在 90 年代早期设计。 +它是如今最常用的编程语言之一。它的语法简洁且优美,几乎就是可执行的伪代码。 -很欢迎来自您的反馈,你可以在[@louiedinh](http://twitter.com/louiedinh) 和 louiedinh [at] [google's email service] 这里找到我 +欢迎大家斧正。英文版原作 Louie Dinh [@louiedinh](http://twitter.com/louiedinh) +邮箱 louiedinh [at] [谷歌的信箱服务]。中文翻译 Geoff Liu。 -注意: 这篇文章针对的版本是Python 2.7,但大多也可使用于其他Python 2的版本 -如果是Python 3,请在网络上寻找其他教程 +注意:这篇教程是基于 Python 3 写的。如果你想学旧版 Python 2,我们特别有[另一篇教程](http://learnxinyminutes.com/docs/pythonlegacy/)。 ```python +# 用井字符开头的是单行注释 -# 单行注释 -""" 多行字符串可以用 - 三个引号包裹,不过这也可以被当做 - 多行注释 +""" 多行字符串用三个引号 + 包裹,也常被用来做多 + 行注释 """ #################################################### -## 1. 原始数据类型和操作符 +## 1. 原始数据类型和运算符 #################################################### -# 数字类型 +# 整数 3 # => 3 -# 简单的算数 -1 + 1 # => 2 -8 - 1 # => 7 +# 算术没有什么出乎意料的 +1 + 1 # => 2 +8 - 1 # => 7 10 * 2 # => 20 -35 / 5 # => 7 -# 整数的除法会自动取整 -5 / 2 # => 2 +# 但是除法例外,会自动转换成浮点数 +35 / 5 # => 7.0 +10.0 / 3 # => 3.3333333333333335 + +# 整数除法的结果都是向下取整 +5 // 3 # => 1 +5.0 // 3.0 # => 1.0 # 浮点数也可以 +-5 // 3 # => -2 +-5.0 // 3.0 # => -2.0 + +# 浮点数的运算结果也是浮点数 +3 * 2.0 # => 6.0 + +# 模除 +7 % 3 # => 1 +# i % j 结果的正负符号会和 j 相同,而不是和 i 相同 +-7 % 3 # => 2 -# 要做精确的除法,我们需要引入浮点数 -2.0 # 浮点数 -11.0 / 4.0 # => 2.75 精确多了 +# x 的 y 次方 +2**4 # => 16 -# 括号具有最高优先级 +# 用括号决定优先级 +1 + 3 * 2 # => 7 (1 + 3) * 2 # => 8 -# 布尔值也是基本的数据类型 -True -False +# 布尔值 (注意: 首字母大写) +True # => True +False # => False -# 用 not 来取非 -not True # => False +# 用 not 取非 +not True # => False not False # => True -# 相等 +# 逻辑运算符,注意 and 和 or 都是小写 +True and False # => False +False or True # => True + +# True 和 False 实质上就是数字 1 和0 +True + True # => 2 +True * 8 # => 8 +False - 5 # => -5 + +# 数值与 True 和 False 之间的比较运算 +0 == False # => True +2 == True # => False +1 == True # => True +-5 != False # => True + +# 使用布尔逻辑运算符对数字类型的值进行运算时,会把数值强制转换为布尔值进行运算 +# 但计算结果会返回它们的强制转换前的值 +# 注意不要把 bool(ints) 与位运算的 "按位与"、"按位或" (&, |) 混淆 +bool(0) # => False +bool(4) # => True +bool(-6) # => True +0 and 2 # => 0 +-5 or 0 # => -5 + +# 用==判断相等 1 == 1 # => True 2 == 1 # => False -# 不等 +# 用!=判断不等 1 != 1 # => False 2 != 1 # => True -# 更多的比较操作符 +# 比较大小 1 < 10 # => True 1 > 10 # => False 2 <= 2 # => True 2 >= 2 # => True -# 比较运算可以连起来写! +# 判断一个值是否在范围里 +1 < 2 and 2 < 3 # => True +2 < 3 and 3 < 2 # => False +# 大小比较可以连起来! 1 < 2 < 3 # => True 2 < 3 < 2 # => False -# 字符串通过 " 或 ' 括起来 -"This is a string." -'This is also a string.' +# (is 对比 ==) is 判断两个变量是否引用同一个对象, +# 而 == 判断两个对象是否含有相同的值 +a = [1, 2, 3, 4] # 变量 a 是一个新的列表, [1, 2, 3, 4] +b = a # 变量 b 赋值了变量 a 的值 +b is a # => True, a 和 b 引用的是同一个对象 +b == a # => True, a 和 b 的对象的值相同 +b = [1, 2, 3, 4] # 变量 b 赋值了一个新的列表, [1, 2, 3, 4] +b is a # => False, a 和 b 引用的不是同一个对象 +b == a # => True, a 和 b 的对象的值相同 -# 字符串通过加号拼接 -"Hello " + "world!" # => "Hello world!" - -# 字符串可以被视为字符的列表 -"This is a string"[0] # => 'T' -# % 可以用来格式化字符串 -"%s can be %s" % ("strings", "interpolated") +# 创建字符串可以使用单引号(')或者双引号(") +"这是个字符串" +'这也是个字符串' -# 也可以用 format 方法来格式化字符串 -# 推荐使用这个方法 -"{0} can be {1}".format("strings", "formatted") -# 也可以用变量名代替数字 -"{name} wants to eat {food}".format(name="Bob", food="lasagna") - -# None 是对象 +# 字符串可以使用加号连接成新的字符串 +"Hello " + "world!" # => "Hello world!" +# 非变量形式的字符串甚至可以在没有加号的情况下连接 +"Hello " "world!" # => "Hello world!" + +# 字符串可以被当作字符列表 +"Hello world!"[0] # => 'H' + +# 你可以获得字符串的长度 +len("This is a string") # => 16 + +# 你可以使用 f-strings 格式化字符串(python3.6+) +name = "Reiko" +f"She said her name is {name}." # => "She said her name is Reiko" +# 你可以在大括号内几乎加入任何 python 表达式,表达式的结果会以字符串的形式返回 +f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long." + +# 用 .format 来格式化字符串 +"{} can be {}".format("strings", "interpolated") +# 可以重复参数以节省时间 +"{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick") +# => "Jack be nimble, Jack be quick, Jack jump over the candle stick" +# 如果不想数参数,可以用关键字 +"{name} wants to eat {food}".format(name="Bob", food="lasagna") +# => "Bob wants to eat lasagna" + +# 如果你的 Python3 程序也要在 Python2.5 以下环境运行,也可以用老式的格式化语法 +"%s can be %s the %s way" % ("strings", "interpolated", "old") + +# None是一个对象 None # => None -# 不要用相等 `==` 符号来和None进行比较 -# 要用 `is` +# 当与 None 进行比较时不要用 ==,要用 is。is 是用来比较两个变量是否指向同一个对象。 "etc" is None # => False None is None # => True -# 'is' 可以用来比较对象的相等性 -# 这个操作符在比较原始数据时没多少用,但是比较对象时必不可少 - -# None, 0, 和空字符串都被算作 False -# 其他的均为 True -0 == False # => True -"" == False # => True +# None,0,空字符串,空列表,空字典,空元组都算是 False +# 所有其他值都是 True +bool(0) # => False +bool("") # => False +bool([]) # => False +bool({}) # => False +bool(()) # => False #################################################### ## 2. 变量和集合 #################################################### -# 很方便的输出 -print "I'm Python. Nice to meet you!" +# print是内置的打印函数 +print("I'm Python. Nice to meet you!") + +# 默认情况下,print 函数会在输出结果后加入一个空行作为结尾 +# 可以使用附加参数改变输出结尾 +print("Hello, World", end="!") # => Hello, World! +# 可以很简单的从终端获得输入数据 +input_string_var = input("Enter some data: ") # 返回字符串数值 -# 给变量赋值前不需要事先声明 -some_var = 5 # 一般建议使用小写字母和下划线组合来做为变量名 +# 在给变量赋值前不用提前声明 +# 习惯上变量命名是小写,用下划线分隔单词 +some_var = 5 some_var # => 5 # 访问未赋值的变量会抛出异常 -# 可以查看控制流程一节来了解如何异常处理 -some_other_var # 抛出 NameError +# 参考流程控制一段来学习异常处理 +some_unknown_var # 抛出 NameError -# if 语句可以作为表达式来使用 -"yahoo!" if 3 > 2 else 2 # => "yahoo!" +# "if" 可以用作表达式,它的作用等同于 C 语言的三元运算符 "?:" +"yay!" if 0 > 1 else "nay!" # => "nay!" -# 列表用来保存序列 +# 用列表 (list) 储存序列 li = [] -# 可以直接初始化列表 +# 创建列表时也可以同时赋给元素 other_li = [4, 5, 6] -# 在列表末尾添加元素 -li.append(1) # li 现在是 [1] -li.append(2) # li 现在是 [1, 2] -li.append(4) # li 现在是 [1, 2, 4] -li.append(3) # li 现在是 [1, 2, 4, 3] -# 移除列表末尾元素 -li.pop() # => 3 li 现在是 [1, 2, 4] -# 重新加进去 -li.append(3) # li is now [1, 2, 4, 3] again. - -# 像其他语言访问数组一样访问列表 +# 用append在列表最后追加元素 +li.append(1) # li现在是[1] +li.append(2) # li现在是[1, 2] +li.append(4) # li现在是[1, 2, 4] +li.append(3) # li现在是[1, 2, 4, 3] +# 用pop从列表尾部删除 +li.pop() # => 3 且li现在是[1, 2, 4] +# 把3再放回去 +li.append(3) # li变回[1, 2, 4, 3] + +# 列表存取跟数组一样 li[0] # => 1 -# 访问最后一个元素 +# 取出最后一个元素 li[-1] # => 3 -# 越界会抛出异常 -li[4] # 抛出越界异常 +# 越界存取会造成 IndexError +li[4] # 抛出 IndexError -# 切片语法需要用到列表的索引访问 -# 可以看做数学之中左闭右开区间 -li[1:3] # => [2, 4] -# 省略开头的元素 -li[2:] # => [4, 3] -# 省略末尾的元素 -li[:3] # => [1, 2, 4] +# 列表有切割语法 +li[1:3] # => [2, 4] +# 取尾 +li[2:] # => [4, 3] +# 取头 +li[:3] # => [1, 2, 4] +# 隔一个取一个 +li[::2] # =>[1, 4] +# 倒排列表 +li[::-1] # => [3, 4, 2, 1] +# 可以用三个参数的任何组合来构建切割 +# li[始:终:步伐] -# 删除特定元素 -del li[2] # li 现在是 [1, 2, 3] +# 简单的实现了单层数组的深度复制 +li2 = li[:] # => li2 = [1, 2, 4, 3] ,但 (li2 is li) 会返回 False -# 合并列表 -li + other_li # => [1, 2, 3, 4, 5, 6] - 并不会不改变这两个列表 +# 用 del 删除任何一个元素 +del li[2] # li 现在为 [1, 2, 3] -# 通过拼接来合并列表 -li.extend(other_li) # li 是 [1, 2, 3, 4, 5, 6] +# 删除第一个匹配的元素 +li.remove(2) # li 现在为 [1, 3] +li.remove(2) # 抛出错误 ValueError: 2 is not in the list -# 用 in 来返回元素是否在列表中 -1 in li # => True +# 在指定索引处插入一个新的元素 +li.insert(1, 2) # li is now [1, 2, 3] again -# 返回列表长度 -len(li) # => 6 +# 获得列表第一个匹配的值的索引 +li.index(2) # => 1 +li.index(4) # 抛出一个 ValueError: 4 is not in the list +# 列表可以相加 +# 注意:li 和 other_li 的值都不变 +li + other_li # => [1, 2, 3, 4, 5, 6] -# 元组类似于列表,但它是不可改变的 -tup = (1, 2, 3) -tup[0] # => 1 -tup[0] = 3 # 类型错误 +# 用 "extend()" 拼接列表 +li.extend(other_li) # li 现在是[1, 2, 3, 4, 5, 6] -# 对于大多数的列表操作,也适用于元组 -len(tup) # => 3 -tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) -tup[:2] # => (1, 2) -2 in tup # => True +# 用 "in" 测试列表是否包含值 +1 in li # => True -# 你可以将元组解包赋给多个变量 -a, b, c = (1, 2, 3) # a 是 1,b 是 2,c 是 3 -# 如果不加括号,将会被自动视为元组 -d, e, f = 4, 5, 6 -# 现在我们可以看看交换两个数字是多么容易的事 -e, d = d, e # d 是 5,e 是 4 +# 用 "len()" 取列表长度 +len(li) # => 6 -# 字典用来储存映射关系 +# 元组类似列表,但是不允许修改 +tup = (1, 2, 3) +tup[0] # => 1 +tup[0] = 3 # 抛出 TypeError + +# 如果元素数量为 1 的元组必须在元素之后加一个逗号 +# 其他元素数量的元组,包括空元组,都不需要 +type((1)) # => <class 'int'> +type((1,)) # => <class 'tuple'> +type(()) # => <class 'tuple'> + +# 列表允许的操作元组大多都可以 +len(tup) # => 3 +tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) +tup[:2] # => (1, 2) +2 in tup # => True + +# 可以把元组合列表解包,赋值给变量 +a, b, c = (1, 2, 3) # 现在 a 是 1,b 是 2,c 是 3 +# 也可以做扩展解包 +a, *b, c = (1, 2, 3, 4) # 现在 a 是 1, b 是 [2, 3], c 是 4 +# 元组周围的括号是可以省略的 +d, e, f = 4, 5, 6 # 元组 4, 5, 6 通过解包被赋值给变量 d, e, f +# 交换两个变量的值就这么简单 +e, d = d, e # 现在 d 是 5,e 是 4 + + +# 字典用来存储 key 到 value 的映射关系 empty_dict = {} -# 字典初始化 +# 初始化的字典 filled_dict = {"one": 1, "two": 2, "three": 3} -# 字典也用中括号访问元素 -filled_dict["one"] # => 1 +# 字典的 key 必须为不可变类型。 这是为了确保 key 被转换为唯一的哈希值以用于快速查询 +# 不可变类型包括整数、浮点、字符串、元组 +invalid_dict = {[1,2,3]: "123"} # => 抛出 TypeError: unhashable type: 'list' +valid_dict = {(1,2,3):[1,2,3]} # 然而 value 可以是任何类型 + +# 用[]取值 +filled_dict["one"] # => 1 + +# 用 keys 获得所有的键。 +# 因为 keys 返回一个可迭代对象,所以我们需要把它包在 "list()" 里才能转换为列表。 +# 我们下面会详细介绍可迭代。 +# 注意: 对于版本 < 3.7 的 python, 字典的 key 的排序是无序的。你的运行结果 +# 可能与下面的例子不符,但是在 3.7 版本,字典中的项会按照他们被插入到字典的顺序进行排序 +list(filled_dict.keys()) # => ["three", "two", "one"] Python 版本 <3.7 +list(filled_dict.keys()) # => ["one", "two", "three"] Python 版本 3.7+ -# 把所有的键保存在列表中 -filled_dict.keys() # => ["three", "two", "one"] -# 键的顺序并不是唯一的,得到的不一定是这个顺序 +# 用 "values()" 获得所有的值。跟 keys 一样也是可迭代对象,要使用 "list()" 才能转换为列表。 +# 注意: 排序顺序和 keys 的情况相同。 -# 把所有的值保存在列表中 -filled_dict.values() # => [3, 2, 1] -# 和键的顺序相同 +list(filled_dict.values()) # => [3, 2, 1] Python 版本 < 3.7 +list(filled_dict.values()) # => [1, 2, 3] Python 版本 3.7+ -# 判断一个键是否存在 -"one" in filled_dict # => True -1 in filled_dict # => False -# 查询一个不存在的键会抛出 KeyError -filled_dict["four"] # KeyError +# 用in测试一个字典是否包含一个键 +"one" in filled_dict # => True +1 in filled_dict # => False -# 用 get 方法来避免 KeyError -filled_dict.get("one") # => 1 -filled_dict.get("four") # => None -# get 方法支持在不存在的时候返回一个默认值 -filled_dict.get("one", 4) # => 1 +# 访问不存在的键会导致 KeyError +filled_dict["four"] # KeyError + +# 用 "get()" 来避免KeyError +filled_dict.get("one") # => 1 +filled_dict.get("four") # => None +# 当键不存在的时候 "get()" 方法可以返回默认值 +filled_dict.get("one", 4) # => 1 filled_dict.get("four", 4) # => 4 -# setdefault 是一个更安全的添加字典元素的方法 -filled_dict.setdefault("five", 5) # filled_dict["five"] 的值为 5 -filled_dict.setdefault("five", 6) # filled_dict["five"] 的值仍然是 5 +# "setdefault()" 方法只有当键不存在的时候插入新值 +filled_dict.setdefault("five", 5) # filled_dict["five"] 设为5 +filled_dict.setdefault("five", 6) # filled_dict["five"] 还是5 + +# 字典赋值 +filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} +filled_dict["four"] = 4 # 另一种赋值方法 +# 用 del 删除项 +del filled_dict["one"] # 从 filled_dict 中把 one 删除 -# 集合储存无顺序的元素 + +# 用 set 表达集合 empty_set = set() -# 初始化一个集合 -some_set = set([1, 2, 2, 3, 4]) # some_set 现在是 set([1, 2, 3, 4]) +# 初始化一个集合,语法跟字典相似。 +some_set = {1, 1, 2, 2, 3, 4} # some_set现在是 {1, 2, 3, 4} + +# 类似字典的 keys,set 的元素也必须是不可变类型 +invalid_set = {[1], 1} # => Raises a TypeError: unhashable type: 'list' +valid_set = {(1,), 1} -# Python 2.7 之后,大括号可以用来表示集合 -filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4} +# 可以把集合赋值于变量 +filled_set = some_set -# 向集合添加元素 -filled_set.add(5) # filled_set 现在是 {1, 2, 3, 4, 5} +# 为集合添加元素 +filled_set.add(5) # filled_set 现在是 {1, 2, 3, 4, 5} +# set 没有重复的元素 +filled_set.add(5) # filled_set 依然是 {1, 2, 3, 4, 5} -# 用 & 来计算集合的交 +# "&" 取交集 other_set = {3, 4, 5, 6} -filled_set & other_set # => {3, 4, 5} +filled_set & other_set # => {3, 4, 5} -# 用 | 来计算集合的并 -filled_set | other_set # => {1, 2, 3, 4, 5, 6} +# "|" 取并集 +filled_set | other_set # => {1, 2, 3, 4, 5, 6} -# 用 - 来计算集合的差 -{1, 2, 3, 4} - {2, 3, 5} # => {1, 4} +# "-" 取补集 +{1, 2, 3, 4} - {2, 3, 5} # => {1, 4} -# 用 in 来判断元素是否存在于集合中 -2 in filled_set # => True -10 in filled_set # => False +# "^" 取异或集(对称差) +{1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5} +# 判断左边的集合是否是右边集合的超集 +{1, 2} >= {1, 2, 3} # => False + +# 判断左边的集合是否是右边集合的子集 +{1, 2} <= {1, 2, 3} # => True + +# in 测试集合是否包含元素 +2 in filled_set # => True +10 in filled_set # => False + +# 单层集合的深度复制 +filled_set = some_set.copy() # filled_set 是 {1, 2, 3, 4, 5} +filled_set is some_set # => False #################################################### -## 3. 控制流程 +## 3. 流程控制和迭代器 #################################################### -# 新建一个变量 +# 先随便定义一个变量 some_var = 5 -# 这是个 if 语句,在 python 中缩进是很重要的。 -# 下面的代码片段将会输出 "some var is smaller than 10" +# 这是个if语句。注意缩进在Python里是有意义的! +# 缩进要使用 4 个空格而不是 tabs。 +# 这段代码会打印 "some_var is smaller than 10" if some_var > 10: - print "some_var is totally bigger than 10." -elif some_var < 10: # 这个 elif 语句是不必须的 - print "some_var is smaller than 10." -else: # 这个 else 也不是必须的 - print "some_var is indeed 10." + print("some_var is totally bigger than 10.") +elif some_var < 10: # elif 语句是可选的 + print("some_var is smaller than 10.") +else: # else 也是可选的 + print("some_var is indeed 10.") """ -用for循环遍历列表 -输出: +用 for 循环语句遍历列表 +打印: dog is a mammal cat is a mammal mouse is a mammal """ for animal in ["dog", "cat", "mouse"]: - # 你可以用 % 来格式化字符串 - print "%s is a mammal" % animal + # 你可以使用 format() 格式化字符串并插入值 + print("{} is a mammal".format(animal)) """ -`range(number)` 返回从0到给定数字的列表 -输出: +"range(number)" 返回数字列表从 0 到 number 的数字 +打印: 0 1 2 3 """ for i in range(4): - print i + print(i) + +""" +"range(lower, upper)" 会返回一个包含从 lower 到 upper 的数字迭代器 +prints: + 4 + 5 + 6 + 7 +""" +for i in range(4, 8): + print(i) + +""" +"range(lower, upper, step)" 会返回一个,从 lower 到 upper、并且间隔值为 step 的迭代器。 +如果 step 未传入则会使用默认值 1 +prints: + 4 + 6 +""" +for i in range(4, 8, 2): + print(i) + +""" +遍历列表,并且同时返回列表里的每一个元素的索引和数值。 +prints: + 0 dog + 1 cat + 2 mouse +""" +animals = ["dog", "cat", "mouse"] +for i, value in enumerate(animals): + print(i, value) """ -while 循环 -输出: +while 循环直到条件不满足 +打印: 0 1 2 @@ -304,173 +478,571 @@ while 循环 """ x = 0 while x < 4: - print x - x += 1 # x = x + 1 的简写 + print(x) + x += 1 # x = x + 1 的简写 -# 用 try/except 块来处理异常 -# Python 2.6 及以上适用: +# 用 try/except 块处理异常状况 try: - # 用 raise 来抛出异常 + # 用 raise 抛出异常 raise IndexError("This is an index error") except IndexError as e: - pass # pass 就是什么都不做,不过通常这里会做一些恢复工作 + pass # pass 是无操作,但是应该在这里处理错误 +except (TypeError, NameError): + pass # 可以同时处理不同类的错误 +else: # else语句是可选的,必须在所有的except之后 + print("All good!") # 只有当try运行完没有错误的时候这句才会运行 +finally: # 在任何情况下都会执行 + print("We can clean up resources here") + +# 你可以使用 with 语句来代替 try/finally 对操作进行结束的操作 +with open("myfile.txt") as f: + for line in f: + print(line) + +# 写入文件 +contents = {"aa": 12, "bb": 21} +with open("myfile1.txt", "w+") as file: + file.write(str(contents)) # 写入字符串到文件 + +with open("myfile2.txt", "w+") as file: + file.write(json.dumps(contents)) # 写入对象到文件 + +# Reading from a file +with open("myfile1.txt", "r+") as file: + contents = file.read() # 从文件读取字符串 +print(contents) +# print: {"aa": 12, "bb": 21} + +with open("myfile2.txt", "r+") as file: + contents = json.load(file) # 从文件读取 json 对象 +print(contents) +# print: {"aa": 12, "bb": 21} + +# Windows 环境调用 open() 读取文件的默认编码为 ANSI,如果需要读取 utf-8 编码的文件, +# 需要指定 encoding 参数: +# open("myfile3.txt", "r+", encoding = "utf-8") + + +# Python 提供一个叫做可迭代 (iterable) 的基本抽象。一个可迭代对象是可以被当作序列 +# 的对象。比如说上面 range 返回的对象就是可迭代的。 + +filled_dict = {"one": 1, "two": 2, "three": 3} +our_iterable = filled_dict.keys() +print(our_iterable) # => dict_keys(['one', 'two', 'three']),是一个实现可迭代接口的对象 + +# 可迭代对象可以遍历 +for i in our_iterable: + print(i) # 打印 one, two, three + +# 但是不可以随机访问 +our_iterable[1] # 抛出TypeError + +# 可迭代对象知道怎么生成迭代器 +our_iterator = iter(our_iterable) + +# 迭代器是一个可以记住遍历的位置的对象 +# 用 "next()" 获得下一个对象 +next(our_iterator) # => "one" + +# 再一次调取 "next()" 时会记得位置 +next(our_iterator) # => "two" +next(our_iterator) # => "three" + +# 当迭代器所有元素都取出后,会抛出 StopIteration +next(our_iterator) # 抛出 StopIteration + +# 我们还可以通过遍历访问所有的值,实际上,for 内部实现了迭代 +our_iterator = iter(our_iterable) +for i in our_iterator: + print(i) # 依次打印 one, two, three + +# 可以用 list 一次取出迭代器或者可迭代对象所有的元素 +list(filled_dict.keys()) # => 返回 ["one", "two", "three"] +list(our_iterator) # => 返回 [] 因为迭代的位置被保存了 #################################################### ## 4. 函数 #################################################### -# 用 def 来新建函数 +# 用def定义新函数 def add(x, y): - print "x is %s and y is %s" % (x, y) - return x + y # 通过 return 来返回值 + print("x is {} and y is {}".format(x, y)) + return x + y # 用 return 语句返回 -# 调用带参数的函数 -add(5, 6) # => 输出 "x is 5 and y is 6" 返回 11 +# 调用函数 +add(5, 6) # => 打印 "x is 5 and y is 6" 并且返回 11 -# 通过关键字赋值来调用函数 -add(y=6, x=5) # 顺序是无所谓的 +# 也可以用关键字参数来调用函数 +add(y=6, x=5) # 关键字参数可以用任何顺序 -# 我们也可以定义接受多个变量的函数,这些变量是按照顺序排列的 + +# 我们可以定义一个可变参数函数 def varargs(*args): return args -varargs(1, 2, 3) # => (1,2,3) +varargs(1, 2, 3) # => (1, 2, 3) -# 我们也可以定义接受多个变量的函数,这些变量是按照关键字排列的 +# 我们也可以定义一个关键字可变参数函数 def keyword_args(**kwargs): return kwargs -# 实际效果: -keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} +# 我们来看看结果是什么: +keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} + -# 你也可以同时将一个函数定义成两种形式 +# 这两种可变参数可以混着用 def all_the_args(*args, **kwargs): - print args - print kwargs + print(args) + print(kwargs) """ all_the_args(1, 2, a=3, b=4) prints: (1, 2) {"a": 3, "b": 4} """ -# 当调用函数的时候,我们也可以进行相反的操作,把元组和字典展开为参数 +# 调用可变参数函数时可以做跟上面相反的,用 * 展开元组,用 ** 展开字典。 args = (1, 2, 3, 4) kwargs = {"a": 3, "b": 4} -all_the_args(*args) # 等价于 foo(1, 2, 3, 4) -all_the_args(**kwargs) # 等价于 foo(a=3, b=4) -all_the_args(*args, **kwargs) # 等价于 foo(1, 2, 3, 4, a=3, b=4) +all_the_args(*args) # 相当于 all_the_args(1, 2, 3, 4) +all_the_args(**kwargs) # 相当于 all_the_args(a=3, b=4) +all_the_args(*args, **kwargs) # 相当于 all_the_args(1, 2, 3, 4, a=3, b=4) + +# 使用返回多个数值(返回值为元组类型) +def swap(x, y): + return y, x # 用不带括号的元组的格式来返回多个数值 + # (注意: 括号不需要加,但是也可以加) + +x = 1 +y = 2 +x, y = swap(x, y) # => x = 2, y = 1 +# (x, y) = swap(x,y) # 同上,括号不需要加,但是也可以加 + + +# 函数作用域 +x = 5 -# 函数在 python 中是一等公民 +def setX(num): + # 局部作用域的 x 和全局域的 x 是不同的 + x = num # => 43 + print (x) # => 43 + +def setGlobalX(num): + global x + print (x) # => 5 + x = num # 现在全局域的 x 被赋值 + print (x) # => 6 + +setX(43) +setGlobalX(6) + + +# 函数在 Python 是一等公民 def create_adder(x): def adder(y): return x + y return adder add_10 = create_adder(10) -add_10(3) # => 13 +add_10(3) # => 13 -# 匿名函数 -(lambda x: x > 2)(3) # => True +# 也有匿名函数 +(lambda x: x > 2)(3) # => True +(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5 -# 内置高阶函数 -map(add_10, [1, 2, 3]) # => [11, 12, 13] -filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7] +# 内置的高阶函数 +list(map(add_10, [1, 2, 3])) # => [11, 12, 13] +list(map(max, [1, 2, 3], [4, 2, 1])) # => [4, 2, 3] -# 可以用列表方法来对高阶函数进行更巧妙的引用 +list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7] + +# 用列表推导式可以简化映射和过滤。列表推导式的返回值是另一个列表。 [add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] -[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] +[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] + +# 你也可以用这种方式实现对集合和字典的构建 +{x for x in 'abcddeef' if x not in 'abc'} # => {'d', 'e', 'f'} +{x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} + #################################################### -## 5. 类 +## 5. 模块 #################################################### -# 我们新建的类是从 object 类中继承的 -class Human(object): +# 导入模块 +import math +print(math.sqrt(16)) # => 4.0 + +# 你可以导入模块中具体的函数 +from math import ceil, floor +print(ceil(3.7)) # => 4.0 +print(floor(3.7)) # => 3.0 + +# 你可以导入模块中的所有的函数 +# 警告: 此操作不推荐 +from math import * + +# 你可以对模块名进行简化 +import math as m +math.sqrt(16) == m.sqrt(16) # => True - # 类属性,由所有类的对象共享 +# Python 模块实质上是 Python 文件 +# 你可以自己编写自己的模块,然后导入 +# 模块的名称和文件名相同 + +# 你可以用 "dir()" 查看模块中定义的函数和字段 +import math +dir(math) + +# 当你的脚本文件所在的文件夹也包含了一个名为 math.py 的 Python 文件 +# 这个 math.py 文件会被代替引入,而不是引入 Python 內建模块中的 math +# 出现这个情况的原因是本地文件夹的引入优先级要比 Python 內建库引入优先级要高 + + +#################################################### +## 6. 类 +#################################################### + +# 我们使用 "class" 语句来创建类 +class Human: + + # 一个类的字段。 这个字段共享给这个类的所有实例。 species = "H. sapiens" - # 基本构造函数 + # 构造方法,当实例被初始化时被调用。注意名字前后的双下划线,这是表明这个属性 + # 或方法对 Python 有特殊意义,但是允许用户自行定义。 + # 方法(可能是对象或者属性) 类似: __init__, __str__,__repr__ etc + # 都是特殊的方法 + # 你自己取名时不应该用这种格式 def __init__(self, name): - # 将参数赋给对象成员属性 + # 将参数赋值给实例的 name 字段 self.name = name - # 成员方法,参数要有 self + # 初始化属性 + self._age = 0 + + # 实例方法,第一个参数总是self,也就是这个实例对象 def say(self, msg): - return "%s: %s" % (self.name, msg) + print("{name}: {message}".format(name=self.name, message=msg)) + + # 另一个实例方法 + def sing(self): + return 'yo... yo... microphone check... one two... one two...' - # 类方法由所有类的对象共享 - # 这类方法在调用时,会把类本身传给第一个参数 + # 类方法,被所有此类的实例共用。 + # 第一个参数是这个类对象。 @classmethod def get_species(cls): return cls.species - # 静态方法是不需要类和对象的引用就可以调用的方法 + # 静态方法。调用时没有实例或类的绑定。 @staticmethod def grunt(): return "*grunt*" + # property 有点类似 getter + # 它把方法 age() 转换为同名并且只读的属性 + # 通常情况下,可以不需要编写复杂的 getter 和 setter。 + @property + def age(self): + return self._age + + # 允许属性被修改 + @age.setter + def age(self, age): + self._age = age + + # 允许属性被删除 + @age.deleter + def age(self): + del self._age + +# 当 Python 解释器在读取源文件的时候,就会执行文件中所有的代码 +# 对 __name__ 的检查可以保证这块代码只会在这个模块是主程序的情况下被运行(而不是在引用时运行) +if __name__ == '__main__': + # + i = Human(name="Ian") + i.say("hi") # "Ian: hi" + j = Human("Joel") + j.say("hello") # "Joel: hello" + # i 和 j 都是 Human 实例化后的对象,换一句话说,它们都是 Human 实例 + + # 运行类方法 (classmethod) + i.say(i.get_species()) # "Ian: H. sapiens" + # 修改共享的类属性 + Human.species = "H. neanderthalensis" + i.say(i.get_species()) # => "Ian: H. neanderthalensis" + j.say(j.get_species()) # => "Joel: H. neanderthalensis" + + # 运行静态方法 (staticmethod) + print(Human.grunt()) # => "*grunt*" + + # 实例上也可以执行静态方法 + print(i.grunt()) # => "*grunt*" + + # 更新实例的属性 + i.age = 42 + # 访问实例的属性 + i.say(i.age) # => "Ian: 42" + j.say(j.age) # => "Joel: 0" + # 删除实例的属性 + del i.age + # i.age # => 这会抛出一个错误: AttributeError + + +#################################################### +## 6.1 类的继承 +#################################################### + +# 继承机制允许子类可以继承父类上的方法和变量。 +# 我们可以把 Human 类作为一个基础类或者说叫做父类, +# 然后定义一个名为 Superhero 的子类来继承父类上的比如 "species"、 "name"、 "age" 的属性 +# 和比如 "sing" 、"grunt" 这样的方法,同时,也可以定义它自己独有的属性 + +# 基于 Python 文件模块化的特点,你可以把这个类放在独立的文件中,比如说,human.py。 + +# 要从别的文件导入函数,需要使用以下的语句 +# from "filename-without-extension" import "function-or-class" + +from human import Human + +# 指定父类作为类初始化的参数 +class Superhero(Human): + + # 如果子类需要继承所有父类的定义,并且不需要做任何的修改, + # 你可以直接使用 "pass" 关键字(并且不需要其他任何语句) + # 但是在这个例子中会被注释掉,以用来生成不一样的子类。 + # pass + + # 子类可以重写父类定义的字段 + species = 'Superhuman' + + # 子类会自动的继承父类的构造函数包括它的参数,但同时,子类也可以新增额外的参数或者定义, + # 甚至去覆盖父类的方法比如说构造函数。 + # 这个构造函数从父类 "Human" 上继承了 "name" 参数,同时又新增了 "superpower" 和 + # "movie" 参数: + def __init__(self, name, movie=False, + superpowers=["super strength", "bulletproofing"]): + + # 新增额外类的参数 + self.fictional = True + self.movie = movie + # 注意可变的默认值,因为默认值是共享的 + self.superpowers = superpowers + + # "super" 函数让你可以访问父类中被子类重写的方法 + # 在这个例子中,被重写的是 __init__ 方法 + # 这个语句是用来运行父类的构造函数: + super().__init__(name) + + # 重写父类中的 sing 方法 + def sing(self): + return 'Dun, dun, DUN!' + + # 新增一个额外的方法 + def boast(self): + for power in self.superpowers: + print("I wield the power of {pow}!".format(pow=power)) + + +if __name__ == '__main__': + sup = Superhero(name="Tick") + + # 检查实例类型 + if isinstance(sup, Human): + print('I am human') + if type(sup) is Superhero: + print('I am a superhero') + + # 获取方法解析顺序 MRO,MRO 被用于 getattr() 和 super() + # 这个字段是动态的,并且可以被修改 + print(Superhero.__mro__) # => (<class '__main__.Superhero'>, + # => <class 'human.Human'>, <class 'object'>) + + # 调用父类的方法并且使用子类的属性 + print(sup.get_species()) # => Superhuman -# 实例化一个类 -i = Human(name="Ian") -print i.say("hi") # 输出 "Ian: hi" + # 调用被重写的方法 + print(sup.sing()) # => Dun, dun, DUN! -j = Human("Joel") -print j.say("hello") # 输出 "Joel: hello" + # 调用 Human 的方法 + sup.say('Spoon') # => Tick: Spoon -# 访问类的方法 -i.get_species() # => "H. sapiens" + # 调用 Superhero 独有的方法 + sup.boast() # => I wield the power of super strength! + # => I wield the power of bulletproofing! -# 改变共享属性 -Human.species = "H. neanderthalensis" -i.get_species() # => "H. neanderthalensis" -j.get_species() # => "H. neanderthalensis" + # 继承类的字段 + sup.age = 31 + print(sup.age) # => 31 -# 访问静态变量 -Human.grunt() # => "*grunt*" + # Superhero 独有的字段 + print('Am I Oscar eligible? ' + str(sup.movie)) #################################################### -## 6. 模块 +## 6.2 多重继承 #################################################### -# 我们可以导入其他模块 -import math -print math.sqrt(16) # => 4.0 +# 定义另一个类 +# bat.py +class Bat: -# 我们也可以从一个模块中导入特定的函数 -from math import ceil, floor -print ceil(3.7) # => 4.0 -print floor(3.7) # => 3.0 + species = 'Baty' -# 从模块中导入所有的函数 -# 警告:不推荐使用 -from math import * + def __init__(self, can_fly=True): + self.fly = can_fly -# 简写模块名 -import math as m -math.sqrt(16) == m.sqrt(16) # => True + # 这个类同样有 say 的方法 + def say(self, msg): + msg = '... ... ...' + return msg -# Python的模块其实只是普通的python文件 -# 你也可以创建自己的模块,并且导入它们 -# 模块的名字就和文件的名字相同 + # 新增一个独有的方法 + def sonar(self): + return '))) ... (((' -# 也可以通过下面的方法查看模块中有什么属性和方法 -import math -dir(math) +if __name__ == '__main__': + b = Bat() + print(b.say('hello')) + print(b.fly) + +# 现在我们来定义一个类来同时继承 Superhero 和 Bat +# superhero.py +from superhero import Superhero +from bat import Bat + +# 定义 Batman 作为子类,来同时继承 SuperHero 和 Bat +class Batman(Superhero, Bat): + + def __init__(self, *args, **kwargs): + # 通常要继承属性,你必须调用 super: + # super(Batman, self).__init__(*args, **kwargs) + # 然而在这里我们处理的是多重继承,而 super() 只会返回 MRO 列表的下一个基础类。 + # 因此,我们需要显式调用初始类的 __init__ + # *args 和 **kwargs 传递参数时更加清晰整洁,而对于父类而言像是 “剥了一层洋葱” + Superhero.__init__(self, 'anonymous', movie=True, + superpowers=['Wealthy'], *args, **kwargs) + Bat.__init__(self, *args, can_fly=False, **kwargs) + # 重写了 name 字段 + self.name = 'Sad Affleck' + + def sing(self): + return 'nan nan nan nan nan batman!' + + +if __name__ == '__main__': + sup = Batman() + + # 获取方法解析顺序 MRO,MRO 被用于 getattr() 和 super() + # 这个字段是动态的,并且可以被修改 + print(Batman.__mro__) # => (<class '__main__.Batman'>, + # => <class 'superhero.Superhero'>, + # => <class 'human.Human'>, + # => <class 'bat.Bat'>, <class 'object'>) + # 调用父类的方法并且使用子类的属性 + print(sup.get_species()) # => Superhuman + # 调用被重写的类 + print(sup.sing()) # => nan nan nan nan nan batman! + + # 调用 Human 上的方法,(之所以是 Human 而不是 Bat),是因为继承顺序起了作用 + sup.say('I agree') # => Sad Affleck: I agree + + # 调用仅存在于第二个继承的父类的方法 + print(sup.sonar()) # => ))) ... ((( + + # 继承类的属性 + sup.age = 100 + print(sup.age) # => 100 + + # 从第二个类上继承字段,并且其默认值被重写 + print('Can I fly? ' + str(sup.fly)) # => Can I fly? False + + +#################################################### +## 7. 高级用法 +#################################################### + +# 用生成器(generators)方便地写惰性运算 +def double_numbers(iterable): + for i in iterable: + yield i + i + +# 生成器只有在需要时才计算下一个值。它们每一次循环只生成一个值,而不是把所有的 +# 值全部算好。 +# +# range的返回值也是一个生成器,不然一个1到900000000的列表会花很多时间和内存。 +# +# 如果你想用一个Python的关键字当作变量名,可以加一个下划线来区分。 +range_ = range(1, 900000000) +# 当找到一个 >=30 的结果就会停 +# 这意味着 `double_numbers` 不会生成大于30的数。 +for i in double_numbers(range_): + print(i) + if i >= 30: + break +# 你也可以把一个生成器推导直接转换为列表 +values = (-x for x in [1,2,3,4,5]) +gen_to_list = list(values) +print(gen_to_list) # => [-1, -2, -3, -4, -5] + + +# 装饰器(decorators) +# 这个例子中,beg装饰say +# beg会先调用say。如果返回的say_please为真,beg会改变返回的字符串。 +from functools import wraps + + +def beg(target_function): + @wraps(target_function) + def wrapper(*args, **kwargs): + msg, say_please = target_function(*args, **kwargs) + if say_please: + return "{} {}".format(msg, "Please! I am poor :(") + return msg + + return wrapper + + +@beg +def say(say_please=False): + msg = "Can you buy me a beer?" + return msg, say_please + + +print(say()) # Can you buy me a beer? +print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :( ``` -## 更多阅读 -希望学到更多?试试下面的链接: -* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/) -* [Dive Into Python](http://www.diveintopython.net/) -* [The Official Docs](http://docs.python.org/2.6/) -* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/) -* [Python Module of the Week](http://pymotw.com/2/) +## 想继续学吗? + +### 在线免费材料(英文) + +* [Automate the Boring Stuff with Python](https://automatetheboringstuff.com/) +* [Ideas for Python Projects](http://pythonpracticeprojects.com/) +* [The Official Docs](https://docs.python.org/3/) +* [Hitchhiker’s Guide to Python](https://docs.python-guide.org/en/latest/) +* [Python Course](https://www.python-course.eu/) +* [Free Interactive Python Course](http://www.kikodo.io/) +* [First Steps With Python](https://realpython.com/learn/python-first-steps/) +* [A curated list of awesome Python frameworks, libraries and software](https://github.com/vinta/awesome-python) +* [30 Python Language Features and Tricks You May Not Know About](https://sahandsaba.com/thirty-python-language-features-and-tricks-you-may-not-know.html) +* [Official Style Guide for Python](https://www.python.org/dev/peps/pep-0008/) +* [Python 3 Computer Science Circles](https://cscircles.cemc.uwaterloo.ca/) +* [Dive Into Python 3](https://www.diveintopython3.net/index.html) +* [A Crash Course in Python for Scientists](https://nbviewer.jupyter.org/gist/anonymous/5924718) +* [Python Tutorial for Intermediates](https://pythonbasics.org/) +* [Build a Desktop App with Python](https://pythonpyqt.com/) + +### 书籍(也是英文) + +* [Programming Python](http://www.amazon.com/gp/product/0596158106/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596158106&linkCode=as2&tag=homebits04-20) +* [Dive Into Python](http://www.amazon.com/gp/product/1441413022/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441413022&linkCode=as2&tag=homebits04-20) +* [Python Essential Reference](http://www.amazon.com/gp/product/0672329786/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0672329786&linkCode=as2&tag=homebits04-20) + diff --git a/zh-cn/python3-cn.html.markdown b/zh-cn/python3-cn.html.markdown deleted file mode 100644 index fd962305..00000000 --- a/zh-cn/python3-cn.html.markdown +++ /dev/null @@ -1,632 +0,0 @@ ---- -language: python3 -contributors: - - ["Louie Dinh", "http://pythonpracticeprojects.com"] - - ["Steven Basart", "http://github.com/xksteven"] - - ["Andre Polykanine", "https://github.com/Oire"] -translators: - - ["Geoff Liu", "http://geoffliu.me"] -filename: learnpython3-cn.py -lang: zh-cn ---- - -Python 是由吉多·范罗苏姆(Guido Van Rossum)在 90 年代早期设计。 -它是如今最常用的编程语言之一。它的语法简洁且优美,几乎就是可执行的伪代码。 - -欢迎大家斧正。英文版原作 Louie Dinh [@louiedinh](http://twitter.com/louiedinh) -邮箱 louiedinh [at] [谷歌的信箱服务]。中文翻译 Geoff Liu。 - -注意:这篇教程是基于 Python 3 写的。如果你想学旧版 Python 2,我们特别有[另一篇教程](http://learnxinyminutes.com/docs/python/)。 - -```python - -# 用井字符开头的是单行注释 - -""" 多行字符串用三个引号 - 包裹,也常被用来做多 - 行注释 -""" - -#################################################### -## 1. 原始数据类型和运算符 -#################################################### - -# 整数 -3 # => 3 - -# 算术没有什么出乎意料的 -1 + 1 # => 2 -8 - 1 # => 7 -10 * 2 # => 20 - -# 但是除法例外,会自动转换成浮点数 -35 / 5 # => 7.0 -5 / 3 # => 1.6666666666666667 - -# 整数除法的结果都是向下取整 -5 // 3 # => 1 -5.0 // 3.0 # => 1.0 # 浮点数也可以 --5 // 3 # => -2 --5.0 // 3.0 # => -2.0 - -# 浮点数的运算结果也是浮点数 -3 * 2.0 # => 6.0 - -# 模除 -7 % 3 # => 1 - -# x的y次方 -2**4 # => 16 - -# 用括号决定优先级 -(1 + 3) * 2 # => 8 - -# 布尔值 -True -False - -# 用not取非 -not True # => False -not False # => True - -# 逻辑运算符,注意and和or都是小写 -True and False # => False -False or True # => True - -# 整数也可以当作布尔值 -0 and 2 # => 0 --5 or 0 # => -5 -0 == False # => True -2 == True # => False -1 == True # => True - -# 用==判断相等 -1 == 1 # => True -2 == 1 # => False - -# 用!=判断不等 -1 != 1 # => False -2 != 1 # => True - -# 比较大小 -1 < 10 # => True -1 > 10 # => False -2 <= 2 # => True -2 >= 2 # => True - -# 大小比较可以连起来! -1 < 2 < 3 # => True -2 < 3 < 2 # => False - -# 字符串用单引双引都可以 -"这是个字符串" -'这也是个字符串' - -# 用加号连接字符串 -"Hello " + "world!" # => "Hello world!" - -# 字符串可以被当作字符列表 -"This is a string"[0] # => 'T' - -# 用.format来格式化字符串 -"{} can be {}".format("strings", "interpolated") - -# 可以重复参数以节省时间 -"{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick") -# => "Jack be nimble, Jack be quick, Jack jump over the candle stick" - -# 如果不想数参数,可以用关键字 -"{name} wants to eat {food}".format(name="Bob", food="lasagna") -# => "Bob wants to eat lasagna" - -# 如果你的Python3程序也要在Python2.5以下环境运行,也可以用老式的格式化语法 -"%s can be %s the %s way" % ("strings", "interpolated", "old") - -# None是一个对象 -None # => None - -# 当与None进行比较时不要用 ==,要用is。is是用来比较两个变量是否指向同一个对象。 -"etc" is None # => False -None is None # => True - -# None,0,空字符串,空列表,空字典都算是False -# 所有其他值都是True -bool(0) # => False -bool("") # => False -bool([]) # => False -bool({}) # => False - - -#################################################### -## 2. 变量和集合 -#################################################### - -# print是内置的打印函数 -print("I'm Python. Nice to meet you!") - -# 在给变量赋值前不用提前声明 -# 传统的变量命名是小写,用下划线分隔单词 -some_var = 5 -some_var # => 5 - -# 访问未赋值的变量会抛出异常 -# 参考流程控制一段来学习异常处理 -some_unknown_var # 抛出NameError - -# 用列表(list)储存序列 -li = [] -# 创建列表时也可以同时赋给元素 -other_li = [4, 5, 6] - -# 用append在列表最后追加元素 -li.append(1) # li现在是[1] -li.append(2) # li现在是[1, 2] -li.append(4) # li现在是[1, 2, 4] -li.append(3) # li现在是[1, 2, 4, 3] -# 用pop从列表尾部删除 -li.pop() # => 3 且li现在是[1, 2, 4] -# 把3再放回去 -li.append(3) # li变回[1, 2, 4, 3] - -# 列表存取跟数组一样 -li[0] # => 1 -# 取出最后一个元素 -li[-1] # => 3 - -# 越界存取会造成IndexError -li[4] # 抛出IndexError - -# 列表有切割语法 -li[1:3] # => [2, 4] -# 取尾 -li[2:] # => [4, 3] -# 取头 -li[:3] # => [1, 2, 4] -# 隔一个取一个 -li[::2] # =>[1, 4] -# 倒排列表 -li[::-1] # => [3, 4, 2, 1] -# 可以用三个参数的任何组合来构建切割 -# li[始:终:步伐] - -# 用del删除任何一个元素 -del li[2] # li is now [1, 2, 3] - -# 列表可以相加 -# 注意:li和other_li的值都不变 -li + other_li # => [1, 2, 3, 4, 5, 6] - -# 用extend拼接列表 -li.extend(other_li) # li现在是[1, 2, 3, 4, 5, 6] - -# 用in测试列表是否包含值 -1 in li # => True - -# 用len取列表长度 -len(li) # => 6 - - -# 元组是不可改变的序列 -tup = (1, 2, 3) -tup[0] # => 1 -tup[0] = 3 # 抛出TypeError - -# 列表允许的操作元组大都可以 -len(tup) # => 3 -tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) -tup[:2] # => (1, 2) -2 in tup # => True - -# 可以把元组合列表解包,赋值给变量 -a, b, c = (1, 2, 3) # 现在a是1,b是2,c是3 -# 元组周围的括号是可以省略的 -d, e, f = 4, 5, 6 -# 交换两个变量的值就这么简单 -e, d = d, e # 现在d是5,e是4 - - -# 用字典表达映射关系 -empty_dict = {} -# 初始化的字典 -filled_dict = {"one": 1, "two": 2, "three": 3} - -# 用[]取值 -filled_dict["one"] # => 1 - - -# 用 keys 获得所有的键。 -# 因为 keys 返回一个可迭代对象,所以在这里把结果包在 list 里。我们下面会详细介绍可迭代。 -# 注意:字典键的顺序是不定的,你得到的结果可能和以下不同。 -list(filled_dict.keys()) # => ["three", "two", "one"] - - -# 用values获得所有的值。跟keys一样,要用list包起来,顺序也可能不同。 -list(filled_dict.values()) # => [3, 2, 1] - - -# 用in测试一个字典是否包含一个键 -"one" in filled_dict # => True -1 in filled_dict # => False - -# 访问不存在的键会导致KeyError -filled_dict["four"] # KeyError - -# 用get来避免KeyError -filled_dict.get("one") # => 1 -filled_dict.get("four") # => None -# 当键不存在的时候get方法可以返回默认值 -filled_dict.get("one", 4) # => 1 -filled_dict.get("four", 4) # => 4 - -# setdefault方法只有当键不存在的时候插入新值 -filled_dict.setdefault("five", 5) # filled_dict["five"]设为5 -filled_dict.setdefault("five", 6) # filled_dict["five"]还是5 - -# 字典赋值 -filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} -filled_dict["four"] = 4 # 另一种赋值方法 - -# 用del删除 -del filled_dict["one"] # 从filled_dict中把one删除 - - -# 用set表达集合 -empty_set = set() -# 初始化一个集合,语法跟字典相似。 -some_set = {1, 1, 2, 2, 3, 4} # some_set现在是{1, 2, 3, 4} - -# 可以把集合赋值于变量 -filled_set = some_set - -# 为集合添加元素 -filled_set.add(5) # filled_set现在是{1, 2, 3, 4, 5} - -# & 取交集 -other_set = {3, 4, 5, 6} -filled_set & other_set # => {3, 4, 5} - -# | 取并集 -filled_set | other_set # => {1, 2, 3, 4, 5, 6} - -# - 取补集 -{1, 2, 3, 4} - {2, 3, 5} # => {1, 4} - -# in 测试集合是否包含元素 -2 in filled_set # => True -10 in filled_set # => False - - -#################################################### -## 3. 流程控制和迭代器 -#################################################### - -# 先随便定义一个变量 -some_var = 5 - -# 这是个if语句。注意缩进在Python里是有意义的 -# 印出"some_var比10小" -if some_var > 10: - print("some_var比10大") -elif some_var < 10: # elif句是可选的 - print("some_var比10小") -else: # else也是可选的 - print("some_var就是10") - - -""" -用for循环语句遍历列表 -打印: - dog is a mammal - cat is a mammal - mouse is a mammal -""" -for animal in ["dog", "cat", "mouse"]: - print("{} is a mammal".format(animal)) - -""" -"range(number)"返回数字列表从0到给的数字 -打印: - 0 - 1 - 2 - 3 -""" -for i in range(4): - print(i) - -""" -while循环直到条件不满足 -打印: - 0 - 1 - 2 - 3 -""" -x = 0 -while x < 4: - print(x) - x += 1 # x = x + 1 的简写 - -# 用try/except块处理异常状况 -try: - # 用raise抛出异常 - raise IndexError("This is an index error") -except IndexError as e: - pass # pass是无操作,但是应该在这里处理错误 -except (TypeError, NameError): - pass # 可以同时处理不同类的错误 -else: # else语句是可选的,必须在所有的except之后 - print("All good!") # 只有当try运行完没有错误的时候这句才会运行 - - -# Python提供一个叫做可迭代(iterable)的基本抽象。一个可迭代对象是可以被当作序列 -# 的对象。比如说上面range返回的对象就是可迭代的。 - -filled_dict = {"one": 1, "two": 2, "three": 3} -our_iterable = filled_dict.keys() -print(our_iterable) # => dict_keys(['one', 'two', 'three']),是一个实现可迭代接口的对象 - -# 可迭代对象可以遍历 -for i in our_iterable: - print(i) # 打印 one, two, three - -# 但是不可以随机访问 -our_iterable[1] # 抛出TypeError - -# 可迭代对象知道怎么生成迭代器 -our_iterator = iter(our_iterable) - -# 迭代器是一个可以记住遍历的位置的对象 -# 用__next__可以取得下一个元素 -our_iterator.__next__() # => "one" - -# 再一次调取__next__时会记得位置 -our_iterator.__next__() # => "two" -our_iterator.__next__() # => "three" - -# 当迭代器所有元素都取出后,会抛出StopIteration -our_iterator.__next__() # 抛出StopIteration - -# 可以用list一次取出迭代器所有的元素 -list(filled_dict.keys()) # => Returns ["one", "two", "three"] - - - -#################################################### -## 4. 函数 -#################################################### - -# 用def定义新函数 -def add(x, y): - print("x is {} and y is {}".format(x, y)) - return x + y # 用return语句返回 - -# 调用函数 -add(5, 6) # => 印出"x is 5 and y is 6"并且返回11 - -# 也可以用关键字参数来调用函数 -add(y=6, x=5) # 关键字参数可以用任何顺序 - - -# 我们可以定义一个可变参数函数 -def varargs(*args): - return args - -varargs(1, 2, 3) # => (1, 2, 3) - - -# 我们也可以定义一个关键字可变参数函数 -def keyword_args(**kwargs): - return kwargs - -# 我们来看看结果是什么: -keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} - - -# 这两种可变参数可以混着用 -def all_the_args(*args, **kwargs): - print(args) - print(kwargs) -""" -all_the_args(1, 2, a=3, b=4) prints: - (1, 2) - {"a": 3, "b": 4} -""" - -# 调用可变参数函数时可以做跟上面相反的,用*展开序列,用**展开字典。 -args = (1, 2, 3, 4) -kwargs = {"a": 3, "b": 4} -all_the_args(*args) # 相当于 foo(1, 2, 3, 4) -all_the_args(**kwargs) # 相当于 foo(a=3, b=4) -all_the_args(*args, **kwargs) # 相当于 foo(1, 2, 3, 4, a=3, b=4) - - -# 函数作用域 -x = 5 - -def setX(num): - # 局部作用域的x和全局域的x是不同的 - x = num # => 43 - print (x) # => 43 - -def setGlobalX(num): - global x - print (x) # => 5 - x = num # 现在全局域的x被赋值 - print (x) # => 6 - -setX(43) -setGlobalX(6) - - -# 函数在Python是一等公民 -def create_adder(x): - def adder(y): - return x + y - return adder - -add_10 = create_adder(10) -add_10(3) # => 13 - -# 也有匿名函数 -(lambda x: x > 2)(3) # => True - -# 内置的高阶函数 -map(add_10, [1, 2, 3]) # => [11, 12, 13] -filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7] - -# 用列表推导式可以简化映射和过滤。列表推导式的返回值是另一个列表。 -[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] -[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] - -#################################################### -## 5. 类 -#################################################### - - -# 定义一个继承object的类 -class Human(object): - - # 类属性,被所有此类的实例共用。 - species = "H. sapiens" - - # 构造方法,当实例被初始化时被调用。注意名字前后的双下划线,这是表明这个属 - # 性或方法对Python有特殊意义,但是允许用户自行定义。你自己取名时不应该用这 - # 种格式。 - def __init__(self, name): - # Assign the argument to the instance's name attribute - self.name = name - - # 实例方法,第一个参数总是self,就是这个实例对象 - def say(self, msg): - return "{name}: {message}".format(name=self.name, message=msg) - - # 类方法,被所有此类的实例共用。第一个参数是这个类对象。 - @classmethod - def get_species(cls): - return cls.species - - # 静态方法。调用时没有实例或类的绑定。 - @staticmethod - def grunt(): - return "*grunt*" - - -# 构造一个实例 -i = Human(name="Ian") -print(i.say("hi")) # 印出 "Ian: hi" - -j = Human("Joel") -print(j.say("hello")) # 印出 "Joel: hello" - -# 调用一个类方法 -i.get_species() # => "H. sapiens" - -# 改一个共用的类属性 -Human.species = "H. neanderthalensis" -i.get_species() # => "H. neanderthalensis" -j.get_species() # => "H. neanderthalensis" - -# 调用静态方法 -Human.grunt() # => "*grunt*" - - -#################################################### -## 6. 模块 -#################################################### - -# 用import导入模块 -import math -print(math.sqrt(16)) # => 4.0 - -# 也可以从模块中导入个别值 -from math import ceil, floor -print(ceil(3.7)) # => 4.0 -print(floor(3.7)) # => 3.0 - -# 可以导入一个模块中所有值 -# 警告:不建议这么做 -from math import * - -# 如此缩写模块名字 -import math as m -math.sqrt(16) == m.sqrt(16) # => True - -# Python模块其实就是普通的Python文件。你可以自己写,然后导入, -# 模块的名字就是文件的名字。 - -# 你可以这样列出一个模块里所有的值 -import math -dir(math) - - -#################################################### -## 7. 高级用法 -#################################################### - -# 用生成器(generators)方便地写惰性运算 -def double_numbers(iterable): - for i in iterable: - yield i + i - -# 生成器只有在需要时才计算下一个值。它们每一次循环只生成一个值,而不是把所有的 -# 值全部算好。 -# -# range的返回值也是一个生成器,不然一个1到900000000的列表会花很多时间和内存。 -# -# 如果你想用一个Python的关键字当作变量名,可以加一个下划线来区分。 -range_ = range(1, 900000000) -# 当找到一个 >=30 的结果就会停 -# 这意味着 `double_numbers` 不会生成大于30的数。 -for i in double_numbers(range_): - print(i) - if i >= 30: - break - - -# 装饰器(decorators) -# 这个例子中,beg装饰say -# beg会先调用say。如果返回的say_please为真,beg会改变返回的字符串。 -from functools import wraps - - -def beg(target_function): - @wraps(target_function) - def wrapper(*args, **kwargs): - msg, say_please = target_function(*args, **kwargs) - if say_please: - return "{} {}".format(msg, "Please! I am poor :(") - return msg - - return wrapper - - -@beg -def say(say_please=False): - msg = "Can you buy me a beer?" - return msg, say_please - - -print(say()) # Can you buy me a beer? -print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :( -``` - -## 想继续学吗? - -### 线上免费材料(英文) - -* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/) -* [Dive Into Python](http://www.diveintopython.net/) -* [Ideas for Python Projects](http://pythonpracticeprojects.com) - -* [The Official Docs](http://docs.python.org/3/) -* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/) -* [Python Module of the Week](http://pymotw.com/3/) -* [A Crash Course in Python for Scientists](http://nbviewer.ipython.org/5920182) - -### 书籍(也是英文) - -* [Programming Python](http://www.amazon.com/gp/product/0596158106/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596158106&linkCode=as2&tag=homebits04-20) -* [Dive Into Python](http://www.amazon.com/gp/product/1441413022/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441413022&linkCode=as2&tag=homebits04-20) -* [Python Essential Reference](http://www.amazon.com/gp/product/0672329786/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0672329786&linkCode=as2&tag=homebits04-20) - diff --git a/zh-cn/pythonlegacy-cn.html.markdown b/zh-cn/pythonlegacy-cn.html.markdown new file mode 100644 index 00000000..f8aa2332 --- /dev/null +++ b/zh-cn/pythonlegacy-cn.html.markdown @@ -0,0 +1,476 @@ +--- +language: Python 2 (legacy) +contributors: + - ["Louie Dinh", "http://ldinh.ca"] +translators: + - ["Chenbo Li", "http://binarythink.net"] +filename: learnpythonlegacy-zh.py +lang: zh-cn +--- + +Python 由 Guido Van Rossum 在90年代初创建。 它现在是最流行的语言之一 +我喜爱python是因为它有极为清晰的语法,甚至可以说,它就是可以执行的伪代码 + +很欢迎来自您的反馈,你可以在[@louiedinh](http://twitter.com/louiedinh) 和 louiedinh [at] [google's email service] 这里找到我 + +注意: 这篇文章针对的版本是Python 2.7,但大多也可使用于其他Python 2的版本 +如果是Python 3,请在网络上寻找其他教程 + +```python + +# 单行注释 +""" 多行字符串可以用 + 三个引号包裹,不过这也可以被当做 + 多行注释 +""" + +#################################################### +## 1. 原始数据类型和操作符 +#################################################### + +# 数字类型 +3 # => 3 + +# 简单的算数 +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7 + +# 整数的除法会自动取整 +5 / 2 # => 2 + +# 要做精确的除法,我们需要引入浮点数 +2.0 # 浮点数 +11.0 / 4.0 # => 2.75 精确多了 + +# 括号具有最高优先级 +(1 + 3) * 2 # => 8 + +# 布尔值也是基本的数据类型 +True +False + +# 用 not 来取非 +not True # => False +not False # => True + +# 相等 +1 == 1 # => True +2 == 1 # => False + +# 不等 +1 != 1 # => False +2 != 1 # => True + +# 更多的比较操作符 +1 < 10 # => True +1 > 10 # => False +2 <= 2 # => True +2 >= 2 # => True + +# 比较运算可以连起来写! +1 < 2 < 3 # => True +2 < 3 < 2 # => False + +# 字符串通过 " 或 ' 括起来 +"This is a string." +'This is also a string.' + +# 字符串通过加号拼接 +"Hello " + "world!" # => "Hello world!" + +# 字符串可以被视为字符的列表 +"This is a string"[0] # => 'T' + +# % 可以用来格式化字符串 +"%s can be %s" % ("strings", "interpolated") + +# 也可以用 format 方法来格式化字符串 +# 推荐使用这个方法 +"{0} can be {1}".format("strings", "formatted") +# 也可以用变量名代替数字 +"{name} wants to eat {food}".format(name="Bob", food="lasagna") + +# None 是对象 +None # => None + +# 不要用相等 `==` 符号来和None进行比较 +# 要用 `is` +"etc" is None # => False +None is None # => True + +# 'is' 可以用来比较对象的相等性 +# 这个操作符在比较原始数据时没多少用,但是比较对象时必不可少 + +# None, 0, 和空字符串都被算作 False +# 其他的均为 True +0 == False # => True +"" == False # => True + + +#################################################### +## 2. 变量和集合 +#################################################### + +# 很方便的输出 +print "I'm Python. Nice to meet you!" + + +# 给变量赋值前不需要事先声明 +some_var = 5 # 一般建议使用小写字母和下划线组合来做为变量名 +some_var # => 5 + +# 访问未赋值的变量会抛出异常 +# 可以查看控制流程一节来了解如何异常处理 +some_other_var # 抛出 NameError + +# if 语句可以作为表达式来使用 +"yahoo!" if 3 > 2 else 2 # => "yahoo!" + +# 列表用来保存序列 +li = [] +# 可以直接初始化列表 +other_li = [4, 5, 6] + +# 在列表末尾添加元素 +li.append(1) # li 现在是 [1] +li.append(2) # li 现在是 [1, 2] +li.append(4) # li 现在是 [1, 2, 4] +li.append(3) # li 现在是 [1, 2, 4, 3] +# 移除列表末尾元素 +li.pop() # => 3 li 现在是 [1, 2, 4] +# 重新加进去 +li.append(3) # li is now [1, 2, 4, 3] again. + +# 像其他语言访问数组一样访问列表 +li[0] # => 1 +# 访问最后一个元素 +li[-1] # => 3 + +# 越界会抛出异常 +li[4] # 抛出越界异常 + +# 切片语法需要用到列表的索引访问 +# 可以看做数学之中左闭右开区间 +li[1:3] # => [2, 4] +# 省略开头的元素 +li[2:] # => [4, 3] +# 省略末尾的元素 +li[:3] # => [1, 2, 4] + +# 删除特定元素 +del li[2] # li 现在是 [1, 2, 3] + +# 合并列表 +li + other_li # => [1, 2, 3, 4, 5, 6] - 并不会改变这两个列表 + +# 通过拼接来合并列表 +li.extend(other_li) # li 是 [1, 2, 3, 4, 5, 6] + +# 用 in 来返回元素是否在列表中 +1 in li # => True + +# 返回列表长度 +len(li) # => 6 + + +# 元组类似于列表,但它是不可改变的 +tup = (1, 2, 3) +tup[0] # => 1 +tup[0] = 3 # 类型错误 + +# 对于大多数的列表操作,也适用于元组 +len(tup) # => 3 +tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) +tup[:2] # => (1, 2) +2 in tup # => True + +# 你可以将元组解包赋给多个变量 +a, b, c = (1, 2, 3) # a 是 1,b 是 2,c 是 3 +# 如果不加括号,将会被自动视为元组 +d, e, f = 4, 5, 6 +# 现在我们可以看看交换两个数字是多么容易的事 +e, d = d, e # d 是 5,e 是 4 + + +# 字典用来储存映射关系 +empty_dict = {} +# 字典初始化 +filled_dict = {"one": 1, "two": 2, "three": 3} + +# 字典也用中括号访问元素 +filled_dict["one"] # => 1 + +# 把所有的键保存在列表中 +filled_dict.keys() # => ["three", "two", "one"] +# 键的顺序并不是唯一的,得到的不一定是这个顺序 + +# 把所有的值保存在列表中 +filled_dict.values() # => [3, 2, 1] +# 和键的顺序相同 + +# 判断一个键是否存在 +"one" in filled_dict # => True +1 in filled_dict # => False + +# 查询一个不存在的键会抛出 KeyError +filled_dict["four"] # KeyError + +# 用 get 方法来避免 KeyError +filled_dict.get("one") # => 1 +filled_dict.get("four") # => None +# get 方法支持在不存在的时候返回一个默认值 +filled_dict.get("one", 4) # => 1 +filled_dict.get("four", 4) # => 4 + +# setdefault 是一个更安全的添加字典元素的方法 +filled_dict.setdefault("five", 5) # filled_dict["five"] 的值为 5 +filled_dict.setdefault("five", 6) # filled_dict["five"] 的值仍然是 5 + + +# 集合储存无顺序的元素 +empty_set = set() +# 初始化一个集合 +some_set = set([1, 2, 2, 3, 4]) # some_set 现在是 set([1, 2, 3, 4]) + +# Python 2.7 之后,大括号可以用来表示集合 +filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4} + +# 向集合添加元素 +filled_set.add(5) # filled_set 现在是 {1, 2, 3, 4, 5} + +# 用 & 来计算集合的交 +other_set = {3, 4, 5, 6} +filled_set & other_set # => {3, 4, 5} + +# 用 | 来计算集合的并 +filled_set | other_set # => {1, 2, 3, 4, 5, 6} + +# 用 - 来计算集合的差 +{1, 2, 3, 4} - {2, 3, 5} # => {1, 4} + +# 用 in 来判断元素是否存在于集合中 +2 in filled_set # => True +10 in filled_set # => False + + +#################################################### +## 3. 控制流程 +#################################################### + +# 新建一个变量 +some_var = 5 + +# 这是个 if 语句,在 python 中缩进是很重要的。 +# 下面的代码片段将会输出 "some var is smaller than 10" +if some_var > 10: + print "some_var is totally bigger than 10." +elif some_var < 10: # 这个 elif 语句是不必须的 + print "some_var is smaller than 10." +else: # 这个 else 也不是必须的 + print "some_var is indeed 10." + + +""" +用for循环遍历列表 +输出: + dog is a mammal + cat is a mammal + mouse is a mammal +""" +for animal in ["dog", "cat", "mouse"]: + # 你可以用 % 来格式化字符串 + print "%s is a mammal" % animal + +""" +`range(number)` 返回从0到给定数字的列表 +输出: + 0 + 1 + 2 + 3 +""" +for i in range(4): + print i + +""" +while 循环 +输出: + 0 + 1 + 2 + 3 +""" +x = 0 +while x < 4: + print x + x += 1 # x = x + 1 的简写 + +# 用 try/except 块来处理异常 + +# Python 2.6 及以上适用: +try: + # 用 raise 来抛出异常 + raise IndexError("This is an index error") +except IndexError as e: + pass # pass 就是什么都不做,不过通常这里会做一些恢复工作 + + +#################################################### +## 4. 函数 +#################################################### + +# 用 def 来新建函数 +def add(x, y): + print "x is %s and y is %s" % (x, y) + return x + y # 通过 return 来返回值 + +# 调用带参数的函数 +add(5, 6) # => 输出 "x is 5 and y is 6" 返回 11 + +# 通过关键字赋值来调用函数 +add(y=6, x=5) # 顺序是无所谓的 + +# 我们也可以定义接受多个变量的函数,这些变量是按照顺序排列的 +def varargs(*args): + return args + +varargs(1, 2, 3) # => (1,2,3) + + +# 我们也可以定义接受多个变量的函数,这些变量是按照关键字排列的 +def keyword_args(**kwargs): + return kwargs + +# 实际效果: +keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} + +# 你也可以同时将一个函数定义成两种形式 +def all_the_args(*args, **kwargs): + print args + print kwargs +""" +all_the_args(1, 2, a=3, b=4) prints: + (1, 2) + {"a": 3, "b": 4} +""" + +# 当调用函数的时候,我们也可以进行相反的操作,把元组和字典展开为参数 +args = (1, 2, 3, 4) +kwargs = {"a": 3, "b": 4} +all_the_args(*args) # 等价于 foo(1, 2, 3, 4) +all_the_args(**kwargs) # 等价于 foo(a=3, b=4) +all_the_args(*args, **kwargs) # 等价于 foo(1, 2, 3, 4, a=3, b=4) + +# 函数在 python 中是一等公民 +def create_adder(x): + def adder(y): + return x + y + return adder + +add_10 = create_adder(10) +add_10(3) # => 13 + +# 匿名函数 +(lambda x: x > 2)(3) # => True + +# 内置高阶函数 +map(add_10, [1, 2, 3]) # => [11, 12, 13] +filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7] + +# 可以用列表方法来对高阶函数进行更巧妙的引用 +[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] +[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] + +#################################################### +## 5. 类 +#################################################### + +# 我们新建的类是从 object 类中继承的 +class Human(object): + + # 类属性,由所有类的对象共享 + species = "H. sapiens" + + # 基本构造函数 + def __init__(self, name): + # 将参数赋给对象成员属性 + self.name = name + + # 成员方法,参数要有 self + def say(self, msg): + return "%s: %s" % (self.name, msg) + + # 类方法由所有类的对象共享 + # 这类方法在调用时,会把类本身传给第一个参数 + @classmethod + def get_species(cls): + return cls.species + + # 静态方法是不需要类和对象的引用就可以调用的方法 + @staticmethod + def grunt(): + return "*grunt*" + + +# 实例化一个类 +i = Human(name="Ian") +print i.say("hi") # 输出 "Ian: hi" + +j = Human("Joel") +print j.say("hello") # 输出 "Joel: hello" + +# 访问类的方法 +i.get_species() # => "H. sapiens" + +# 改变共享属性 +Human.species = "H. neanderthalensis" +i.get_species() # => "H. neanderthalensis" +j.get_species() # => "H. neanderthalensis" + +# 访问静态变量 +Human.grunt() # => "*grunt*" + + +#################################################### +## 6. 模块 +#################################################### + +# 我们可以导入其他模块 +import math +print math.sqrt(16) # => 4.0 + +# 我们也可以从一个模块中导入特定的函数 +from math import ceil, floor +print ceil(3.7) # => 4.0 +print floor(3.7) # => 3.0 + +# 从模块中导入所有的函数 +# 警告:不推荐使用 +from math import * + +# 简写模块名 +import math as m +math.sqrt(16) == m.sqrt(16) # => True + +# Python的模块其实只是普通的python文件 +# 你也可以创建自己的模块,并且导入它们 +# 模块的名字就和文件的名字相同 + +# 也可以通过下面的方法查看模块中有什么属性和方法 +import math +dir(math) + + +``` + +## 更多阅读 + +希望学到更多?试试下面的链接: + +* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/) +* [Dive Into Python](http://www.diveintopython.net/) +* [The Official Docs](http://docs.python.org/2.6/) +* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/) +* [Python Module of the Week](http://pymotw.com/2/) diff --git a/zh-cn/qt-cn.html.markdown b/zh-cn/qt-cn.html.markdown new file mode 100644 index 00000000..1d24f200 --- /dev/null +++ b/zh-cn/qt-cn.html.markdown @@ -0,0 +1,160 @@ +--- +category: tool +tool: Qt Framework +language: C++ +filename: learnqt-cn.cpp +contributors: + - ["Aleksey Kholovchuk", "https://github.com/vortexxx192"] +translators: + - ["GengchenXU", "https://github.com/GengchenXU"] +lang: zh-cn + +--- + +**Qt** Qt是一个广为人知的框架,用于开发跨平台软件,该软件可以在各种软件和硬件平台上运行,代码几乎没有变化,同时具有本机应用程序的能力和速度。虽然**Qt**最初是用*C*++,但也有其他语言的端口: *[PyQt](https://learnxinyminutes.com/docs/pyqt/)*, *QtRuby*, *PHP-Qt*, 等等. + +**Qt** 非常适合使用图形用户界面 (GUI) 创建应用程序。本教程是关于如何用*C++*去实现。 + +```c++ +/* + * 让我们从最经典的开始 + */ + +// Qt框架的所有标头均以大写字母'Q'开头 +#include <QApplication> +#include <QLineEdit> + +int main(int argc, char *argv[]) { + // 创建一个对象来管理应用程序范围内的资源 + QApplication app(argc, argv); + + // 创建行编辑widgets并在屏幕上显示 + QLineEdit lineEdit("Hello world!"); + lineEdit.show(); + + // 启动应用程序的事件循环 + return app.exec(); +} +``` + +**Qt**与 GUI 相关的部分与*widgets*及其之间的*connection*有关。 + +[阅读更多有关widgets的信息](http://doc.qt.io/qt-5/qtwidgets-index.html) + +```c++ +/* + * 让我们创建一个标签和一个按钮。 + * 按下按钮时应显示一个标签。 + * Qt代码本身就可以说明问题。 + */ + +#include <QApplication> +#include <QDialog> +#include <QVBoxLayout> +#include <QPushButton> +#include <QLabel> + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + QDialog dialogWindow; + dialogWindow.show(); + + // 添加垂直布局 + QVBoxLayout layout; + dialogWindow.setLayout(&layout); + + QLabel textLabel("Thanks for pressing that button"); + layout.addWidget(&textLabel); + textLabel.hide(); + + QPushButton button("Press me"); + layout.addWidget(&button); + + // 按下按钮时显示隐藏标签 + QObject::connect(&button, &QPushButton::pressed, + &textLabel, &QLabel::show); + + return app.exec(); +} +``` + +注意,*QObject :: connect*部分。 此方法用于将一个对象的*SIGNAL*连接到另一个对象的*SLOTS*。 + +**Signals** 会被发出当对象发生某些事情时,例如当用户按下QPushButton对象时会发出*push*的信号。 + +**Slots** 是可以响应于接收到的信号而执行的*action*。 + +[阅读有关SLOTS和SIGNALS的更多信息](http://doc.qt.io/qt-5/signalsandslots.html) + + +接下来,让我们了解到我们不仅可以使用标准的wigets,而且可以通过继承扩展其行为。 让我们创建一个按钮并计算其被按下的次数。 为此,我们定义了自己的类* CounterLabel *。 由于特定的Qt体系结构,必须在单独的文件中声明它。 + +```c++ +// counterlabel.hpp + +#ifndef COUNTERLABEL +#define COUNTERLABEL + +#include <QLabel> + +class CounterLabel : public QLabel { + Q_OBJECT // 在每个自定义wiget中必须存在的Qt定义的宏 + +public: + CounterLabel() : counter(0) { + setText("Counter has not been increased yet"); // QLabel方法 + } + +public slots: + // 将响应按钮按下而调用的操作 + void increaseCounter() { + setText(QString("Counter value: %1").arg(QString::number(++counter))); + } + +private: + int counter; +}; + +#endif // COUNTERLABEL +``` + +```c++ +// main.cpp +// 与前面的示例几乎相同 + +#include <QApplication> +#include <QDialog> +#include <QVBoxLayout> +#include <QPushButton> +#include <QString> +#include "counterlabel.hpp" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + QDialog dialogWindow; + dialogWindow.show(); + + QVBoxLayout layout; + dialogWindow.setLayout(&layout); + + CounterLabel counterLabel; + layout.addWidget(&counterLabel); + + QPushButton button("Push me once more"); + layout.addWidget(&button); + QObject::connect(&button, &QPushButton::pressed, + &counterLabel, &CounterLabel::increaseCounter); + + return app.exec(); +} +``` + +当然,Qt框架比本教程介绍的部分要复杂得多,因此请仔细阅读和练习。 + +## 进一步阅读 +- [Qt 4.8 tutorials](http://doc.qt.io/qt-4.8/tutorials.html) +- [Qt 5 tutorials](http://doc.qt.io/qt-5/qtexamplesandtutorials.html) + +祝你好运,生活愉快! diff --git a/zh-cn/racket-cn.html.markdown b/zh-cn/racket-cn.html.markdown index 8ef3671f..b373e1d9 100644 --- a/zh-cn/racket-cn.html.markdown +++ b/zh-cn/racket-cn.html.markdown @@ -444,7 +444,7 @@ n ; => 6 (set-box! n* (add1 (unbox n*))) (unbox n*) ; => 6 -;; 很多 Racket 诗句类型是不可变的 (对,列表,等),有一些既是可变的 +;; 很多 Racket 数据类型是不可变的 (对,列表,等),有一些既是可变的 ;; 又是不可变的 (字符串,向量,散列表 ;; 等...) diff --git a/zh-cn/raylib-cn.html.markdown b/zh-cn/raylib-cn.html.markdown new file mode 100644 index 00000000..c88aa91e --- /dev/null +++ b/zh-cn/raylib-cn.html.markdown @@ -0,0 +1,147 @@ +--- +category: tool +tool: raylib +lang: zh-cn +filename: learnraylib-cn.c +contributors: + - ["Nikolas Wipper", "https://notnik.cc"] +translators: + - ["lzw-723", "https://github.com/lzw-723"] +--- + +**raylib** 是一个跨平台、易用的图形库,围绕OpenGL 1.1、2.1、3.3和OpenGL ES 2.0构建。 +虽然它是用C语言编写的,却有超过50种不同语言的绑定。本教程将使用C语言。 +更确切地说,是C99。 + +```c +#include <raylib.h> + +int main(void) +{ + const int screenWidth = 800; + const int screenHeight = 450; + + // 在初始化raylib之前,可以设置标志位 + SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); + + // raylib并不要求我们存储任何实例结构 + // 目前raylib一次只能处理一个窗口 + InitWindow(screenWidth, screenHeight, "MyWindow"); + + // 设置我们的游戏以每秒60帧的速度运行 + SetTargetFPS(60); + + // 设置一个关闭窗口的键。 + //可以是0,表示没有键 + SetExitKey(KEY_DELETE); + + // raylib定义了两种类型的相机。Camera3D和Camera2D + // Camera是Camera3D的一个类型化定义 + Camera camera = { + .position = {0.0f, 0.0f, 0.0f}, + .target = {0.0f, 0.0f, 1.0f}, + .up = {0.0f, 1.0f, 0.0f}, + .fovy = 70.0f, + .type = CAMERA_PERSPECTIVE + }; + + + // raylib支持加载各种不同的文件格式的模型、动画、图像和声音。 + Model myModel = LoadModel("my_model.obj"); + Font someFont = LoadFont("some_font.ttf"); + + // 创建一个100x100的渲染纹理 + RenderTexture renderTexture = LoadRenderTexture(100, 100); + + // WindowShouldClose方法检查用户是否正在关闭窗口。 + // 可能用的是快捷方式、窗口控制或之前设置的关闭窗口键 + while (!WindowShouldClose()) + { + + // BeginDrawing方法要在任何绘图操作之前被调用。 + BeginDrawing(); + { + + // 为背景设定某种颜色 + ClearBackground(BLACK); + + if (IsKeyDown(KEY_SPACE)) + DrawCircle(400, 400, 30, GREEN); + + // 简单地绘制文本 + DrawText("Congrats! You created your first window!", + 190, // x + 200, // y + 20, // 字体大小 + LIGHTGRAY + ); + + // 大多数函数都有几个版本 + // 通常后缀为Ex, Pro, V + // 或者是Rec、Wires(仅适用于3D)、Lines(仅适用于2D)。 + DrawTextEx(someFont, + "Text in another font", + (Vector2) {10, 10}, + 20, // 字体大小 + 2, // 间距 + LIGHTGRAY); + + // 绘制3D时需要,有2D的等价方法 + BeginMode3D(camera); + { + + DrawCube((Vector3) {0.0f, 0.0f, 3.0f}, + 1.0f, 1.0f, 1.0f, RED); + + // 绘图时的白色色调将保持原来的颜色 + DrawModel(myModel, (Vector3) {0.0f, 0.0f, 3.0f}, + 1.0f, // 缩放 + WHITE); + + } + // 结束3D模式,这样就可以再次普通绘图 + EndMode3D(); + + // 开始在渲染纹理上绘图 + BeginTextureMode(renderTexture); + { + + // 它的行为与刚才调用的`BeginDrawing()`方法相同 + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + { + + DrawGrid(10, // Slices + 1.0f // 间距 + ); + + } + EndMode3D(); + + } + EndTextureMode(); + + // 渲染有Texture2D字段的纹理 + DrawTexture(renderTexture.texture, 40, 378, BLUE); + + } + EndDrawing(); + } + + // 卸载已载入的对象 + UnloadFont(someFont); + UnloadModel(myModel); + + // 关闭窗口和OpenGL上下文 + CloseWindow(); + + return 0; +} + +``` + +## 延伸阅读 +raylib有一些[不错的例子](https://www.raylib.com/examples.html) +如果你不喜欢C语言你也可以看看[raylib的其他语言绑定](https://github.com/raysan5/raylib/blob/master/BINDINGS.md) diff --git a/zh-cn/red-cn.html.markdown b/zh-cn/red-cn.html.markdown index 85812990..7fee007e 100644 --- a/zh-cn/red-cn.html.markdown +++ b/zh-cn/red-cn.html.markdown @@ -150,11 +150,11 @@ msg: either a > 0 [ ] print ["a 是 " msg lf] -; util +; until ; 循环执行一段代码块,直到满足给定的条件为止。 -; util 没有返回值,所以它不能用在表示式中。 +; UNTIL 在最后一次循环执行完最后一个表达式后,总是返回真值 'true'。 c: 5 -util [ +until [ prin "o" c: c - 1 c = 0 ; 终止循环的条件 @@ -193,15 +193,15 @@ print twice b ; 输出 6 ## 更进一步 -Red 相关的源码信息在 [Red 语言主页](http://www.red-lang.org)。 +Red 相关的源码信息在 [Red 语言主页](https://www.red-lang.org)。 源代码的 [github 库](https://github.com/red/red)。 -Red/System 特性在 [这里](http://static.red-lang.org/red-system-specs-light.html)。 +Red/System 特性在 [这里](https://static.red-lang.org/red-system-specs-light.html)。 想要了解更多关于 Rebol 和 Red 的信息,加入 [Gitter 聊天室](https://gitter.im/red/red)。如果你无法加入,也可以给我们发[邮件](mailto:red-langNO_SPAM@googlegroups.com)。 -也可以在 [Stack Overflow](stackoverflow.com/questions/tagged/red) 上查阅、提交问题。 +也可以在 [Stack Overflow](https://stackoverflow.com/questions/tagged/red) 上查阅、提交问题。 也许你现在就要试一试 Red ?可以在线尝试 [try Rebol and Red site](http://tryrebol.esperconsultancy.nl)。 diff --git a/zh-cn/ruby-cn.html.markdown b/zh-cn/ruby-cn.html.markdown index 9918c022..b0b8b58c 100644 --- a/zh-cn/ruby-cn.html.markdown +++ b/zh-cn/ruby-cn.html.markdown @@ -430,7 +430,7 @@ def guests(*array) array.each { |guest| puts guest } end -# 结构 +# 解构 # 如果函数返回一个数组,在赋值时可以进行拆分: def foods @@ -449,7 +449,7 @@ end best *ranked_competitors.first(3) #=> Winners are John, Sally, and Dingus. -# 结构操作符也可放在参数里面 +# 解构操作符也可放在参数里面 def best(first, second, third, *others) puts "Winners are #{first}, #{second}, and #{third}." puts "There were #{others.count} other participants." @@ -657,4 +657,4 @@ Something.new.qux # => 'qux' - [Ruby from other languages](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/) - [Programming Ruby](http://www.amazon.com/Programming-Ruby-1-9-2-0-Programmers/dp/1937785491/) - An older [free edition](http://ruby-doc.com/docs/ProgrammingRuby/) is available online. - [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide) - A community-driven Ruby coding style guide. -- [Try Ruby](http://tryruby.org) - Learn the basic of Ruby programming language, interactive in the browser. +- [Try Ruby](https://try.ruby-lang.org/) - Learn the basic of Ruby programming language, interactive in the browser. diff --git a/zh-cn/rust-cn.html.markdown b/zh-cn/rust-cn.html.markdown index 5d3fc693..6bed1650 100644 --- a/zh-cn/rust-cn.html.markdown +++ b/zh-cn/rust-cn.html.markdown @@ -1,5 +1,5 @@ --- -language: rust +language: Rust contributors: - ["P1start", "http://p1start.github.io/"] translators: @@ -211,7 +211,7 @@ fn main() { // `for` 循环 let array = [1, 2, 3]; - for i in array.iter() { + for i in array { println!("{}", i); } diff --git a/zh-cn/set-theory-cn.html.markdown b/zh-cn/set-theory-cn.html.markdown new file mode 100644 index 00000000..13ba2c80 --- /dev/null +++ b/zh-cn/set-theory-cn.html.markdown @@ -0,0 +1,138 @@ +--- +category: Algorithms & Data Structures +name: Set theory +contributors: +translators: + - ["Tianchen Xu", "https://github.com/lo0b0o"] +lang: zh-cn +--- +集合论是数学的一个分支,研究集合、它们的运算和它们的性质。 + +* 集合由不重复的项组成。 + +## 基本符号 + +### 运算符 +* 并运算符,`∪`,表示“或”; +* 交运算符,`∩`,表示“且”; +* 差运算符,`\`,表示“不包括”; +* 补运算符,`'`,表示补集; +* 叉积运算符,`×`,表示笛卡尔积。 + +### 限定词 +* 冒号限定词,`:`,表示“使得”; +* 从属限定词,`∈`,表示“属于”; +* 子集限定词,`⊆`,表示“是……的子集”; +* 真子集限定词,`⊂`,表示“是……的真子集”。 + +### 重要的集合 +* `∅`,空集,即不包含任何元素的集合; +* `ℕ`,自然数集; +* `ℤ`,整数集; +* `ℚ`,有理数集; +* `ℝ`,实数集。 + +关于以上集合,有如下几点需要注意: +1. 空集是其本身的子集(并且也是任何其他集合的子集),即便空集不包含任何项; +2. 数学家们对于零是否为自然数的看法通常并不统一,教科书一般会明确说明作者是否认为零是自然数。 + +### 基数 + +集合的基数,或者说大小,由该集合中的项目数量决定。基数运算符为 `|...|`。 + +例如,若 `S = { 1, 2, 4 }`,则 `|S| = 3`。 + +### 空集 + +* 可以在集合符号中使用不成立的条件来构造空集,例如,`∅ = { x : x ≠ x }`,或 `∅ = { x : x ∈ N, x < 0 }`; +* 空集总是唯一的(即,有且只有一个空集); +* 空集是所有集合的子集; +* 空集的基数为 0,即 `|∅| = 0`。 + +## 集合的表示 + +### 集合的逐项构造 + +集合可以通过包含其全部项的列表逐项生成。例如,`S = { a, b, c, d }`。 + +只要构成集合的项清楚,长列表可以用省略号缩短。例如,`E = { 2, 4, 6, 8, ... }` 显然为所有偶数构成的集合,它包含无穷多项,虽然我们只显式写出了其中四项。 + +### 集合构造器 + +集合构造器符号是构造集合的一种更具描述性的方式。它依赖于一个主语和一个谓词,使得 `S = { 主语 : 谓词 }`。 例如, + +``` +A = { x : x 是元音字母 } = { a, e, i, o, u, y} +B = { x : x ∈ N, x < 10 } = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } +C = { x : x = 2k, k ∈ N } = { 0, 2, 4, 6, 8, ... } +``` + +有时,谓词可能会 "漏 "到主语中,例如, + +``` +D = { 2x : x ∈ N } = { 0, 2, 4, 6, 8, ... } +``` + +## 关系 + +### 从属关系 + +* 如果值 `a` 包含在集合 `A` 中,那么我们说 `a` 属于 `A`,并用符号表示为 `a ∈ A`。 +* 如果值 `a` 不包含于集合 `A` 中,那么我们说 `a` 不属于 `A`,并用符号表示为 `a ∉ A`。 + +### 相等关系 + +* 如果两个集合包括相同的项,那么我们说这两个集合相等,例如,`A = B`。 +* 集合的相等关系于顺序无关,例如 `{ 1, 2, 3, 4 } = { 2, 3, 1, 4 }`。 +* 集合中的元素不能重复,例如 `{ 1, 2, 2, 3, 4, 3, 4, 2 } = { 1, 2, 3, 4 }`。 +* 集合 `A` 与 `B` 相等当且仅当 `A ⊆ B` 且 `B ⊆ A`。 + +## 特殊集合 + +### 幂集 + +* 令 `A` 为任意集合。幂集指的是包括了 `A` 的所有子集的集合,记作 `P(A)`。如果集合 `A` 由 `2n` 个元素组成,那么 `P(A)` 中有 `2^n` 个元素。 + +``` +P(A) = { x : x ⊆ A } +``` + +## 两个集合的运算 +### 并 + +给定集合 `A` 和 `B`,两个集合的并由出现在 `A` 或 `B` 中的项构成,记作 `A ∪ B`。 + +``` +A ∪ B = { x : x ∈ A ∪ x ∈ B } +``` + +### 交 + +给定集合 `A` 和 `B`,两个集合的交由出现在 `A` 和 `B` 中的项构成,记作 `A ∩ B`。 + +``` +A ∩ B = { x : x ∈ A, x ∈ B } +``` + +### 差 +给定集合 `A` 和 `B`,`A` 对于 `B` 的集合差指的是属于 `A` 但不属于 `B` 的每一项。 + +``` +A \ B = { x : x ∈ A, x ∉ B } +``` + +### 对称差 +给定集合 `A` 和 `B`,对称差指的是属于 `A` 或 `B` 但不属于它们交集的所有项。 + +``` +A △ B = { x : ((x ∈ A) ∩ (x ∉ B)) ∪ ((x ∈ B) ∩ (x ∉ A)) } + +A △ B = (A \ B) ∪ (B \ A) +``` + +### 笛卡尔积 +给定集合 `A` 和 `B`,`A` 和 `B` 的笛卡尔积由 `A` 和 `B` 的项的所有组合构成。 + +``` +A × B = { (x, y) | x ∈ A, y ∈ B } +``` diff --git a/zh-cn/solidity-cn.html.markdown b/zh-cn/solidity-cn.html.markdown index ec684997..12a532bc 100644 --- a/zh-cn/solidity-cn.html.markdown +++ b/zh-cn/solidity-cn.html.markdown @@ -213,7 +213,7 @@ names.length; // 获得数组长度 names.length = 1; // 可以设定长度(仅针对 storage 中的动态数组) // 多维数组 -uint x[][5]; // 5个动态数组元素的数组(和多数语言的顺序相反) +uint[][5] x; // 5个动态数组元素的数组(和多数语言的顺序相反) // 字典类型 (任一类型到其他类型的映射) mapping (string => uint) public balances; diff --git a/zh-cn/sql.html.markdown b/zh-cn/sql-cn.html.markdown index 9d430bd1..9d430bd1 100644 --- a/zh-cn/sql.html.markdown +++ b/zh-cn/sql-cn.html.markdown diff --git a/zh-cn/swift-cn.html.markdown b/zh-cn/swift-cn.html.markdown index 18bc52ed..c56a0d33 100644 --- a/zh-cn/swift-cn.html.markdown +++ b/zh-cn/swift-cn.html.markdown @@ -10,7 +10,7 @@ translators: lang: zh-cn --- -Swift 是 Apple 开发的用于 iOS 和 OS X 开发的编程语言。Swift 于2014年 Apple WWDC (全球开发者大会)中被引入,用以与 Objective-C 共存,同时对错误代码更具弹性。Swift 由 Xcode 6 beta 中包含的 LLVM 编译器编译。 +Swift 是 Apple 开发的用于 iOS 和 macOS 开发的编程语言。Swift 于2014年 Apple WWDC (全球开发者大会)中被引入,用以与 Objective-C 共存,同时对错误代码更具弹性。Swift 由 Xcode 6 beta 中包含的 LLVM 编译器编译。 Swift 的官方语言教程 [Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329) 可以从 iBooks 免费下载. diff --git a/zh-cn/visualbasic-cn.html.markdown b/zh-cn/visualbasic-cn.html.markdown index e30041b3..cda7b864 100644 --- a/zh-cn/visualbasic-cn.html.markdown +++ b/zh-cn/visualbasic-cn.html.markdown @@ -8,7 +8,7 @@ lang: zh-cn filename: learnvisualbasic-cn.vb --- -``` +```visualbasic Module Module1 Sub Main() diff --git a/zh-cn/wolfram-cn.html.markdown b/zh-cn/wolfram-cn.html.markdown new file mode 100644 index 00000000..64990234 --- /dev/null +++ b/zh-cn/wolfram-cn.html.markdown @@ -0,0 +1,140 @@ +--- +language: wolfram +contributors: + - ["hyphz", "http://github.com/hyphz/"] +translators: + - ["wuyudi", "http://github.com/wuyudi/"] +filename: learnwolfram-cn.nb +lang: zh-cn +--- + +Wolfram 语言是最初在 Mathematica 中使用的底层语言,但现在可用于多种场合。 + +Wolfram 语言有几个界面。 + +- Raspberry Pi 上的命令行内核界面(就叫 _The Wolfram Language_),它是交互式运行的,不能产生图形输入。 +- _Mathematica_ 是一个丰富的文本/数学编辑器,内置交互式的 Wolfram: 在 "代码单元 "上按 shift+Return 键可以创建一个输出单元,输出结果,这不是动态的。 +- _Wolfram Workbench_,这是 Eclipse 与 Wolfram 语言后端的界面。 + +本例中的代码可以在任何界面中输入,并使用 Wolfram Workbench 进行编辑。直接加载到 Mathematica 中可能会很不方便,因为该文件不包含单元格格式化信息(这将使该文件作为文本阅读时变得一团糟)--它可以被查看/编辑,但可能需要一些设置。 + +```mathematica +(* 这是一个注释 *) + +(* 在Mathematica中,您可以创建一个文本单元格,用排版好的文本和图像来注释您的代码,而不是使用这些注释 *) + +(* 输入一个表达式返回结果 *) +2*2 (* 4 *) +5+8 (* 13 *) + +(* 调用函数 *) +(* 注意,函数名(和其他所有东西)是区分大小写的 *) +Sin[Pi/2] (* 1 *) + +(* 带有一个参数的函数调用的替代语法 *) +Sin@(Pi/2) (* 1 *) +(Pi/2) // Sin (* 1 *) + +(* WL 中的每一种语法都有一些等价的函数调用,即万物皆函数 *) +Times[2, 2] (* 4 *) +Plus[5, 8] (* 13 *) + +(* 第一次使用一个变量定义它,并使其成为全局变量 *) +x = 5 (* 5 *) +x == 5 (* 返回真,C-style 的赋值和相等测试 *) +x (* 5 *) +x = x + 5 (* 10 *) +x (* 10 *) +Set[x, 20] (* 当我说万物皆函数时 我是认真的 *) +x (* 20 *) + +(* 因为WL是基于计算机代数系统的, *) +(* 可以使用未定义的变量,它们只是阻碍了计算 *) +cow + 5 (* 5 + cow, cow 是未定义的,所以无法进一步计算 *) +cow + 5 + 10 (* 15 + cow, 它将尽力计算 *) +% (* 15 + cow, % 获取最后一次返回的内容 *) +% - cow (* 15, 未定义的变量cow被消去 *) +moo = cow + 5 (* 小心,moo 现在是一个表达式,不是一个数字! *) + +(* 定义一个函数 *) +Double[x_] := x * 2 (* 注意 := 防止对RHS进行即时计算。 + 而 x 后面的 _ 表示没有模式匹配的约束条件*) +Double[10] (* 20 *) +Double[Sin[Pi/2]] (* 2 *) +Double @ Sin @ (Pi/2) (* 2, @-syntax 避免了闭括号的队列 *) +(Pi/2) // Sin // Double(* 2, //-syntax 按执行顺序列举了函数 *) + +(* 对于命令式编程,使用 ; 来分隔语句。 *) +(* 丢弃 LHS 的任何输出并运行 RHS *) +MyFirst[] := (Print@"Hello"; Print@"World") (* 请注意,外侧的小括号是关键 + ;的优先级低于:= *) +MyFirst[] (* 你好,世界 *) + +(* C-Style 的 For 循环,注:在 Mathematica 中使用 For 循环是低效的 *) +PrintTo[x_] := For[y=0, y<x, y++, (Print[y])] (* 开始,测试,自增,循环体 *) +PrintTo[5] (* 0 1 2 3 4 *) + +(* While 循环 *) +x = 0; While[x < 2, (Print@x; x++)] (* 带测试和程序体的 while 循环 *) + +(* If 和 条件 *) +x = 8; If[x==8, Print@"Yes", Print@"No"] (* Condition, true case, else case *) +Switch[x, 2, Print@"Two", 8, Print@"Yes"] (* 值匹配风格切换 *) +Which[x==2, Print@"No", x==8, Print@"Yes"] (* Elif风格切换 *) + +(* 除参数外的变量默认为全局变量,即使在函数内部也是如此 *) +y = 10 (* 10, 全局变量 y *) +PrintTo[5] (* 0 1 2 3 4 *) +y (* 5, 全局的 y 被 PrintTo 内的循环计数器所占用 *) +x = 20 (* 20, 全局变量 x *) +PrintTo[5] (* 0 1 2 3 4 *) +x (* 20, PrintTo 中的 x 是一个参数,并自动局部化 *) + +(* 局部变量使用 Module 元函数声明 *) +(* 带本地变量的版本 *) +BetterPrintTo[x_] := Module[{y}, (For[y=0, y<x, y++, (Print@y)])] +y = 20 (* 全局变量 y *) +BetterPrintTo[5] (* 0 1 2 3 4 *) +y (* 20, 那更好 *) + +(* 实际上,模块允许我们声明任何我们喜欢的作用域 *) +Module[{count}, count=0; (* 声明这个变量count的作用域 *) + (IncCount[] := ++count); (* 这些函数都在该作用域内 *) + (DecCount[] := --count)] +count (* count - 全局变量 count 未定义 *) +IncCount[] (* 1, 使用作用域内的counter变量 *) +IncCount[] (* 2, incCount 升级了它 *) +DecCount[] (* 1, decCount 也是 *) +count (* count - 这个名字依旧不是全局变量 *) + +(* Lists *) +myList = {1, 2, 3, 4} (* {1, 2, 3, 4} *) +myList[[1]] (* 1 - 注意列表索引从 1 开始,而不是从 0 开始。 *) +Map[Double, myList] (* {2, 4, 6, 8} - 函数式列表的 map 函数 *) +Double /@ myList (* {2, 4, 6, 8} - 以上的缩略语法 *) +Scan[Print, myList] (* 1 2 3 4 - 命令式循环 *) +Fold[Plus, 0, myList] (* 10 (0+1+2+3+4) *) +FoldList[Plus, 0, myList] (* {0, 1, 3, 6, 10} - 存值的 Fold *) +Append[myList, 5] (* {1, 2, 3, 4, 5} - 注意 myList 没有更新 *) +Prepend[myList, 5] (* {5, 1, 2, 3, 4} - 如果想让 myList 更新,就加上 "myList = " *) +Join[myList, {3, 4}] (* {1, 2, 3, 4, 3, 4} *) +myList[[2]] = 5 (* {1, 5, 3, 4} - 这确实更新了myList *) + +(* 关联,又称字典/哈希值 *) +myHash = <|"Green" -> 2, "Red" -> 1|> (* 创建一个关联 *) +myHash[["Green"]] (* 2, 使用 *) +myHash[["Green"]] := 5 (* 5, 更新 *) +myHash[["Puce"]] := 3.5 (* 3.5, 扩展 *) +KeyDropFrom[myHash, "Green"] (* 抹去绿色的键 *) +Keys[myHash] (* {Red, Puce} *) +Values[myHash] (* {1, 3.5} *) + +(* 做 Wolfram 的演示时 你不能不展示这个 *) +Manipulate[y^2, {y, 0, 20}] (* 返回一个反应式的用户界面,显示y^2 + 并允许通过滑块在 0-20 之间调整y。 + 只适用于图形前端 *) +``` + +## 想多来点? + +- [Wolfram Language Documentation Center](http://reference.wolfram.com/language/) diff --git a/zh-cn/yaml-cn.html.markdown b/zh-cn/yaml-cn.html.markdown index de933d12..cfa22dfb 100644 --- a/zh-cn/yaml-cn.html.markdown +++ b/zh-cn/yaml-cn.html.markdown @@ -1,29 +1,31 @@ --- language: yaml contributors: - - ["Adam Brenecki", "https://github.com/adambrenecki"] + - ["Leigh Brenecki", "https://github.com/adambrenecki"] translators: - ["Zach Zhang", "https://github.com/checkcheckzz"] - ["Jiang Haiyun", "https://github.com/haiiiiiyun"] + - ["Wen Sun", "https://github.com/HermitSun"] filename: learnyaml-cn.yaml lang: zh-cn --- -YAML 是一个数据序列化语言,被设计成人类直接可写可读的。 +YAML 是一种数据序列化语言,旨在让人类直接可写可读。 -它是 JSON 的严格超集,增加了语法显著换行符和缩进,就像 Python。但和 Python 不一样, -YAML 根本不容许文字制表符。 +它是 JSON 的严格超集,增加了*在语法上有意义的*(syntactically significant)换行符和缩进,就像 Python 一样。但和 Python 的不同之处在于,YAML 不允许使用*文字制表符*(literal tab characters)来表示缩进。 ```yaml -# YAML 中的注解看起来像这样。 +--- # 文档开头 + +# YAML 中的注释看起来像这样。 ################ # 标量类型 # ################ -# 我们的根对象 (它们在整个文件里延续) 将会是一个映射, -# 它等价于在别的语言里的一个字典,哈希表或对象。 +# 我们的根对象 (贯穿整个文档的始终) 是一个映射(map), +# 它等价于其它语言中的一个字典(dictionary),哈希表(hash)或对象(object)。 key: value another_key: Another value goes here. a_number_value: 100 @@ -33,11 +35,16 @@ scientific_notation: 1e+12 boolean: true null_value: null key with spaces: value -# 注意到字符串不需要被括在引号中。但是,它们可以被括起来。 -"Keys can be quoted too.": "Useful if you want to put a ':' in your key." - -# 多行字符串既可以写成像一个'文字块'(使用 |), -# 或像一个'折叠块'(使用 '>')。 +# 注意,字符串可以不括在引号里。当然,也可以括在引号里。 +however: 'A string, enclosed in quotes.' +'Keys can be quoted too.': "Useful if you want to put a ':' in your key." +single quotes: 'have ''one'' escape pattern' +double quotes: "have many: \", \0, \t, \u263A, \x0d\x0a == \r\n, and more." +# UTF-8/16/32字符需要指明编码(通过\u)。 +Superscript two: \u00B2 + +# 多行字符串既可以写成一个'字面量块'(使用 '|'), +# 也可以写成一个'折叠块'(使用 '>')。 literal_block: | This entire block of text will be the value of the 'literal_block' key, with line breaks being preserved. @@ -60,7 +67,7 @@ folded_style: > # 集合类型 # #################### -# 嵌套是通过缩进完成的。推荐使用 2 个空格的缩进(但非必须) +# 嵌套是通过缩进完成的。推荐使用 2 个空格的缩进(但非必须)。 a_nested_map: key: value another_key: Another Value @@ -70,21 +77,22 @@ a_nested_map: # 映射的键不必是字符串。 0.25: a float key -# 键也可以是复合型的,比如多行对象 -# 我们用 ? 后跟一个空格来表示一个复合键的开始。 +# 键也可以是复合(complex)的,比如多行对象 +# 我们用 '?' 后跟一个空格来表示一个复合键的开始。 ? | - This is a key - that has multiple lines + This is a key + that has multiple lines : and this is its value # YAML 也允许使用复杂键语法表示序列间的映射关系。 -# 但有些语言的解析器可能会不支持。 +# 但有些解析器可能会不支持。 # 一个例子: ? - Manchester United - Real Madrid : [ 2001-01-01, 2002-02-02 ] -# 序列 (等价于列表或数组) 看起来像这样: +# 序列 (sequences,等价于列表 list 或数组 array ) 看起来像这样: +# 注意 '-' 也算缩进: a_sequence: - Item 1 - Item 2 @@ -95,6 +103,8 @@ a_sequence: - - This is a sequence - inside another sequence + - - - Nested sequence indicators + - can be collapsed # 因为 YAML 是 JSON 的超集,你也可以写 JSON 风格的映射和序列: json_map: {"key": "value"} @@ -105,7 +115,7 @@ and quotes are optional: {key: [3, 2, 1, takeoff]} # 其余的 YAML 特性 # ####################### -# YAML 还有一个方便的特性叫 '锚',它能让你很容易在文档中进行文本复用。 +# YAML 还有一个方便的特性叫“锚”(anchors)。你可以使用它在文档中轻松地完成文本复用。 # 如下两个键会有相同的值: anchored_content: &anchor_name This string will appear as the value of two keys. other_anchor: *anchor_name @@ -114,8 +124,8 @@ other_anchor: *anchor_name base: &base name: Everyone has same name -# The regexp << is called Merge Key Language-Independent Type. -# 它表明指定映射的所有键值会插入到当前的映射中。 +# '<<'称为语言无关的合并键类型(Merge Key Language-Independent Type). +# 它表明一个或多个指定映射中的所有键值会插入到当前的映射中。 foo: &foo <<: *base @@ -127,22 +137,22 @@ bar: &bar # foo 和 bar 将都含有 name: Everyone has same name -# YAML 还有标签,你可以用它显示地声明类型。 +# YAML 还有标签(tags),你可以用它显式地声明类型。 explicit_string: !!str 0.5 -# 一些解析器实现特定语言的标签,就像这个针对 Python 的复数类型。 +# 一些解析器实现了特定语言的标签,就像这个针对Python的复数类型的标签。 python_complex_number: !!python/complex 1+2j -# 我们也可以在 YAML 的复合键中使用特定语言的标签 +# 我们也可以在 YAML 的复合键中使用特定语言的标签: ? !!python/tuple [5, 7] : Fifty Seven -# 将会是 Python 中的 {(5, 7): 'Fifty Seven'} +# 将会是 Python 中的 {(5, 7): 'Fifty Seven'} #################### # 其余的 YAML 类型 # #################### -# 除了字符串和数字,YAML 还能理解其它标量。 -# ISO 格式的日期和日期时间文本也可以被解析。 +# 除了字符串和数字,YAML 还支持其它标量。 +# ISO 格式的日期和时间字面量也可以被解析。 datetime: 2001-12-15T02:59:43.1Z datetime_with_spaces: 2001-12-14 21:59:43.10 -5 date: 2002-12-14 @@ -155,20 +165,24 @@ gif_file: !!binary | +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= -# YAML 还有一个集合类型,它看起来像这样: +# YAML 还有一个集合(set)类型,它看起来像这样: set: - ? item1 - ? item2 - ? item3 + ? item1 + ? item2 + ? item3 +or: {item1, item2, item3} -# 集合只是值为 null 的映射;上面的集合等价于: +# 集合只是值均为 null 的映射;上面的集合等价于: set2: - item1: null - item2: null - item3: null + item1: null + item2: null + item3: null + +... # 文档结束 ``` ### 更多资源 + [YAML official website](http://yaml.org/) ++ [Online YAML Converter](http://yamlonline.com) + [Online YAML Validator](http://codebeautify.org/yaml-validator) diff --git a/zh-cn/zfs-cn.html.markdown b/zh-cn/zfs-cn.html.markdown index fdf5277e..80688fbe 100644 --- a/zh-cn/zfs-cn.html.markdown +++ b/zh-cn/zfs-cn.html.markdown @@ -288,6 +288,7 @@ tank/home/sarlalian@now 0 - 259M - tank/home/alice@now 0 - 156M - tank/home/bob@now 0 - 156M - ... +``` Destroy snapshots (删除快照) @@ -297,7 +298,6 @@ $ zfs destroy tank/home/sarlalian@now # 删除某一数据集及其子集的快照 $ zfs destroy -r tank/home/sarlalian@now - ``` Renaming Snapshots (重命名) |