From 818b8eec46b11b36b5235ecbce540557afec4687 Mon Sep 17 00:00:00 2001 From: Boris Verkhovskiy Date: Thu, 4 Apr 2024 00:27:01 -0700 Subject: Convert \r\n to \n --- zh-cn/c++-cn.html.markdown | 1144 ++++++++++++++++++++--------------------- zh-cn/elisp-cn.html.markdown | 690 ++++++++++++------------- zh-cn/matlab-cn.html.markdown | 1008 ++++++++++++++++++------------------ 3 files changed, 1421 insertions(+), 1421 deletions(-) (limited to 'zh-cn') 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 - -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 // 引入包含输入/输出流的头文件 - -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 ” - - cerr << "Used for error messages"; -} - -///////// -// 字符串 -///////// - -// C++中的字符串是对象,它们有很多成员函数 -#include - -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 - -// 声明一个类。 -// 类通常在头文件(.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 and weights " - - std::cout << "Dog is owned by " << owner << "\n"; - // "Dog is owned by " -} - -///////////////////// -// 初始化与运算符重载 -///////////////////// - -// 在C++中,通过定义一些特殊名称的函数, -// 你可以重载+、-、*、/等运算符的行为。 -// 当运算符被使用时,这些特殊函数会被调用,从而实现运算符重载。 - -#include -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 - -// 在_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 + +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 // 引入包含输入/输出流的头文件 + +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 ” + + cerr << "Used for error messages"; +} + +///////// +// 字符串 +///////// + +// C++中的字符串是对象,它们有很多成员函数 +#include + +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 + +// 声明一个类。 +// 类通常在头文件(.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 and weights " + + std::cout << "Dog is owned by " << owner << "\n"; + // "Dog is owned by " +} + +///////////////////// +// 初始化与运算符重载 +///////////////////// + +// 在C++中,通过定义一些特殊名称的函数, +// 你可以重载+、-、*、/等运算符的行为。 +// 当运算符被使用时,这些特殊函数会被调用,从而实现运算符重载。 + +#include +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 + +// 在_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/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/matlab-cn.html.markdown b/zh-cn/matlab-cn.html.markdown index ca08b36b..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/) -- cgit v1.2.3