mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-09-25 05:51:31 +02:00
Merge pull request #147 from lichenbo/master-cn
Chinese translated version of c and elisp tutorial
This commit is contained in:
394
zh-cn/c-cn.html.markdown
Executable file
394
zh-cn/c-cn.html.markdown
Executable file
@@ -0,0 +1,394 @@
|
||||
---
|
||||
language: c
|
||||
filename: learnc.c
|
||||
contributors:
|
||||
- ["Adam Bard", "http://adambard.com/"]
|
||||
translators:
|
||||
- ["Chenbo Li", "http://binarythink.net/"]
|
||||
---
|
||||
|
||||
C语言在今天仍然是高性能计算的主要选择。
|
||||
|
||||
C大概是大多数程序员用到的最接近底层的语言了,但是C语言本身不仅可以用来提升程序运行的速度
|
||||
注意看看C语言的文档,你就会知道C语言在内存管理方面的强大也是其他语言无法比拟的。
|
||||
|
||||
```c
|
||||
// 用“//”来实现单行注释
|
||||
|
||||
/*
|
||||
多行注释是这个样子的
|
||||
*/
|
||||
|
||||
// 用#include来导入头文件
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// 函数的标签(signature)应该放在.h文件中,并且引入到程序顶部
|
||||
// 也可以直接放到你的.c文件的最上面
|
||||
void function_1();
|
||||
void function_2();
|
||||
|
||||
// c程序的入口是一个返回值为int型的函数,名字叫做main
|
||||
int main() {
|
||||
|
||||
// 用printf来实现标准输出,这种输出也可以用格式来控制
|
||||
// %d 代表一个整数, \n 代表一个新行
|
||||
printf("%d\n", 0); // => 输出 0
|
||||
// 所有的语句都要以分号结束
|
||||
|
||||
///////////////////////////////////////
|
||||
// 类型
|
||||
///////////////////////////////////////
|
||||
|
||||
// 在使用变量之前我们必须先声明它们。
|
||||
// 变量在声明时需要指明其类型,而类型能够告诉系统这个变量所占用的空间
|
||||
|
||||
// int型(整型)变量一般占用4个字节
|
||||
int x_int = 0;
|
||||
|
||||
// short型(短整型)变量一般占用2个字节
|
||||
short x_short = 0;
|
||||
|
||||
// char型(字符型)变量会占用1个字节
|
||||
char x_char = 0;
|
||||
char y_char = 'y'; // 字符变量的字面值需要用单引号包住
|
||||
|
||||
// long型(长整型)一般需要4个字节到8个字节; 而long long型则至少需要8个字节(64位)
|
||||
|
||||
long x_long = 0;
|
||||
long long x_long_long = 0;
|
||||
|
||||
// float一般是用32位表示的浮点数字
|
||||
float x_float = 0.0;
|
||||
|
||||
// double一般是用64位表示的浮点数字
|
||||
double x_double = 0.0;
|
||||
|
||||
// 整数类型也可以有无符号的类型表示。这样这些变量就无法表示负数
|
||||
// 但是无符号整数所能表示的范围就可以比原来的整数大一些
|
||||
|
||||
unsigned char ux_char;
|
||||
unsigned short ux_short;
|
||||
unsigned int ux_int;
|
||||
unsigned long long ux_long_long;
|
||||
|
||||
// char类型一定会占用1个字节,但是其他的类型却会因具体机器的不同而各异
|
||||
// sizeof(T) 可以返回T类型在运行的机器上占用多少个字节
|
||||
// 这样你的代码就可以在各处正确运行了
|
||||
// 比如
|
||||
printf("%lu\n", sizeof(int)); // => 4 (字长为4的机器上)
|
||||
|
||||
// 数组必须要在开始被初始化为特定的长度
|
||||
char my_char_array[20]; // 这个数组占据 1 * 20 = 20 个字节
|
||||
int my_int_array[20]; // 这个数组占据 4 * 20 = 80 个字节
|
||||
// (这里我们假设字长为4)
|
||||
|
||||
|
||||
// 可以用下面的方法把数组初始化为0:
|
||||
char my_array[20] = {0};
|
||||
|
||||
// 对数组任意存取就像其他语言的方式 -- 其实是其他的语言像C
|
||||
my_array[0]; // => 0
|
||||
|
||||
// 数组是可变的,其实就是内存的映射!
|
||||
my_array[1] = 2;
|
||||
printf("%d\n", my_array[1]); // => 2
|
||||
|
||||
// 字符串就是以 NUL (0x00) 这个字符结尾的字符数组,
|
||||
// 这个字符可以用'\0'来表示.
|
||||
// (在字符串字面值中我们不必输入这个字符,编译器会自动添加的)
|
||||
char a_string[20] = "This is a string";
|
||||
printf("%s\n", a_string); // %s 可以对字符串进行格式化
|
||||
|
||||
/*
|
||||
也许你会注意到 a_string 实际上只有16个字节长.
|
||||
第17个字节是一个空字符(NUL)
|
||||
而第18, 19 和 20 个字符的值是不确定的。
|
||||
*/
|
||||
|
||||
printf("%d\n", a_string[16]); // => 0
|
||||
|
||||
///////////////////////////////////////
|
||||
// 操作符
|
||||
///////////////////////////////////////
|
||||
|
||||
int i1 = 1, i2 = 2; // 多个变量声明的简写
|
||||
float f1 = 1.0, f2 = 2.0;
|
||||
|
||||
// 算数运算
|
||||
i1 + i2; // => 3
|
||||
i2 - i1; // => 1
|
||||
i2 * i1; // => 2
|
||||
i1 / i2; // => 0 (0.5 会被化整为 0)
|
||||
|
||||
f1 / f2; // => 0.5, 也许会有很小的误差
|
||||
|
||||
// 取余运算
|
||||
11 % 3; // => 2
|
||||
|
||||
// 比较操作符我们也很熟悉, 但是有一点,C中没有布尔类型
|
||||
// 而是用整形替代
|
||||
// 0 就是 false, 其他的就是 true. (比较操作符的返回值则仅有0和1)
|
||||
3 == 2; // => 0 (false)
|
||||
3 != 2; // => 1 (true)
|
||||
3 > 2; // => 1
|
||||
3 < 2; // => 0
|
||||
2 <= 2; // => 1
|
||||
2 >= 2; // => 1
|
||||
|
||||
// 逻辑运算符需要作用于整数
|
||||
!3; // => 0 (非)
|
||||
!0; // => 1
|
||||
1 && 1; // => 1 (且)
|
||||
0 && 1; // => 0
|
||||
0 || 1; // => 1 (或)
|
||||
0 || 0; // => 0
|
||||
|
||||
// 位运算
|
||||
~0x0F; // => 0xF0 (取反)
|
||||
0x0F & 0xF0; // => 0x00 (和)
|
||||
0x0F | 0xF0; // => 0xFF (或)
|
||||
0x04 ^ 0x0F; // => 0x0B (异或)
|
||||
0x01 << 1; // => 0x02 (左移1位)
|
||||
0x02 >> 1; // => 0x01 (右移1位)
|
||||
|
||||
///////////////////////////////////////
|
||||
// 控制结构
|
||||
///////////////////////////////////////
|
||||
|
||||
if (0) {
|
||||
printf("I am never run\n");
|
||||
} else if (0) {
|
||||
printf("I am also never run\n");
|
||||
} else {
|
||||
printf("I print\n");
|
||||
}
|
||||
|
||||
// While循环
|
||||
int ii = 0;
|
||||
while (ii < 10) {
|
||||
printf("%d, ", ii++); // ii++ 在取值过后自增
|
||||
} // => 输出 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
|
||||
|
||||
printf("\n");
|
||||
|
||||
int kk = 0;
|
||||
do {
|
||||
printf("%d, ", kk);
|
||||
} while (++kk < 10); // ++kk 先自增,在被取值
|
||||
// => 输出 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
|
||||
|
||||
printf("\n");
|
||||
|
||||
// For 循环
|
||||
int jj;
|
||||
for (jj=0; jj < 10; jj++) {
|
||||
printf("%d, ", jj);
|
||||
} // => 输出 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
|
||||
|
||||
printf("\n");
|
||||
|
||||
///////////////////////////////////////
|
||||
// 类型转换
|
||||
///////////////////////////////////////
|
||||
|
||||
// 在C中每个变量都有类型,你可以将变量的类型进行转换
|
||||
|
||||
int x_hex = 0x01; // 可以用16进制赋值
|
||||
|
||||
// 在类型转换时,数字本身的值会被保留下来
|
||||
printf("%d\n", x_hex); // => 输出 1
|
||||
printf("%d\n", (short) x_hex); // => 输出 1
|
||||
printf("%d\n", (char) x_hex); // => 输出 1
|
||||
|
||||
// 类型转换时可能会造成溢出,而且不会抛出警告
|
||||
printf("%d\n", (char) 257); // => 1 (char的最大值为255)
|
||||
|
||||
// 整数型和浮点型可以互相转换
|
||||
printf("%f\n", (float)100); // %f 表示单精度浮点
|
||||
printf("%lf\n", (double)100); // %lf 表示双精度浮点
|
||||
printf("%d\n", (char)100.0);
|
||||
|
||||
///////////////////////////////////////
|
||||
// 指针
|
||||
///////////////////////////////////////
|
||||
|
||||
// 指针变量是用来储存内存地址的变量
|
||||
// 指针变量的定义也会告诉你指向的地址的变量的类型
|
||||
// 你也可以得到某个变量的地址,并对它们进行操作
|
||||
|
||||
int x = 0;
|
||||
printf("%p\n", &x); // 用 & 来获取变量的地址
|
||||
// (%p 表示一个指针)
|
||||
// => 输出某个内存地址
|
||||
|
||||
// 指针类型在定义是需要以*结束
|
||||
int* px; // px是一个指向int型的指针
|
||||
px = &x; // 把x的地址保存到px中
|
||||
printf("%p\n", px); // => 输出内存中的某个地址
|
||||
|
||||
// 要得到某个指针指向的内容的值,可以在指针前加一个*来取得(去引用)
|
||||
printf("%d\n", *px); // => 输出 0, 即x的值
|
||||
|
||||
// 你也可以改变指针所指向的值
|
||||
// 此时你需要在*运算符后添加一个括号,因为++比*的优先级更高
|
||||
(*px)++; // 把px所指向的值增加2
|
||||
printf("%d\n", *px); // => 输出 1
|
||||
printf("%d\n", x); // => 输出 1
|
||||
|
||||
int x_array[20]; // 数组是分配一系列连续空间的常用方式
|
||||
int xx;
|
||||
for (xx=0; xx<20; xx++) {
|
||||
x_array[xx] = 20 - xx;
|
||||
} // 初始化 x_array 为 20, 19, 18,... 2, 1
|
||||
|
||||
// 生命一个变量为指向整型的指针类型,并初始化为指向x_array
|
||||
int* x_ptr = x_array;
|
||||
// x_ptr现在指向了数组的第一个元素(即整数20).
|
||||
|
||||
// 事实上数组本身就是指向它的第一个元素的指针
|
||||
printf("%d\n", *(x_ptr)); // => 输出 20
|
||||
printf("%d\n", x_array[0]); // => 输出 20
|
||||
|
||||
// 指针的增减多少是依据它本身的类型而定的
|
||||
printf("%d\n", *(x_ptr + 1)); // => 输出 19
|
||||
printf("%d\n", x_array[1]); // => 输出 19
|
||||
|
||||
// 你也可以通过标准库函数malloc来实现动态分配
|
||||
// 这个函数接受一个代表容量的参数
|
||||
// 系统会从堆区分配指定容量字节大小的空间
|
||||
int* my_ptr = (int*) malloc(sizeof(int) * 20);
|
||||
for (xx=0; xx<20; xx++) {
|
||||
*(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx 也可以
|
||||
} // 初始化内存为 20, 19, 18, 17... 2, 1 (as ints)
|
||||
|
||||
// 如果对一些未分配的内存取值则会得到未知的结果
|
||||
printf("%d\n", *(my_ptr + 21)); // => 谁知道会输出什么
|
||||
|
||||
// 当你通过malloc得到一块区域后,你需要释放它
|
||||
// 否则没人能够再次使用这块内存,直到程序结束为止
|
||||
free(my_ptr);
|
||||
|
||||
// 字符串通常是字符数组,但是经常用字符指针表示
|
||||
// 指针:
|
||||
char* my_str = "This is my very own string";
|
||||
|
||||
printf("%c\n", *my_str); // => 'T'
|
||||
|
||||
function_1();
|
||||
} // main函数结束
|
||||
|
||||
///////////////////////////////////////
|
||||
// 函数
|
||||
///////////////////////////////////////
|
||||
|
||||
// 函数声明语法:
|
||||
// <返回值类型> <函数名称>(<参数>)
|
||||
|
||||
int add_two_ints(int x1, int x2){
|
||||
return x1 + x2; // 用return来返回一个值
|
||||
}
|
||||
|
||||
/*
|
||||
函数是按值传递的, 但是你可以通过传递参数来传递引用,这样函数就可以更改值
|
||||
|
||||
例子:字符串本身翻转
|
||||
*/
|
||||
|
||||
// 类型为void的函数没有返回值
|
||||
void str_reverse(char* str_in){
|
||||
char tmp;
|
||||
int ii=0, len = strlen(str_in); // Strlen 是C标准库函数
|
||||
for(ii=0; ii<len/2; ii++){
|
||||
tmp = str_in[ii];
|
||||
str_in[ii] = str_in[len - ii - 1]; // 从倒数第ii个开始
|
||||
str_in[len - ii - 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
char c[] = "This is a test.";
|
||||
str_reverse(c);
|
||||
printf("%s\n", c); // => ".tset a si sihT"
|
||||
*/
|
||||
|
||||
///////////////////////////////////////
|
||||
// 用户自定义类型和结构
|
||||
///////////////////////////////////////
|
||||
|
||||
// Typedefs可以创建类型别名
|
||||
typedef int my_type;
|
||||
my_type my_type_var = 0;
|
||||
|
||||
// 结构是一系列数据的集合
|
||||
struct rectangle {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
|
||||
void function_1(){
|
||||
|
||||
struct rectangle my_rec;
|
||||
|
||||
// 通过 . 来访问结构中的数据
|
||||
my_rec.width = 10;
|
||||
my_rec.height = 20;
|
||||
|
||||
// 你也可以声明指向结构体的指针
|
||||
struct rectangle* my_rec_ptr = &my_rec;
|
||||
|
||||
// 通过取值来改变结构体的成员...
|
||||
(*my_rec_ptr).width = 30;
|
||||
|
||||
// ... 或者用 -> 操作符作为简写
|
||||
my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10;
|
||||
}
|
||||
|
||||
// 你也可以用typedef来给一个结构体起一个别名
|
||||
typedef struct rectangle rect;
|
||||
|
||||
int area(rect r){
|
||||
return r.width * r.height;
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// 函数指针
|
||||
///////////////////////////////////////
|
||||
/*
|
||||
在运行时,函数本身也被存放到某块内存区域当中
|
||||
函数指针就像其他指针一样, 但却可以被用来直接调用函数,
|
||||
并且可以被四处传递(就像回调函数那样)
|
||||
但是,定义的语法有可能在一开始会有些误解
|
||||
|
||||
例子:通过指针调用str_reverse
|
||||
*/
|
||||
void str_reverse_through_pointer(char * str_in) {
|
||||
// 定义一个函数指针 f.
|
||||
void (*f)(char *); // 签名一定要与目标函数相同
|
||||
f = &str_reverse; // 将函数的地址在运行时赋给指针
|
||||
(*f)(str_in); // 通过指针调用函数
|
||||
// f(str_in); // 等价于这种调用方式
|
||||
}
|
||||
|
||||
/*
|
||||
只要函数签名是正确的,任何时候都能将正确的函数赋给某个函数指针
|
||||
为了可读性和简洁性,函数指针经常和typedef搭配使用:
|
||||
*/
|
||||
|
||||
typedef void (*my_fnp_type)(char *);
|
||||
|
||||
// 实际声明函数指针会这么用:
|
||||
// ...
|
||||
// my_fnp_type f;
|
||||
|
||||
```
|
||||
|
||||
## 更多阅读
|
||||
|
||||
最好找一本 [K&R, aka "The C Programming Language", “C程序设计语言”](https://en.wikipedia.org/wiki/The_C_Programming_Language)
|
||||
|
||||
其他一些比较好的资源 [Learn C the hard way](http://c.learncodethehardway.org/book/)
|
||||
|
||||
除了这些,多多Google吧
|
340
zh-cn/elisp-cn.html.markdown
Executable file
340
zh-cn/elisp-cn.html.markdown
Executable file
@@ -0,0 +1,340 @@
|
||||
---
|
||||
language: elisp
|
||||
contributors:
|
||||
- ["Bastien Guerry", "http://bzg.fr"]
|
||||
translators:
|
||||
- ["Chenbo Li", "http://binarythink.net"]
|
||||
filename: learn-emacs-lisp.el
|
||||
---
|
||||
|
||||
```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
|
||||
```
|
||||
|
404
zh-cn/java-cn.html.markdown
Executable file
404
zh-cn/java-cn.html.markdown
Executable file
@@ -0,0 +1,404 @@
|
||||
---
|
||||
|
||||
language: java
|
||||
contributors:
|
||||
- ["Jake Prather", "http://github.com/JakeHP"]
|
||||
translators:
|
||||
- ["Chenbo Li", "http://binarythink.net"]
|
||||
filename: LearnJava.java
|
||||
|
||||
---
|
||||
|
||||
Java是一个通用的程序语言, 包含并发, 基于类的面向对象等特性
|
||||
[阅读更多](http://docs.oracle.com/javase/tutorial/java/index.html)
|
||||
|
||||
```java
|
||||
// 单行注释
|
||||
/*
|
||||
多行注释
|
||||
*/
|
||||
/**
|
||||
JavaDoc(Java文档)注释是这样的。可以用来描述类和类的属性。
|
||||
*/
|
||||
|
||||
// 导入 java.util中的 ArrayList 类
|
||||
import java.util.ArrayList;
|
||||
// 导入 java.security 包中的所有类
|
||||
import java.security.*;
|
||||
|
||||
// 每个 .java 文件都包含一个public类,这个类的名字必须和这个文件名一致。
|
||||
public class LearnJava {
|
||||
|
||||
// 每个程序都需要有一个main函数作为入口
|
||||
public static void main (String[] args) {
|
||||
|
||||
// 使用 System.out.println 来输出到标准输出
|
||||
System.out.println("Hello World!");
|
||||
System.out.println(
|
||||
"Integer: " + 10 +
|
||||
" Double: " + 3.14 +
|
||||
" Boolean: " + true);
|
||||
|
||||
// 如果要在输出后不自动换行,可以使用System.out.print方法。
|
||||
System.out.print("Hello ");
|
||||
System.out.print("World");
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// 类型与变量
|
||||
///////////////////////////////////////
|
||||
|
||||
// 用 <type> <name> 来声明变量
|
||||
// 字节类型 - 8位补码表示
|
||||
// (-128 <= 字节 <= 127)
|
||||
byte fooByte = 100;
|
||||
|
||||
// 短整型 - 16位补码表示
|
||||
// (-32,768 <= 短整型 <= 32,767)
|
||||
short fooShort = 10000;
|
||||
|
||||
// 整型 - 32位补码表示
|
||||
// (-2,147,483,648 <= 整型 <= 2,147,483,647)
|
||||
int fooInt = 1;
|
||||
|
||||
// 长整型 - 64位补码表示
|
||||
// (-9,223,372,036,854,775,808 <= 长整型 <= 9,223,372,036,854,775,807)
|
||||
long fooLong = 100000L;
|
||||
// L可以用来表示一个数字是长整型的。
|
||||
// 其他的数字都默认为整型。
|
||||
|
||||
// 注意:Java中没有无符号类型
|
||||
|
||||
// 浮点型 - 即 IEEE 754 规定的32位单精度浮点类型
|
||||
float fooFloat = 234.5f;
|
||||
// f用来表示一个数字是浮点型的。
|
||||
// 否则会被默认当做是双精度浮点型。
|
||||
|
||||
// 双精度浮点型 - 即 IEEE 754 规定的64位双精度浮点类型
|
||||
double fooDouble = 123.4;
|
||||
|
||||
// 布尔类型 - true 与 false
|
||||
boolean fooBoolean = true;
|
||||
boolean barBoolean = false;
|
||||
|
||||
// 字符类型 - 16位 Unicode编码字符
|
||||
char fooChar = 'A';
|
||||
|
||||
// 用 final 可以使一个常量不可更改
|
||||
final int HOURS_I_WORK_PER_WEEK = 9001;
|
||||
|
||||
// 字符串
|
||||
String fooString = "My String Is Here!";
|
||||
|
||||
// \n 代表一个新的换行
|
||||
String barString = "Printing on a new line?\nNo Problem!";
|
||||
// \t 代表一个新的制表符
|
||||
String bazString = "Do you want to add a tab?\tNo Problem!";
|
||||
System.out.println(fooString);
|
||||
System.out.println(barString);
|
||||
System.out.println(bazString);
|
||||
|
||||
// 数组
|
||||
// 数组在声明时大小必须已经确定
|
||||
// 数组的声明格式:
|
||||
//<数据类型> [] <变量名> = new <数据类型>[<数组大小>];
|
||||
int [] intArray = new int[10];
|
||||
String [] stringArray = new String[1];
|
||||
boolean [] booleanArray = new boolean[100];
|
||||
|
||||
// 声明并初始化数组也可以这样:
|
||||
int [] y = {9000, 1000, 1337};
|
||||
|
||||
// 随机访问数组中的元素
|
||||
System.out.println("intArray @ 0: " + intArray[0]);
|
||||
|
||||
// 数组下标从0开始并且可以被更改
|
||||
intArray[1] = 1;
|
||||
System.out.println("intArray @ 1: " + intArray[1]); // => 1
|
||||
|
||||
// 其他数据类型
|
||||
// ArrayLists - 类似于数组,但是功能更多,并且大小也可以改变
|
||||
// LinkedLists
|
||||
// Maps
|
||||
// HashMaps
|
||||
|
||||
///////////////////////////////////////
|
||||
// 操作符
|
||||
///////////////////////////////////////
|
||||
System.out.println("\n->Operators");
|
||||
|
||||
int i1 = 1, i2 = 2; // 多重声明可以简化
|
||||
|
||||
// 算数运算
|
||||
System.out.println("1+2 = " + (i1 + i2)); // => 3
|
||||
System.out.println("2-1 = " + (i2 - i1)); // => 1
|
||||
System.out.println("2*1 = " + (i2 * i1)); // => 2
|
||||
System.out.println("1/2 = " + (i1 / i2)); // => 0 (0.5 truncated down)
|
||||
|
||||
// 取余
|
||||
System.out.println("11%3 = "+(11 % 3)); // => 2
|
||||
|
||||
// 比较操作符
|
||||
System.out.println("3 == 2? " + (3 == 2)); // => false
|
||||
System.out.println("3 != 2? " + (3 != 2)); // => true
|
||||
System.out.println("3 > 2? " + (3 > 2)); // => true
|
||||
System.out.println("3 < 2? " + (3 < 2)); // => false
|
||||
System.out.println("2 <= 2? " + (2 <= 2)); // => true
|
||||
System.out.println("2 >= 2? " + (2 >= 2)); // => true
|
||||
|
||||
// 位运算操作符
|
||||
/*
|
||||
~ 补
|
||||
<< 带符号左移
|
||||
>> 带符号右移
|
||||
>>> 无符号右移
|
||||
& 和
|
||||
^ 异或
|
||||
| 相容或
|
||||
*/
|
||||
|
||||
// 自增
|
||||
int i = 0;
|
||||
System.out.println("\n->Inc/Dec-rementation");
|
||||
System.out.println(i++); //i = 1 后自增
|
||||
System.out.println(++i); //i = 2 前自增
|
||||
System.out.println(i--); //i = 1 后自减
|
||||
System.out.println(--i); //i = 0 前自减
|
||||
|
||||
///////////////////////////////////////
|
||||
// 控制结构
|
||||
///////////////////////////////////////
|
||||
System.out.println("\n->Control Structures");
|
||||
|
||||
// If语句和C的类似
|
||||
int j = 10;
|
||||
if (j == 10){
|
||||
System.out.println("I get printed");
|
||||
} else if (j > 10) {
|
||||
System.out.println("I don't");
|
||||
} else {
|
||||
System.out.println("I also don't");
|
||||
}
|
||||
|
||||
// While循环
|
||||
int fooWhile = 0;
|
||||
while(fooWhile < 100)
|
||||
{
|
||||
//System.out.println(fooWhile);
|
||||
//增加计数器
|
||||
//遍历99次, fooWhile 0->99
|
||||
fooWhile++;
|
||||
}
|
||||
System.out.println("fooWhile Value: " + fooWhile);
|
||||
|
||||
// Do While循环
|
||||
int fooDoWhile = 0;
|
||||
do
|
||||
{
|
||||
//System.out.println(fooDoWhile);
|
||||
//增加计数器
|
||||
//遍历99次, fooDoWhile 0->99
|
||||
fooDoWhile++;
|
||||
}while(fooDoWhile < 100);
|
||||
System.out.println("fooDoWhile Value: " + fooDoWhile);
|
||||
|
||||
// For 循环
|
||||
int fooFor;
|
||||
//for 循环结构 => for(<起始语句>; <循环进行的条件>; <步长>)
|
||||
for(fooFor=0; fooFor<10; fooFor++){
|
||||
//System.out.println(fooFor);
|
||||
//遍历 10 次, fooFor 0->9
|
||||
}
|
||||
System.out.println("fooFor Value: " + fooFor);
|
||||
|
||||
// Switch Case 语句
|
||||
// switch可以用来处理 byte, short, char, 和 int 数据类型
|
||||
// 也可以用来处理枚举类型,字符串类,和原始数据类型的包装类:
|
||||
// Character, Byte, Short, 和 Integer
|
||||
int month = 3;
|
||||
String monthString;
|
||||
switch (month){
|
||||
case 1:
|
||||
monthString = "January";
|
||||
break;
|
||||
case 2:
|
||||
monthString = "February";
|
||||
break;
|
||||
case 3:
|
||||
monthString = "March";
|
||||
break;
|
||||
default:
|
||||
monthString = "Some other month";
|
||||
break;
|
||||
}
|
||||
System.out.println("Switch Case Result: " + monthString);
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// 类型转换
|
||||
///////////////////////////////////////
|
||||
|
||||
// 数据转换
|
||||
|
||||
// 将字符串转换为整型
|
||||
Integer.parseInt("123");//返回整数123
|
||||
|
||||
// 将整型转换为字符串
|
||||
Integer.toString(123);//返回字符串"123"
|
||||
|
||||
// 其他的数据也可以进行互相转换:
|
||||
// Double
|
||||
// Long
|
||||
// String
|
||||
|
||||
// 类型转换
|
||||
// 你也可以对java对象进行类型转换, 但其中会牵扯到很多概念
|
||||
// 在这里可以查看更详细的信息:
|
||||
// http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// 类与函数
|
||||
///////////////////////////////////////
|
||||
|
||||
System.out.println("\n->Classes & Functions");
|
||||
|
||||
// (Bicycle类定义如下)
|
||||
|
||||
// 用new来实例化一个类
|
||||
Bicycle trek = new Bicycle();
|
||||
|
||||
// 调用对象的方法
|
||||
trek.speedUp(3); // 需用getter和setter方法
|
||||
trek.setCadence(100);
|
||||
|
||||
// toString 可以把对象转换为字符串
|
||||
System.out.println("trek info: " + trek.toString());
|
||||
|
||||
} // main 方法结束
|
||||
} // LearnJava 类结束
|
||||
|
||||
|
||||
// 你也可以把其他的非public类放入到.java文件中
|
||||
|
||||
|
||||
// 类定义的语法:
|
||||
// <public/private/protected> class <类名>{
|
||||
// //成员变量, 构造函数, 函数
|
||||
// //Java中函数被称作方法
|
||||
// }
|
||||
|
||||
class Bicycle {
|
||||
|
||||
// Bicycle 类的成员变量和方法
|
||||
public int cadence; // Public: 任意位置均可访问
|
||||
private int speed; // Private: 只在同类中可以访问
|
||||
protected int gear; // Protected: 可以在同类与子类中可以访问
|
||||
String name; // default: 可以在包内中可以访问
|
||||
|
||||
// 构造函数是初始化一个对象的方式
|
||||
// 以下是一个默认构造函数
|
||||
public Bi450635425cycle() {
|
||||
gear = 1;
|
||||
cadence = 50;
|
||||
speed = 5;
|
||||
name = "Bontrager";
|
||||
}
|
||||
|
||||
// 一下是一个含有参数的构造函数
|
||||
public Bicycle(int startCadence, int startSpeed, int startGear, String name) {
|
||||
this.gear = startGear;
|
||||
this.cadence = startCadence;
|
||||
this.speed = startSpeed;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// 函数语法:
|
||||
// <public/private/protected> <返回值类型> <函数名称>(<参数列表>)
|
||||
|
||||
// Java类中经常会用getter和setter来对成员变量进行操作
|
||||
|
||||
// 方法声明的语法:
|
||||
// <作用域> <返回值类型> <方法名>(<参数列表>)
|
||||
public int getCadence() {
|
||||
retur450635425n cadence;
|
||||
}
|
||||
|
||||
// void返450635425回值函数没有返回值
|
||||
public void setCadence(int newValue) {
|
||||
cadence = newValue;
|
||||
}
|
||||
|
||||
public void setGear(int newValue) {
|
||||
gear = newValue;
|
||||
}
|
||||
|
||||
public void speedUp(int increment) {
|
||||
speed += increment;
|
||||
}
|
||||
|
||||
public void slowDown(int decrement) {
|
||||
speed -= decrement;
|
||||
}
|
||||
|
||||
public void setName(String newName) {
|
||||
name = newName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
//返回对象属性的方法
|
||||
@Override
|
||||
public String toString() {
|
||||
return "gear: " + gear +
|
||||
" cadence: " + cadence +
|
||||
" speed: " + speed +
|
||||
" name: " + name;
|
||||
}
|
||||
} // Bicycle 类结束
|
||||
|
||||
// PennyFarthing 是 Bicycle 的子类
|
||||
class PennyFarthing extends Bicycle {
|
||||
// (Penny Farthings 是前轮很大的 Bicycle, 并且没有齿轮)
|
||||
|
||||
public PennyFarthing(int startCadence, int startSpeed){
|
||||
// 通过super调用父类的构造函数
|
||||
super(startCadence, startSpeed, 0, "PennyFarthing");
|
||||
}
|
||||
|
||||
// 你可以用@注释来表示需要重载的方法
|
||||
// 了解更多的注释使用方法,可以访问下面的地址:
|
||||
// http://docs.oracle.com/javase/tutorial/java/annotations/
|
||||
@Override
|
||||
public void setGear(int gear) {
|
||||
gear = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 更多阅读
|
||||
|
||||
下面的链接只是为了便于大家理解这些主题而给出的,对于具体的例子请大家自行Google
|
||||
|
||||
其他主题:
|
||||
|
||||
* [Java 官方教程](http://docs.oracle.com/javase/tutorial/index.html)
|
||||
|
||||
* [Java 访问修饰符](http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html)
|
||||
|
||||
* [面向对象程序设计概念](http://docs.oracle.com/javase/tutorial/java/concepts/index.html):
|
||||
* [继承](http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html)
|
||||
* [多态](http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html)
|
||||
* [抽象](http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html)
|
||||
|
||||
* [异常](http://docs.oracle.com/javase/tutorial/essential/exceptions/index.html)
|
||||
|
||||
* [接口](http://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html)
|
||||
|
||||
* [泛型](http://docs.oracle.com/javase/tutorial/java/generics/index.html)
|
||||
|
||||
* [Java代码规范](http://www.oracle.com/technetwork/java/codeconv-138413.html)
|
635
zh-cn/php-cn.html.markdown
Executable file
635
zh-cn/php-cn.html.markdown
Executable file
@@ -0,0 +1,635 @@
|
||||
---
|
||||
language: php
|
||||
contributors:
|
||||
- ["Malcolm Fell", "http://emarref.net/"]
|
||||
- ["Trismegiste", "https://github.com/Trismegiste"]
|
||||
translators:
|
||||
- ["Chenbo Li", "http://binarythink.net"]
|
||||
filename: learnphp.php
|
||||
---
|
||||
|
||||
这份教程所使用的版本是 PHP 5+.
|
||||
|
||||
```php
|
||||
<?php // PHP必须被包围于 <?php ? > 之中
|
||||
|
||||
// 如果你的文件中只有php代码,那么最好省略结束括号标记
|
||||
|
||||
// 这是单行注释的标志
|
||||
|
||||
# 井号也可以,但是//更常见
|
||||
|
||||
/*
|
||||
这是多行注释
|
||||
*/
|
||||
|
||||
// 使用 "echo" 或者 "print" 来输出信息到标准输出
|
||||
print('Hello '); // 输出 "Hello " 并且没有换行符
|
||||
|
||||
// () 对于echo和print是可选的
|
||||
echo "World\n"; // 输出 "World" 并且换行
|
||||
// (每个语句必须以分号结尾)
|
||||
|
||||
// 在 <?php 标签之外的语句都被自动输出到标准输出
|
||||
?>Hello World Again!
|
||||
<?php
|
||||
|
||||
|
||||
/************************************
|
||||
* 类型与变量
|
||||
*/
|
||||
|
||||
// 变量以$开始
|
||||
// 变量可以以字母或者下划线开头,后面可以跟着数字、字母和下划线
|
||||
|
||||
// 布尔值是大小写无关的
|
||||
$boolean = true; // 或 TRUE 或 True
|
||||
$boolean = false; // 或 FALSE 或 False
|
||||
|
||||
// 整型
|
||||
$int1 = 12; // => 12
|
||||
$int2 = -12; // => -12
|
||||
$int3 = 012; // => 10 (0开头代表八进制数)
|
||||
$int4 = 0x0F; // => 15 (0x开头代表十六进制数)
|
||||
|
||||
// 浮点型 (即双精度浮点型)
|
||||
$float = 1.234;
|
||||
$float = 1.2e3;
|
||||
$float = 7E-10;
|
||||
|
||||
// 算数运算
|
||||
$sum = 1 + 1; // 2
|
||||
$difference = 2 - 1; // 1
|
||||
$product = 2 * 2; // 4
|
||||
$quotient = 2 / 1; // 2
|
||||
|
||||
// 算数运算的简写
|
||||
$number = 0;
|
||||
$number += 1; // $number 自增1
|
||||
echo $number++; // 输出1 (运算后自增)
|
||||
echo ++$number; // 输出3 (自增后运算)
|
||||
$number /= $float; // 先除后赋值给 $number
|
||||
|
||||
// 字符串需要被包含在单引号之中
|
||||
$sgl_quotes = '$String'; // => '$String'
|
||||
|
||||
// 如果需要在字符串中引用变量,就需要使用双引号
|
||||
$dbl_quotes = "This is a $sgl_quotes."; // => 'This is a $String.'
|
||||
|
||||
// 特殊字符只有在双引号中有用
|
||||
$escaped = "This contains a \t tab character.";
|
||||
$unescaped = 'This just contains a slash and a t: \t';
|
||||
|
||||
// 可以把变量包含在一对大括号中
|
||||
$money = "I have $${number} in the bank.";
|
||||
|
||||
// 自 PHP 5.3 开始, nowdocs 可以被用作多行非计算型字符串
|
||||
$nowdoc = <<<'END'
|
||||
Multi line
|
||||
string
|
||||
END;
|
||||
|
||||
// 而Heredocs则可以用作多行计算型字符串
|
||||
$heredoc = <<<END
|
||||
Multi line
|
||||
$sgl_quotes
|
||||
END;
|
||||
|
||||
// 字符串需要用 . 来连接
|
||||
echo 'This string ' . 'is concatenated';
|
||||
|
||||
|
||||
/********************************
|
||||
* 数组
|
||||
*/
|
||||
|
||||
// PHP 中的数组都是关联型数组,也就是某些语言中的哈希表或字典
|
||||
|
||||
// 在所有PHP版本中均适用:
|
||||
$associative = array('One' => 1, 'Two' => 2, 'Three' => 3);
|
||||
|
||||
// PHP 5.4 中引入了新的语法
|
||||
$associative = ['One' => 1, 'Two' => 2, 'Three' => 3];
|
||||
|
||||
echo $associative['One']; // 输出 1
|
||||
|
||||
// 声明为列表实际上是给每个值都分配了一个整数键(key)
|
||||
$array = ['One', 'Two', 'Three'];
|
||||
echo $array[0]; // => "One"
|
||||
|
||||
|
||||
/********************************
|
||||
* 输出
|
||||
*/
|
||||
|
||||
echo('Hello World!');
|
||||
// 输出到标准输出
|
||||
// 此时标准输出就是浏览器中的网页
|
||||
|
||||
print('Hello World!'); // 和echo相同
|
||||
|
||||
// echo和print实际上也属于这个语言本身,所以我们省略括号
|
||||
echo 'Hello World!';
|
||||
print 'Hello World!';
|
||||
|
||||
$paragraph = 'paragraph';
|
||||
|
||||
echo 100; // 直接输出标量
|
||||
echo $paragraph; // 或者输出变量
|
||||
|
||||
// 如果你配置了短标签,或者使用5.4.0及以上的版本
|
||||
// 你就可以使用简写的echo语法
|
||||
?>
|
||||
<p><?= $paragraph ?></p>
|
||||
<?php
|
||||
|
||||
$x = 1;
|
||||
$y = 2;
|
||||
$x = $y; // $x 现在和 $y 的值相同
|
||||
$z = &$y;
|
||||
// $z 现在持有 $y 的引用. 现在更改 $z 的值也会更改 $y 的值,反之亦然
|
||||
// 但是改变 $y 的值不会改变 $x 的值
|
||||
|
||||
echo $x; // => 2
|
||||
echo $z; // => 2
|
||||
$y = 0;
|
||||
echo $x; // => 2
|
||||
echo $z; // => 0
|
||||
|
||||
|
||||
/********************************
|
||||
* 逻辑
|
||||
*/
|
||||
$a = 0;
|
||||
$b = '0';
|
||||
$c = '1';
|
||||
$d = '1';
|
||||
|
||||
// 如果assert的参数为假,就会抛出警告
|
||||
|
||||
// 下面的比较都为真,不管它们的类型是否匹配
|
||||
assert($a == $b); // 相等
|
||||
assert($c != $a); // 不等
|
||||
assert($c <> $a); // 另一种不等的表示
|
||||
assert($a < $c);
|
||||
assert($c > $b);
|
||||
assert($a <= $b);
|
||||
assert($c >= $d);
|
||||
|
||||
// 下面的比较只有在类型相同、值相同的情况下才为真
|
||||
assert($c === $d);
|
||||
assert($a !== $d);
|
||||
assert(1 == '1');
|
||||
assert(1 !== '1');
|
||||
|
||||
// 变量可以根据其使用来进行类型转换
|
||||
|
||||
$integer = 1;
|
||||
echo $integer + $integer; // => 2
|
||||
|
||||
$string = '1';
|
||||
echo $string + $string; // => 2 (字符串在此时被转化为整数)
|
||||
|
||||
$string = 'one';
|
||||
echo $string + $string; // => 0
|
||||
// 输出0,因为'one'这个字符串无法被转换为整数
|
||||
|
||||
// 类型转换可以将一个类型视作另一种类型
|
||||
|
||||
$boolean = (boolean) 1; // => true
|
||||
|
||||
$zero = 0;
|
||||
$boolean = (boolean) $zero; // => false
|
||||
|
||||
// 还有一些专用的函数来进行类型转换
|
||||
$integer = 5;
|
||||
$string = strval($integer);
|
||||
|
||||
$var = null; // 空值
|
||||
|
||||
|
||||
/********************************
|
||||
* 控制结构
|
||||
*/
|
||||
|
||||
if (true) {
|
||||
print 'I get printed';
|
||||
}
|
||||
|
||||
if (false) {
|
||||
print 'I don\'t';
|
||||
} else {
|
||||
print 'I get printed';
|
||||
}
|
||||
|
||||
if (false) {
|
||||
print 'Does not get printed';
|
||||
} elseif(true) {
|
||||
print 'Does';
|
||||
}
|
||||
|
||||
// 三目运算符
|
||||
print (false ? 'Does not get printed' : 'Does');
|
||||
|
||||
$x = 0;
|
||||
if ($x === '0') {
|
||||
print 'Does not print';
|
||||
} elseif($x == '1') {
|
||||
print 'Does not print';
|
||||
} else {
|
||||
print 'Does print';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 下面的语法常用语模板中:
|
||||
?>
|
||||
|
||||
<?php if ($x): ?>
|
||||
This is displayed if the test is truthy.
|
||||
<?php else: ?>
|
||||
This is displayed otherwise.
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
|
||||
// 用switch来实现相同的逻辑
|
||||
switch ($x) {
|
||||
case '0':
|
||||
print 'Switch does type coercion';
|
||||
break; // 在case中必须使用一个break语句,
|
||||
// 否则在执行完这个语句后会直接执行后面的语句
|
||||
case 'two':
|
||||
case 'three':
|
||||
// 如果$variable是 'two' 或 'three',执行这里的语句
|
||||
break;
|
||||
default:
|
||||
// 其他情况
|
||||
}
|
||||
|
||||
// While, do...while 和 for 循环
|
||||
$i = 0;
|
||||
while ($i < 5) {
|
||||
echo $i++;
|
||||
}; // 输出 "01234"
|
||||
|
||||
echo "\n";
|
||||
|
||||
$i = 0;
|
||||
do {
|
||||
echo $i++;
|
||||
} while ($i < 5); // 输出 "01234"
|
||||
|
||||
echo "\n";
|
||||
|
||||
for ($x = 0; $x < 10; $x++) {
|
||||
echo $x;
|
||||
} // 输出 "0123456789"
|
||||
|
||||
echo "\n";
|
||||
|
||||
$wheels = ['bicycle' => 2, 'car' => 4];
|
||||
|
||||
// Foreach 循环可以遍历数组
|
||||
foreach ($wheels as $wheel_count) {
|
||||
echo $wheel_count;
|
||||
} // 输出 "24"
|
||||
|
||||
echo "\n";
|
||||
|
||||
// 也可以同时遍历键和值
|
||||
foreach ($wheels as $vehicle => $wheel_count) {
|
||||
echo "A $vehicle has $wheel_count wheels";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
$i = 0;
|
||||
while ($i < 5) {
|
||||
if ($i === 3) {
|
||||
break; // 退出循环
|
||||
}
|
||||
echo $i++;
|
||||
} // 输出 "012"
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
if ($i === 3) {
|
||||
continue; // 跳过此次遍历
|
||||
}
|
||||
echo $i;
|
||||
} // 输出 "0124"
|
||||
|
||||
|
||||
/********************************
|
||||
* 函数
|
||||
*/
|
||||
|
||||
// 通过"function"定义函数:
|
||||
function my_function () {
|
||||
return 'Hello';
|
||||
}
|
||||
|
||||
echo my_function(); // => "Hello"
|
||||
|
||||
// 函数名需要以字母或者下划线开头,
|
||||
// 后面可以跟着任意的字幕、下划线、数字.
|
||||
|
||||
function add ($x, $y = 1) { // $y 是可选参数,默认值为 1
|
||||
$result = $x + $y;
|
||||
return $result;
|
||||
}
|
||||
|
||||
echo add(4); // => 5
|
||||
echo add(4, 2); // => 6
|
||||
|
||||
// $result 在函数外部不可访问
|
||||
// print $result; // 抛出警告
|
||||
|
||||
// 从 PHP 5.3 起我们可以定义匿名函数
|
||||
$inc = function ($x) {
|
||||
return $x + 1;
|
||||
};
|
||||
|
||||
echo $inc(2); // => 3
|
||||
|
||||
function foo ($x, $y, $z) {
|
||||
echo "$x - $y - $z";
|
||||
}
|
||||
|
||||
// 函数也可以返回一个函数
|
||||
function bar ($x, $y) {
|
||||
// 用 'use' 将外部的参数引入到里面
|
||||
return function ($z) use ($x, $y) {
|
||||
foo($x, $y, $z);
|
||||
};
|
||||
}
|
||||
|
||||
$bar = bar('A', 'B');
|
||||
$bar('C'); // 输出 "A - B - C"
|
||||
|
||||
// 你也可以通过字符串调用函数
|
||||
$function_name = 'add';
|
||||
echo $function_name(1, 2); // => 3
|
||||
// 在通过程序来决定调用哪个函数时很有用
|
||||
// 或者,使用 call_user_func(callable $callback [, $parameter [, ... ]]);
|
||||
|
||||
/********************************
|
||||
* 导入
|
||||
*/
|
||||
|
||||
<?php
|
||||
// 被导入的php文件也必须以php开标签开始
|
||||
|
||||
include 'my-file.php';
|
||||
// 现在my-file.php就在当前作用域中可见了
|
||||
// 如果这个文件无法被导入(比如文件不存在),会抛出警告
|
||||
|
||||
include_once 'my-file.php';
|
||||
// my-file.php中的代码在其他地方被导入了,那么就不会被再次导入
|
||||
// 这会避免类的多重定义错误
|
||||
|
||||
require 'my-file.php';
|
||||
require_once 'my-file.php';
|
||||
// 和include功能相同,只不过如果不能被导入时,会抛出错误
|
||||
|
||||
// my-include.php的内容:
|
||||
<?php
|
||||
|
||||
return 'Anything you like.';
|
||||
// 文件结束
|
||||
|
||||
// Include和Require函数也有返回值
|
||||
$value = include 'my-include.php';
|
||||
|
||||
// 被引入的文件是根据文件路径或者include_path配置来查找到的
|
||||
// 如果文件最终没有被找到,那么就会查找当前文件夹。之后才会报错
|
||||
/* */
|
||||
|
||||
/********************************
|
||||
* 类
|
||||
*/
|
||||
|
||||
// 类是由class关键字定义的
|
||||
|
||||
class MyClass
|
||||
{
|
||||
const MY_CONST = 'value'; // 常量
|
||||
|
||||
static $staticVar = 'static';
|
||||
|
||||
// 属性必须声明其作用域
|
||||
public $property = 'public';
|
||||
public $instanceProp;
|
||||
protected $prot = 'protected'; // 当前类和子类可访问
|
||||
private $priv = 'private'; // 仅当前类可访问
|
||||
|
||||
// 通过 __construct 来定义构造函数
|
||||
public function __construct($instanceProp) {
|
||||
// 通过 $this 访问当前对象
|
||||
$this->instanceProp = $instanceProp;
|
||||
}
|
||||
|
||||
// 方法就是类中定义的函数
|
||||
public function myMethod()
|
||||
{
|
||||
print 'MyClass';
|
||||
}
|
||||
|
||||
final function youCannotOverrideMe()
|
||||
{
|
||||
}
|
||||
|
||||
public static function myStaticMethod()
|
||||
{
|
||||
print 'I am static';
|
||||
}
|
||||
}
|
||||
|
||||
echo MyClass::MY_CONST; // 输出 'value';
|
||||
echo MyClass::$staticVar; // 输出 'static';
|
||||
MyClass::myStaticMethod(); // 输出 'I am static';
|
||||
|
||||
// 通过new来新建实例
|
||||
$my_class = new MyClass('An instance property');
|
||||
// 如果不传递参数,那么括号可以省略
|
||||
|
||||
// 用 -> 来访问成员
|
||||
echo $my_class->property; // => "public"
|
||||
echo $my_class->instanceProp; // => "An instance property"
|
||||
$my_class->myMethod(); // => "MyClass"
|
||||
|
||||
|
||||
// 使用extends来生成子类
|
||||
class MyOtherClass extends MyClass
|
||||
{
|
||||
function printProtectedProperty()
|
||||
{
|
||||
echo $this->prot;
|
||||
}
|
||||
|
||||
// 方法覆盖
|
||||
function myMethod()
|
||||
{
|
||||
parent::myMethod();
|
||||
print ' > MyOtherClass';
|
||||
}
|
||||
}
|
||||
|
||||
$my_other_class = new MyOtherClass('Instance prop');
|
||||
$my_other_class->printProtectedProperty(); // => 输出 "protected"
|
||||
$my_other_class->myMethod(); // 输出 "MyClass > MyOtherClass"
|
||||
|
||||
final class YouCannotExtendMe
|
||||
{
|
||||
}
|
||||
|
||||
// 你可以使用“魔法方法”来生成getter和setter方法
|
||||
class MyMapClass
|
||||
{
|
||||
private $property;
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
public function __set($key, $value)
|
||||
{
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$x = new MyMapClass();
|
||||
echo $x->property; // 会使用 __get() 方法
|
||||
$x->property = 'Something'; // 会使用 __set() 方法
|
||||
|
||||
// 类可以是被定义成抽象类 (使用 abstract 关键字) 或者
|
||||
// 去实现接口 (使用 implements 关键字).
|
||||
// 接口需要通过interface关键字来定义
|
||||
|
||||
interface InterfaceOne
|
||||
{
|
||||
public function doSomething();
|
||||
}
|
||||
|
||||
interface InterfaceTwo
|
||||
{
|
||||
public function doSomethingElse();
|
||||
}
|
||||
|
||||
// 接口可以被扩展
|
||||
interface InterfaceThree extends InterfaceTwo
|
||||
{
|
||||
public function doAnotherContract();
|
||||
}
|
||||
|
||||
abstract class MyAbstractClass implements InterfaceOne
|
||||
{
|
||||
public $x = 'doSomething';
|
||||
}
|
||||
|
||||
class MyConcreteClass extends MyAbstractClass implements InterfaceTwo
|
||||
{
|
||||
public function doSomething()
|
||||
{
|
||||
echo $x;
|
||||
}
|
||||
|
||||
public function doSomethingElse()
|
||||
{
|
||||
echo 'doSomethingElse';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 一个类可以实现多个接口
|
||||
class SomeOtherClass implements InterfaceOne, InterfaceTwo
|
||||
{
|
||||
public function doSomething()
|
||||
{
|
||||
echo 'doSomething';
|
||||
}
|
||||
|
||||
public function doSomethingElse()
|
||||
{
|
||||
echo 'doSomethingElse';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
* 特征
|
||||
*/
|
||||
|
||||
// 特征 从 PHP 5.4.0 开始包括,需要用 "trait" 这个关键字声明
|
||||
|
||||
trait MyTrait
|
||||
{
|
||||
public function myTraitMethod()
|
||||
{
|
||||
print 'I have MyTrait';
|
||||
}
|
||||
}
|
||||
|
||||
class MyTraitfulClass
|
||||
{
|
||||
use MyTrait;
|
||||
}
|
||||
|
||||
$cls = new MyTraitfulClass();
|
||||
$cls->myTraitMethod(); // 输出 "I have MyTrait"
|
||||
|
||||
|
||||
/********************************
|
||||
* 命名空间
|
||||
*/
|
||||
|
||||
// 这部分是独立于这个文件的
|
||||
// 因为命名空间必须在一个文件的开始处。
|
||||
|
||||
<?php
|
||||
|
||||
// 类会被默认的放在全局命名空间中,可以被一个\来显式调用
|
||||
|
||||
$cls = new \MyClass();
|
||||
|
||||
|
||||
|
||||
// 为一个文件设置一个命名空间
|
||||
namespace My\Namespace;
|
||||
|
||||
class MyClass
|
||||
{
|
||||
}
|
||||
|
||||
// (或者从其他文件中)
|
||||
$cls = new My\Namespace\MyClass;
|
||||
|
||||
//或者从其他命名空间中
|
||||
namespace My\Other\Namespace;
|
||||
|
||||
use My\Namespace\MyClass;
|
||||
|
||||
$cls = new MyClass();
|
||||
|
||||
// 你也可以为命名空间起一个别名
|
||||
|
||||
namespace My\Other\Namespace;
|
||||
|
||||
use My\Namespace as SomeOtherNamespace;
|
||||
|
||||
$cls = new SomeOtherNamespace\MyClass();
|
||||
|
||||
*/
|
||||
|
||||
```
|
||||
|
||||
## 更多阅读
|
||||
|
||||
访问 [PHP 官方文档](http://www.php.net/manual/)
|
||||
|
||||
如果你对最佳实践感兴趣(实时更新) [PHP The Right Way](http://www.phptherightway.com/).
|
||||
|
||||
如果你很熟悉善于包管理的语言 [Composer](http://getcomposer.org/).
|
||||
|
||||
如要了解通用标准,请访问PHP Framework Interoperability Group's [PSR standards](https://github.com/php-fig/fig-standards).
|
Reference in New Issue
Block a user