diff options
Diffstat (limited to 'zh-cn')
-rw-r--r-- | zh-cn/asciidoc-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/awk-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/c++-cn.html.markdown | 1144 | ||||
-rw-r--r-- | zh-cn/c-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/cobol-cn.html.markdown | 192 | ||||
-rw-r--r-- | zh-cn/crystal-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/csharp-cn.html.markdown | 820 | ||||
-rw-r--r-- | zh-cn/elisp-cn.html.markdown | 690 | ||||
-rw-r--r-- | zh-cn/elixir-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/fortran-cn.html.markdown | 444 | ||||
-rw-r--r-- | zh-cn/fortran95-cn.html.markdown | 435 | ||||
-rw-r--r-- | zh-cn/go-cn.html.markdown | 10 | ||||
-rw-r--r-- | zh-cn/java-cn.html.markdown | 4 | ||||
-rw-r--r-- | zh-cn/json-cn.html.markdown | 43 | ||||
-rw-r--r-- | zh-cn/markdown-cn.html.markdown | 16 | ||||
-rw-r--r-- | zh-cn/matlab-cn.html.markdown | 1008 | ||||
-rw-r--r-- | zh-cn/nim-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/pyqt-cn.html.markdown | 6 | ||||
-rw-r--r-- | zh-cn/ruby-cn.html.markdown | 4 | ||||
-rw-r--r-- | zh-cn/visualbasic-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/wolfram-cn.html.markdown | 2 | ||||
-rw-r--r-- | zh-cn/zfs-cn.html.markdown | 2 |
22 files changed, 2803 insertions, 2031 deletions
diff --git a/zh-cn/asciidoc-cn.html.markdown b/zh-cn/asciidoc-cn.html.markdown index 74eed445..0709e8c1 100644 --- a/zh-cn/asciidoc-cn.html.markdown +++ b/zh-cn/asciidoc-cn.html.markdown @@ -1,6 +1,6 @@ --- language: asciidoc -filename: asciidoc-cn.md +filename: asciidoc-cn.adoc contributors: - ["Ryan Mavilia", "http://unoriginality.rocks/"] - ["Abel Salgado Romero", "https://twitter.com/abelsromero"] diff --git a/zh-cn/awk-cn.html.markdown b/zh-cn/awk-cn.html.markdown index bac833a6..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/c++-cn.html.markdown b/zh-cn/c++-cn.html.markdown index a6a61949..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 dd4445f7..851f2981 100644 --- a/zh-cn/c-cn.html.markdown +++ b/zh-cn/c-cn.html.markdown @@ -474,7 +474,7 @@ void testFunc() { // 用户自定义类型和结构 /////////////////////////////////////// -// Typedefs可以创建类型别名 +// typedef 可以创建类型别名 typedef int my_type; my_type my_type_var = 0; 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 0bad34ce..b6cc70c0 100644 --- a/zh-cn/csharp-cn.html.markdown +++ b/zh-cn/csharp-cn.html.markdown @@ -5,45 +5,64 @@ contributors: - ["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/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/go-cn.html.markdown b/zh-cn/go-cn.html.markdown index 0123c0a6..a2b71761 100644 --- a/zh-cn/go-cn.html.markdown +++ b/zh-cn/go-cn.html.markdown @@ -78,7 +78,7 @@ 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变量可以直接初始化。 @@ -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/java-cn.html.markdown b/zh-cn/java-cn.html.markdown index 1de7f3e6..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); 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/markdown-cn.html.markdown b/zh-cn/markdown-cn.html.markdown index 707d6927..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,7 +204,7 @@ GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以 ```md my_array.each do |item| - puts item + puts item end ``` @@ -216,12 +216,13 @@ 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/nim-cn.html.markdown b/zh-cn/nim-cn.html.markdown index dc662b1e..fa7d8259 100644 --- a/zh-cn/nim-cn.html.markdown +++ b/zh-cn/nim-cn.html.markdown @@ -3,7 +3,7 @@ language: Nim filename: learnNim-cn.nim contributors: - ["Jason J. Ayala P.", "http://JasonAyala.com"] - - ["Dennis Felsing", "http://felsin9.de/nnis/"] + - ["Dennis Felsing", "https://dennis.felsing.org"] translators: - ["lzw-723", "https://github.com/lzw-723"] lang: zh-cn diff --git a/zh-cn/pyqt-cn.html.markdown b/zh-cn/pyqt-cn.html.markdown index 55e5bbe3..04041c83 100644 --- a/zh-cn/pyqt-cn.html.markdown +++ b/zh-cn/pyqt-cn.html.markdown @@ -19,7 +19,7 @@ lang: zh-cn ```python import sys from PyQt4 import QtGui - + def window(): # 创建应用对象 app = QtGui.QApplication(sys.argv) @@ -44,7 +44,7 @@ if __name__ == '__main__': 为了运用 pyqt 中一些更高级的功能,我们需要开始学习使用其他控件。下文演示了如何弹出对话框,该对话框在用户确认操作或输入信息等情况下经常用到。 -```Python +```python import sys from PyQt4.QtGui import * from PyQt4.QtCore import * @@ -62,7 +62,7 @@ def window(): w.setWindowTitle("PyQt Dialog") w.show() sys.exit(app.exec_()) - + # 对话框窗口创建函数 # 当窗口中的按钮被点击时退出本程序 def showdialog(): diff --git a/zh-cn/ruby-cn.html.markdown b/zh-cn/ruby-cn.html.markdown index 63adab64..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." 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 index 9d6ef54f..64990234 100644 --- a/zh-cn/wolfram-cn.html.markdown +++ b/zh-cn/wolfram-cn.html.markdown @@ -18,7 +18,7 @@ Wolfram 语言有几个界面。 本例中的代码可以在任何界面中输入,并使用 Wolfram Workbench 进行编辑。直接加载到 Mathematica 中可能会很不方便,因为该文件不包含单元格格式化信息(这将使该文件作为文本阅读时变得一团糟)--它可以被查看/编辑,但可能需要一些设置。 -```mma +```mathematica (* 这是一个注释 *) (* 在Mathematica中,您可以创建一个文本单元格,用排版好的文本和图像来注释您的代码,而不是使用这些注释 *) 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 (重命名) |