Git
最后更新于:2022-04-01 05:33:30
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=git
Git是一个分布式版本控制及源代码管理工具
Git可以为你的项目保存若干快照,以此来对整个项目进行版本管理
## 版本
### 什么是版本控制
版本控制系统就是根据时间来记录一个或多个文件的更改情况的系统。
### 集中式版本控制 VS 分布式版本控制
* 集中式版本控制的主要功能为同步,跟踪以及备份文件
* 分布式版本控制则更注重共享更改。每一次更改都有唯一的标识
* 分布式系统没有预定的结构。你也可以用git很轻松的实现SVN风格的集中式系统控制
[更多信息](http://git-scm.com/book/en/Getting-Started-About-Version-Control)
### 为什么要使用Git
* 可以离线工作
* 和他人协同工作变得简单
* 分支很轻松
* 合并很容易
* Git系统速度快,也很灵活
## Git 架构
### 版本库
一系列文件,目录,历史记录,提交记录和头指针。 可以把它视作每个源代码文件都带有历史记录属性数据结构
一个Git版本库包括一个 .git 目录和其工作目录
### .git 目录(版本库的一部分)
.git 目录包含所有的配置、日志、分支信息、头指针等 [详细列表](http://gitready.com/advanced/2009/03/23/whats-inside-your-git-directory.html)
### 工作目录 (版本库的一部分)
版本库中的目录和文件,可以看做就是你工作时的目录
### 索引(.git 目录)
索引就是git中的 staging 区. 可以算作是把你的工作目录与Git版本库分割开的一层 这使得开发者能够更灵活的决定要将要在版本库中添加什么内容
### 提交
一个 git 提交就是一组更改或者对工作目录操作的快照 比如你添加了5个文件,删除了2个文件,那么这些变化就会被写入一个提交比如你添加了5个文件,删除了2个文件,那么这些变化就会被写入一个提交中 而这个提交之后也可以被决定是否推送到另一个版本库中
### 分支
分支其实就是一个指向你最后一次的提交的指针 当你提交时,这个指针就会自动指向最新的提交
### 头指针 与 头(.git 文件夹的作用)
头指针是一个指向当前分支的指针,一个版本库只有一个当前活动的头指针 而头则可以指向版本库中任意一个提交,每个版本库也可以有多个头
### 其他形象化解释
* [给计算机科学家的解释](http://eagain.net/articles/git-for-computer-scientists/)
* [给设计师的解释](http://hoth.entp.com/output/git_for_designers.html)
## 命令
### 初始化
创建一个新的git版本库。这个版本库的配置、存储等信息会被保存到.git文件夹中
~~~
$ git init
~~~
### 配置
更改设置。可以是版本库的设置,也可以是系统的或全局的
~~~
# 输出、设置基本的全局变量
$ git config --global user.email
$ git config --global user.name
$ git config --global user.email "MyEmail@Zoho.com"
$ git config --global user.name "My Name"
~~~
[关于git的更多设置](http://git-scm.com/docs/git-config)
### 帮助
git内置了对命令非常详细的解释,可以供我们快速查阅
~~~
# 查找可用命令
$ git help
# 查找所有可用命令
$ git help -a
# 在文档当中查找特定的命令
# git help <命令>
$ git help add
$ git help commit
$ git help init
~~~
### 状态
显示索引文件(也就是当前工作空间)和当前的头指针指向的提交的不同
~~~
# 显示分支,为跟踪文件,更改和其他不同
$ git status
# 查看其他的git status的用法
$ git help status
~~~
### 添加
添加文件到当前工作空间中。如果你不使用 `git add` 将文件添加进去, 那么这些文件也不会添加到之后的提交之中
~~~
# 添加一个文件
$ git add HelloWorld.java
# 添加一个子目录中的文件
$ git add /path/to/file/HelloWorld.c
# 支持正则表达式
$ git add ./*.java
~~~
### 分支
管理分支,可以通过下列命令对分支进行增删改查
~~~
# 查看所有的分支和远程分支
$ git branch -a
# 创建一个新的分支
$ git branch myNewBranch
# 删除一个分支
$ git branch -d myBranch
# 重命名分支
# git branch -m <旧名称> <新名称>
$ git branch -m myBranchName myNewBranchName
# 编辑分支的介绍
$ git branch myBranchName --edit-description
~~~
### 检出
将当前工作空间更新到索引所标识的或者某一特定的工作空间
~~~
# 检出一个版本库,默认将更新到master分支
$ git checkout
# 检出到一个特定的分支
$ git checkout branchName
# 新建一个分支,并且切换过去,相当于"git branch <名字>; git checkout <名字>"
$ git checkout -b newBranch
~~~
### clone
这个命令就是将一个版本库拷贝到另一个目录中,同时也将 分支都拷贝到新的版本库中。这样就可以在新的版本库中提交到远程分支
~~~
# clone learnxinyminutes-docs
$ git clone https://github.com/adambard/learnxinyminutes-docs.git
~~~
### commit
将当前索引的更改保存为一个新的提交,这个提交包括用户做出的更改与信息
~~~
# 提交时附带提交信息
$ git commit -m "Added multiplyNumbers() function to HelloWorld.c"
~~~
### diff
显示当前工作空间和提交的不同
~~~
# 显示工作目录和索引的不同
$ git diff
# 显示索引和最近一次提交的不同
$ git diff --cached
# 显示工作目录和最近一次提交的不同
$ git diff HEAD
~~~
### grep
可以在版本库中快速查找
可选配置:
~~~
# 感谢Travis Jeffery提供的以下用法:
# 在搜索结果中显示行号
$ git config --global grep.lineNumber true
# 是搜索结果可读性更好
$ git config --global alias.g "grep --break --heading --line-number"
~~~
~~~
# 在所有的java中查找variableName
$ git grep 'variableName' -- '*.java'
# 搜索包含 "arrayListName" 和, "add" 或 "remove" 的所有行
$ git grep -e 'arrayListName' --and \( -e add -e remove \)
~~~
更多的例子可以查看: [Git Grep Ninja](http://travisjeffery.com/b/2012/02/search-a-git-repo-like-a-ninja)
### log
显示这个版本库的所有提交
~~~
# 显示所有提交
$ git log
# 显示某几条提交信息
$ git log -n 10
# 仅显示合并提交
$ git log --merges
~~~
### merge
合并就是将外部的提交合并到自己的分支中
~~~
# 将其他分支合并到当前分支
$ git merge branchName
# 在合并时创建一个新的合并后的提交
$ git merge --no-ff branchName
~~~
### mv
重命名或移动一个文件
~~~
# 重命名
$ git mv HelloWorld.c HelloNewWorld.c
# 移动
$ git mv HelloWorld.c ./new/path/HelloWorld.c
# 强制重命名或移动
# 这个文件已经存在,将要覆盖掉
$ git mv -f myFile existingFile
~~~
### pull
从远端版本库合并到当前分支
~~~
# 从远端origin的master分支更新版本库
# git pull <远端> <分支>
$ git pull origin master
~~~
### push
把远端的版本库更新
~~~
# 把本地的分支更新到远端origin的master分支上
# git push <远端> <分支>
# git push 相当于 git push origin master
$ git push origin master
~~~
### rebase (谨慎使用)
将一个分支上所有的提交历史都应用到另一个分支上 *不要在一个已经公开的远端分支上使用rebase*.
~~~
# 将experimentBranch应用到master上面
# git rebase <basebranch> <topicbranch>
$ git rebase master experimentBranch
~~~
[更多阅读](http://git-scm.com/book/en/Git-Branching-Rebasing)
### reset (谨慎使用)
将当前的头指针复位到一个特定的状态。这样可以使你撤销merge、pull、commits、add等 这是个很强大的命令,但是在使用时一定要清楚其所产生的后果
~~~
# 使 staging 区域恢复到上次提交时的状态,不改变现在的工作目录
$ git reset
# 使 staging 区域恢复到上次提交时的状态,覆盖现在的工作目录
$ git reset --hard
# 将当前分支恢复到某次提交,不改变现在的工作目录
# 在工作目录中所有的改变仍然存在
$ git reset 31f2bb1
# 将当前分支恢复到某次提交,覆盖现在的工作目录
# 并且删除所有未提交的改变和指定提交之后的所有提交
$ git reset --hard 31f2bb1
~~~
### rm
和add相反,从工作空间中去掉某个文件
~~~
# 移除 HelloWorld.c
$ git rm HelloWorld.c
# 移除子目录中的文件
$ git rm /pather/to/the/file/HelloWorld.c
~~~
## 更多阅读
* [tryGit - 学习Git的有趣方式](http://try.github.io/levels/1/challenges/1)
* [git-scm - 视频教程](http://git-scm.com/videos)
* [git-scm - 文档](http://git-scm.com/docs)
* [Atlassian Git - 教程与工作流程](https://www.atlassian.com/git/)
* [SalesForce Cheat Sheet](https://na1.salesforce.com/help/doc/en/salesforce_git_developer_cheatsheet.pdf)
* [GitGuys](http://www.gitguys.com/)
Bash
最后更新于:2022-04-01 05:33:28
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=bash
源代码下载: [LearnBash-cn.sh](http://learnxinyminutes.com/docs/files/LearnBash-cn.sh)
Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 Mac OS X 下的默认 shell。 以下大多数例子可以作为脚本的一部分运行,也可直接在 shell 下交互执行。
[更多信息](http://www.gnu.org/software/bash/manual/bashref.html)
~~~
#!/bin/bash
# 脚本的第一行叫 shebang,用来告知系统如何执行该脚本:
# 参见: http://en.wikipedia.org/wiki/Shebang_(Unix)
# 如你所见,注释以 # 开头,shebang 也是注释。
# 显示 “Hello world!”
echo Hello world!
# 每一句指令以换行或分号隔开:
echo 'This is the first line'; echo 'This is the second line'
# 声明一个变量:
Variable="Some string"
# 下面是错误的做法:
Variable = "Some string"
# Bash 会把 Variable 当做一个指令,由于找不到该指令,因此这里会报错。
# 也不可以这样:
Variable= 'Some string'
# Bash 会认为 'Some string' 是一条指令,由于找不到该指令,这里再次报错。
# (这个例子中 'Variable=' 这部分会被当作仅对 'Some string' 起作用的赋值。)
# 使用变量:
echo $Variable
echo "$Variable"
echo '$Variable'
# 当你赋值 (assign) 、导出 (export),或者以其他方式使用变量时,变量名前不加 $。
# 如果要使用变量的值, 则要加 $。
# 注意: ' (单引号) 不会展开变量(即会屏蔽掉变量)。
# 在变量内部进行字符串代换
echo ${Variable/Some/A}
# 会把 Variable 中首次出现的 "some" 替换成 “A”。
# 变量的截取
Length=7
echo ${Variable:0:Length}
# 这样会仅返回变量值的前7个字符
# 变量的默认值
echo ${Foo:-"DefaultValueIfFooIsMissingOrEmpty"}
# 对 null (Foo=) 和空串 (Foo="") 起作用; 零(Foo=0)时返回0
# 注意这仅返回默认值而不是改变变量的值
# 内置变量:
# 下面的内置变量很有用
echo "Last program return value: $?"
echo "Script's PID: $$"
echo "Number of arguments: $#"
echo "Scripts arguments: $@"
echo "Scripts arguments separated in different variables: $1 $2..."
# 读取输入:
echo "What's your name?"
read Name # 这里不需要声明新变量
echo Hello, $Name!
# 通常的 if 结构看起来像这样:
# 'man test' 可查看更多的信息
if [ $Name -ne $USER ]
then
echo "Your name isn't your username"
else
echo "Your name is your username"
fi
# 根据上一个指令执行结果决定是否执行下一个指令
echo "Always executed" || echo "Only executed if first command fails"
echo "Always executed" && echo "Only executed if first command does NOT fail"
# 在 if 语句中使用 && 和 || 需要多对方括号
if [ $Name == "Steve" ] && [ $Age -eq 15 ]
then
echo "This will run if $Name is Steve AND $Age is 15."
fi
if [ $Name == "Daniya" ] || [ $Name == "Zach" ]
then
echo "This will run if $Name is Daniya OR Zach."
fi
# 表达式的格式如下:
echo $(( 10 + 5 ))
# 与其他编程语言不同的是,bash 运行时依赖上下文。比如,使用 ls 时,列出当前目录。
ls
# 指令可以带有选项:
ls -l # 列出文件和目录的详细信息
# 前一个指令的输出可以当作后一个指令的输入。grep 用来匹配字符串。
# 用下面的指令列出当前目录下所有的 txt 文件:
ls -l | grep "\.txt"
# 重定向输入和输出(标准输入,标准输出,标准错误)。
# 以 ^EOF$ 作为结束标记从标准输入读取数据并覆盖 hello.py :
cat > hello.py << EOF
#!/usr/bin/env python
from __future__ import print_function
import sys
print("#stdout", file=sys.stdout)
print("#stderr", file=sys.stderr)
for line in sys.stdin:
print(line, file=sys.stdout)
EOF
# 重定向可以到输出,输入和错误输出。
python hello.py < "input.in"
python hello.py > "output.out"
python hello.py 2> "error.err"
python hello.py > "output-and-error.log" 2>&1
python hello.py > /dev/null 2>&1
# > 会覆盖已存在的文件, >> 会以累加的方式输出文件中。
python hello.py >> "output.out" 2>> "error.err"
# 覆盖 output.out , 追加 error.err 并统计行数
info bash 'Basic Shell Features' 'Redirections' > output.out 2>> error.err
wc -l output.out error.err
# 运行指令并打印文件描述符 (比如 /dev/fd/123)
# 具体可查看: man fd
echo <(echo "#helloworld")
# 以 "#helloworld" 覆盖 output.out:
cat > output.out <(echo "#helloworld")
echo "#helloworld" > output.out
echo "#helloworld" | cat > output.out
echo "#helloworld" | tee output.out >/dev/null
# 清理临时文件并显示详情(增加 '-i' 选项启用交互模式)
rm -v output.out error.err output-and-error.log
# 一个指令可用 $( ) 嵌套在另一个指令内部:
# 以下的指令会打印当前目录下的目录和文件总数
echo "There are $(ls | wc -l) items here."
# 反引号 `` 起相同作用,但不允许嵌套
# 优先使用 $( ).
echo "There are `ls | wc -l` items here."
# Bash 的 case 语句与 Java 和 C++ 中的 switch 语句类似:
case "$Variable" in
# 列出需要匹配的字符串
0) echo "There is a zero.";;
1) echo "There is a one.";;
*) echo "It is not null.";;
esac
# 循环遍历给定的参数序列:
# 变量$Variable 的值会被打印 3 次。
for Variable in {1..3}
do
echo "$Variable"
done
# 或传统的 “for循环” :
for ((a=1; a <= 3; a++))
do
echo $a
done
# 也可以用于文件
# 用 cat 输出 file1 和 file2 内容
for Variable in file1 file2
do
cat "$Variable"
done
# 或作用于其他命令的输出
# 对 ls 输出的文件执行 cat 指令。
for Output in $(ls)
do
cat "$Output"
done
# while 循环:
while [ true ]
do
echo "loop body here..."
break
done
# 你也可以使用函数
# 定义函数:
function foo ()
{
echo "Arguments work just like script arguments: $@"
echo "And: $1 $2..."
echo "This is a function"
return 0
}
# 更简单的方法
bar ()
{
echo "Another way to declare functions!"
return 0
}
# 调用函数
foo "My name is" $Name
# 有很多有用的指令需要学习:
# 打印 file.txt 的最后 10 行
tail -n 10 file.txt
# 打印 file.txt 的前 10 行
head -n 10 file.txt
# 将 file.txt 按行排序
sort file.txt
# 报告或忽略重复的行,用选项 -d 打印重复的行
uniq -d file.txt
# 打印每行中 ',' 之前内容
cut -d ',' -f 1 file.txt
# 将 file.txt 文件所有 'okay' 替换为 'great', (兼容正则表达式)
sed -i 's/okay/great/g' file.txt
# 将 file.txt 中匹配正则的行打印到标准输出
# 这里打印以 "foo" 开头, "bar" 结尾的行
grep "^foo.*bar$" file.txt
# 使用选项 "-c" 统计行数
grep -c "^foo.*bar$" file.txt
# 如果只是要按字面形式搜索字符串而不是按正则表达式,使用 fgrep (或 grep -F)
fgrep "^foo.*bar$" file.txt
# 以 bash 内建的 'help' 指令阅读 Bash 自带文档:
help
help help
help for
help return
help source
help .
# 用 mam 指令阅读相关的 Bash 手册
apropos bash
man 1 bash
man bash
# 用 info 指令查阅命令的 info 文档 (info 中按 ? 显示帮助信息)
apropos info | grep '^info.*('
man info
info info
info 5 info
# 阅读 Bash 的 info 文档:
info bash
info bash 'Bash Features'
info bash 6
info --apropos bash
~~~
工具类
最后更新于:2022-04-01 05:33:25
Yaml
最后更新于:2022-04-01 05:33:23
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=yaml
源代码下载: [learnyaml-cn.yaml](http://learnxinyminutes.com/docs/files/learnyaml-cn.yaml)
YAML是一个数据序列化语言,被设计成人类直接可写可读的。
它是JSON的严格超集,增加了语法显著换行符和缩进,就像Python。但和Python不一样, YAML根本不容许文字制表符。
~~~
# YAML中的注解看起来像这样。
################
# 标量类型 #
################
# 我们的根对象 (它们在整个文件里延续) 将会是一个地图,
# 它等价于在别的语言里的一个字典,哈西表或对象。
key: value
another_key: Another value goes here.
a_number_value: 100
scientific_notation: 1e+12
boolean: true
null_value: null
key with spaces: value
# 注意到字符串不需要被引用。但是,它们可以被引用。
"Keys can be quoted too.": "Useful if you want to put a ':' in your key."
# 多行字符串既可以写成像一个'文字块'(使用 |),
# 或像一个'折叠块'(使用 '>')。
literal_block: |
This entire block of text will be the value of the 'literal_block' key,
with line breaks being preserved.
The literal continues until de-dented, and the leading indentation is
stripped.
Any lines that are 'more-indented' keep the rest of their indentation -
these lines will be indented by 4 spaces.
folded_style: >
This entire block of text will be the value of 'folded_style', but this
time, all newlines will be replaced with a single space.
Blank lines, like above, are converted to a newline character.
'More-indented' lines keep their newlines, too -
this text will appear over two lines.
####################
# 集合类型 #
####################
# 嵌套是通过缩进完成的。
a_nested_map:
key: value
another_key: Another Value
another_nested_map:
hello: hello
# 地图不用有字符串键值。
0.25: a float key
# 键值也可以是多行对象,用?表明键值的开始。
? |
This is a key
that has multiple lines
: and this is its value
# YAML也容许键值是集合类型,但是很多语言将会抱怨。
# 序列 (等价于表或数组) 看起来像这样:
a_sequence:
- Item 1
- Item 2
- 0.5 # 序列可以包含不同类型。
- Item 4
- key: value
another_key: another_value
-
- This is a sequence
- inside another sequence
# 因为YAML是JSON的超集,你也可以写JSON风格的地图和序列:
json_map: {"key": "value"}
json_seq: [3, 2, 1, "takeoff"]
#######################
# 其余的YAML特点 #
#######################
# YAML还有一个方便的特点叫'锚',它让你简单地在整个文件里重复内容。
# 两个键值将会有相同的值:
anchored_content: &anchor_name This string will appear as the value of two keys.
other_anchor: *anchor_name
# YAML还有标签,你可以用它显示地声明类型。
explicit_string: !!str 0.5
# 一些解析器实现特定语言的标签,就像这个为了Python的复数类型。
python_complex_number: !!python/complex 1+2j
####################
# 其余的YAML类型 #
####################
# 字符串和数字不是仅有的YAML可以理解的标量。
# ISO 格式的日期和日期时间文字也是可以被解析的。
datetime: 2001-12-15T02:59:43.1Z
datetime_with_spaces: 2001-12-14 21:59:43.10 -5
date: 2002-12-14
# 这个!!binary标签表明一个字符串实际上是一个二进制blob的base64编码表示。
gif_file: !!binary |
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
# YAML还有一个集合类型,它看起来像这样:
set:
? item1
? item2
? item3
# 像Python一样,集合仅是有null数值的地图;上面的集合等价于:
set2:
item1: null
item2: null
item3: null
~~~
XML
最后更新于:2022-04-01 05:33:21
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=xml
源代码下载: [learnxml-cn.xml](http://learnxinyminutes.com/docs/files/learnxml-cn.xml)
XML是一种标记语言,被设计用来存储数据和传输数据。
不像HTML, XML不指定怎样显示或格式化数据,只是携带它。
* XML 语法
~~~
<!-- XML中的注解像这样 -->
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
<!-- 上面是一个典型的XML文件。
它以一个声明开始,通知一些元数据(自选的)
XML使用一个树的结构。上面的文件中,根节点是'bookstore',它有三个孩子节点,
所有的'books'。那些节点有更多的孩子节点,等等。。。
节点用开放/关闭标签创建, 并且孩子就是在开发和关闭标签之间的节点。-->
<!-- XML 携带两类信息:
1 - 属性 -> 那是关于一个元素的元数据。
通常,XML解析器使用这些信息去正确地存储数据。
它通过在开放标签里出现在插入语中来表示。
2 - 元素 -> 那是纯数据。
那就是解析器将从XML文件提取的东西。
元素出现在开放和关闭标签之间,没插入语。-->
<!-- 下面, 一个有两个属性的元素-->
<file type="gif" id="4293">computer.gif</file>
~~~
* 良好格式的文件 x 验证
一个XML文件是良好格式的如果它是语法正确的。 但是, 使用文件定义,比如DTD和XML概要,在文件中插入更多的限制是可能的。
一个遵守一个文件定义的XML文件被叫做有效的,对于那个文件来说。
有了这个工具,你能够在应用逻辑之外检查XML数据。
~~~
<!-- 下面, 你能够看到一个简化版本的增加了DTD定义的bookstore文件。-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note SYSTEM "Bookstore.dtd">
<bookstore>
<book category="COOKING">
<title >Everyday Italian</title>
<price>30.00</price>
</book>
</bookstore>
<!-- 这个DTD可能是像这样的:-->
<!DOCTYPE note
[
<!ELEMENT bookstore (book+)>
<!ELEMENT book (title,price)>
<!ATTLIST book category CDATA "Literature">
<!ELEMENT title (#PCDATA)>
<!ELEMENT price (#PCDATA)>
]>
<!-- 这个DTD以一个声明开始。
接下来, 根节点被声明, 它需要一个或多个孩子节点'book'。
每个 'book' 应该准确包含一个 'title' 和 'price' 和
一个被叫做'category'的缺省值为"Literature"的属性。
这个'title' 和 'price'节点包含一个解析过的字符数据。-->
<!-- 这个DTD可以在XML文件中本身被声明。-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note
[
<!ELEMENT bookstore (book+)>
<!ELEMENT book (title,price)>
<!ATTLIST book category CDATA "Literature">
<!ELEMENT title (#PCDATA)>
<!ELEMENT price (#PCDATA)>
]>
<bookstore>
<book category="COOKING">
<title >Everyday Italian</title>
<price>30.00</price>
</book>
</bookstore>
~~~
Visual Basic
最后更新于:2022-04-01 05:33:18
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=Visual Basic
源代码下载: [learnvisualbasic.vb-cn](http://learnxinyminutes.com/docs/files/learnvisualbasic.vb-cn)
~~~
Module Module1
Sub Main()
' 让我们先从简单的终端程序学起。
' 单引号用来生成注释(注意是半角单引号,非全角单引号’)
' 为了方便运行此示例代码,我写了个目录索引。
' 可能你还不了解以下代码的意义,但随着教程的深入,
' 你会渐渐理解其用法。
Console.Title = ("Learn X in Y Minutes")
Console.WriteLine("NAVIGATION") ' 显示目录
Console.WriteLine("")
Console.ForegroundColor = ConsoleColor.Green
Console.WriteLine("1\. Hello World Output") ' Hello world 输出示例
Console.WriteLine("2\. Hello World Input") ' Hello world 输入示例
Console.WriteLine("3\. Calculating Whole Numbers") ' 求整数之和
Console.WriteLine("4\. Calculating Decimal Numbers") ' 求小数之和
Console.WriteLine("5\. Working Calculator") ' 计算器
Console.WriteLine("6\. Using Do While Loops") ' 使用 Do While 循环
Console.WriteLine("7\. Using For While Loops") ' 使用 For While 循环
Console.WriteLine("8\. Conditional Statements") ' 条件语句
Console.WriteLine("9\. Select A Drink") ' 选饮料
Console.WriteLine("50\. About") ' 关于
Console.WriteLine("Please Choose A Number From The Above List")
Dim selection As String = Console.ReadLine
Select Case selection
Case "1" ' Hello world 输出示例
Console.Clear() ' 清空屏幕
HelloWorldOutput() ' 调用程序块
Case "2" ' Hello world 输入示例
Console.Clear()
HelloWorldInput()
Case "3" ' 求整数之和
Console.Clear()
CalculatingWholeNumbers()
Case "4" ' 求小数之和
Console.Clear()
CalculatingDecimalNumbers()
Case "5" ' 计算器
Console.Clear()
WorkingCalculator()
Case "6" ' 使用 do while 循环
Console.Clear()
UsingDoWhileLoops()
Case "7" ' 使用 for while 循环
Console.Clear()
UsingForLoops()
Case "8" ' 条件语句
Console.Clear()
ConditionalStatement()
Case "9" ' If/Else 条件语句
Console.Clear()
IfElseStatement() ' 选饮料
Case "50" ' 关于本程序和作者
Console.Clear()
Console.Title = ("Learn X in Y Minutes :: About")
MsgBox("This tutorial is by Brian Martin (@BrianMartinn")
Console.Clear()
Main()
Console.ReadLine()
End Select
End Sub
' 一、对应程序目录1,下同
' 使用 private subs 声明函数。
Private Sub HelloWorldOutput()
' 程序名
Console.Title = "Hello World Ouput | Learn X in Y Minutes"
' 使用 Console.Write("") 或者 Console.WriteLine("") 来输出文本到屏幕上
' 对应的 Console.Read() 或 Console.Readline() 用来读取键盘输入
Console.WriteLine("Hello World")
Console.ReadLine()
' Console.WriteLine()后加Console.ReadLine()是为了防止屏幕输出信息一闪而过
' 类似平时常见的“单击任意键继续”的意思。
End Sub
' 二
Private Sub HelloWorldInput()
Console.Title = "Hello World YourName | Learn X in Y Minutes"
' 变量
' 用来存储用户输入的数据
' 变量声明以 Dim 开始,结尾为 As VariableType (变量类型).
' 此教程中,我们希望知道你的姓名,并让程序记录并输出。
Dim username As String
' 我们定义username使用字符串类型(String)来记录用户姓名。
Console.WriteLine("Hello, What is your name? ") ' 询问用户输入姓名
username = Console.ReadLine() ' 存储用户名到变量 username
Console.WriteLine("Hello " + username) ' 输出将是 Hello + username
Console.ReadLine() ' 暂停屏幕并显示以上输出
' 以上程序将询问你的姓名,并和你打招呼。
' 其它变量如整型(Integer)我们用整型来处理整数。
End Sub
' 三
Private Sub CalculatingWholeNumbers()
Console.Title = "Calculating Whole Numbers | Learn X in Y Minutes"
Console.Write("First number: ") ' 输入一个整数:1,2,50,104,等等
Dim a As Integer = Console.ReadLine()
Console.Write("Second number: ") ' 输入第二个整数
Dim b As Integer = Console.ReadLine()
Dim c As Integer = a + b
Console.WriteLine(c)
Console.ReadLine()
' 以上程序将两个整数相加
End Sub
' 四
Private Sub CalculatingDecimalNumbers()
Console.Title = "Calculating with Double | Learn X in Y Minutes"
' 当然,我们还需要能够处理小数。
' 只需要要将整型(Integer)改为小数(Double)类型即可。
' 输入一个小数: 1.2, 2.4, 50.1, 104.9,等等
Console.Write("First number: ")
Dim a As Double = Console.ReadLine
Console.Write("Second number: ") ' 输入第二个数
Dim b As Double = Console.ReadLine
Dim c As Double = a + b
Console.WriteLine(c)
Console.ReadLine()
' 以上代码能实现两个小数相加
End Sub
' 五
Private Sub WorkingCalculator()
Console.Title = "The Working Calculator| Learn X in Y Minutes"
' 但是如果你希望有个能够处理加减乘除的计算器呢?
' 只需将上面代码复制粘帖即可。
Console.Write("First number: ") ' 输入第一个数
Dim a As Double = Console.ReadLine
Console.Write("Second number: ") ' 输入第二个数
Dim b As Integer = Console.ReadLine
Dim c As Integer = a + b
Dim d As Integer = a * b
Dim e As Integer = a - b
Dim f As Integer = a / b
' 通过以下代码我们可以将以上所算的加减乘除结果输出到屏幕上。
Console.Write(a.ToString() + " + " + b.ToString())
' 我们希望答案开头能有3个空格,可以使用String.PadLeft(3)方法。
Console.WriteLine(" = " + c.ToString.PadLeft(3))
Console.Write(a.ToString() + " * " + b.ToString())
Console.WriteLine(" = " + d.ToString.PadLeft(3))
Console.Write(a.ToString() + " - " + b.ToString())
Console.WriteLine(" = " + e.ToString.PadLeft(3))
Console.Write(a.ToString() + " / " + b.ToString())
Console.WriteLine(" = " + e.ToString.PadLeft(3))
Console.ReadLine()
End Sub
' 六
Private Sub UsingDoWhileLoops()
' 如同以上的代码一样
' 这次我们将询问用户是否继续 (Yes or No?)
' 我们将使用Do While循环,因为我们不知到用户是否需要使用一次以上。
Console.Title = "UsingDoWhileLoops | Learn X in Y Minutes"
Dim answer As String ' 我们使用字符串变量来存储answer(答案)
Do ' 循环开始
Console.Write("First number: ")
Dim a As Double = Console.ReadLine
Console.Write("Second number: ")
Dim b As Integer = Console.ReadLine
Dim c As Integer = a + b
Dim d As Integer = a * b
Dim e As Integer = a - b
Dim f As Integer = a / b
Console.Write(a.ToString() + " + " + b.ToString())
Console.WriteLine(" = " + c.ToString.PadLeft(3))
Console.Write(a.ToString() + " * " + b.ToString())
Console.WriteLine(" = " + d.ToString.PadLeft(3))
Console.Write(a.ToString() + " - " + b.ToString())
Console.WriteLine(" = " + e.ToString.PadLeft(3))
Console.Write(a.ToString() + " / " + b.ToString())
Console.WriteLine(" = " + e.ToString.PadLeft(3))
Console.ReadLine()
' 询问用户是否继续,注意大小写。
Console.Write("Would you like to continue? (yes / no)")
' 程序读入用户输入
answer = Console.ReadLine() ' added a bracket here
' 当用户输入"yes"时,程序将跳转到Do,并再次执行
Loop While answer = "yes"
End Sub
' 七
Private Sub UsingForLoops()
' 有一些程序只需要运行一次。
' 这个程序我们将实现从10倒数计数.
Console.Title = "Using For Loops | Learn X in Y Minutes"
' 声明变量和Step (步长,即递减的速度,如-1,-2,-3等)。
For i As Integer = 10 To 0 Step -1
Console.WriteLine(i.ToString) ' 将计数结果输出的屏幕
Next i ' 计算新的i值
Console.WriteLine("Start")
Console.ReadLine()
End Sub
' 八
Private Sub ConditionalStatement()
Console.Title = "Conditional Statements | Learn X in Y Minutes"
Dim userName As String = Console.ReadLine
Console.WriteLine("Hello, What is your name? ") ' 询问用户姓名
userName = Console.ReadLine() ' 存储用户姓名
If userName = "Adam" Then
Console.WriteLine("Hello Adam")
Console.WriteLine("Thanks for creating this useful site")
Console.ReadLine()
Else
Console.WriteLine("Hello " + userName)
Console.WriteLine("Have you checked out www.learnxinyminutes.com")
Console.ReadLine() ' 程序停止,并输出以上文本
End If
End Sub
' 九
Private Sub IfElseStatement()
Console.Title = "If / Else Statement | Learn X in Y Minutes"
' 有时候我们需要考虑多于两种情况。
' 这时我们就需要使用If/ElesIf条件语句。
' If语句就好似个自动售货机,当用户输入A1,A2,A3,等去选择物品时,
' 所有的选择可以合并到一个If语句中
Dim selection As String = Console.ReadLine() ' 读入用户选择
Console.WriteLine("A1\. for 7Up") ' A1 七喜
Console.WriteLine("A2\. for Fanta") ' A2 芬达
Console.WriteLine("A3\. for Dr. Pepper") ' A3 胡椒医生
Console.WriteLine("A4\. for Diet Coke") ' A4 无糖可乐
Console.ReadLine()
If selection = "A1" Then
Console.WriteLine("7up")
Console.ReadLine()
ElseIf selection = "A2" Then
Console.WriteLine("fanta")
Console.ReadLine()
ElseIf selection = "A3" Then
Console.WriteLine("dr. pepper")
Console.ReadLine()
ElseIf selection = "A4" Then
Console.WriteLine("diet coke")
Console.ReadLine()
Else
Console.WriteLine("Please select a product") ' 请选择你需要的产品
Console.ReadLine()
End If
End Sub
End Module
~~~
## 参考
我(译注:原作者)在命令行下学习的VB。命令行编程使我能够更好的了解程序编译运行机制,并使学习其它语言变得容易。
如果希望进一步学习VB,这里还有更深层次的 [VB教学(英文)](http://www.vbbootcamp.co.uk/ "VB教学")。
所有代码均通过测试。只需复制粘帖到Visual Basic中,并按F5运行即可。
Swift
最后更新于:2022-04-01 05:33:16
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=swift
源代码下载: [learnswift-cn.swift](http://learnxinyminutes.com/docs/files/learnswift-cn.swift)
Swift 是 Apple 开发的用于 iOS 和 OS X 开发的编程语言。Swift 于2014年 Apple WWDC (全球开发者大会)中被引入,用以与 Objective-C 共存,同时对错误代码更具弹性。Swift 由 Xcode 6 beta 中包含的 LLVM 编译器编译。
Swift 的官方语言教程 [Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329) 可以从 iBooks 免费下载.
亦可参阅:Apple’s [getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/RoadMapiOS/index.html) ——一个完整的Swift 教程
~~~
// 导入外部模块
import UIKit
//
// MARK: 基础
//
// XCODE 支持给注释代码作标记,这些标记会列在 XCODE 的跳转栏里,支持的标记为
// MARK: 普通标记
// TODO: TODO 标记
// FIXME: FIXME 标记
println("Hello, world")
// 变量 (var) 的值设置后可以随意改变
// 常量 (let) 的值设置后不能改变
var myVariable = 42
let øπΩ = "value" // 可以支持 unicode 变量名
let π = 3.1415926
let myConstant = 3.1415926
let explicitDouble: Double = 70 // 明确指定变量类型为 Double ,否则编译器将自动推断变量类型
let weak = "keyword"; let override = "another keyword" // 语句之间可以用分号隔开,语句未尾不需要分号
let intValue = 0007 // 7
let largeIntValue = 77_000 // 77000
let label = "some text " + String(myVariable) // 类型转换
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // 格式化字符串
// 条件编译
// 使用 -D 定义编译开关
#if false
println("Not printed")
let buildValue = 3
#else
let buildValue = 7
#endif
println("Build value: \(buildValue)") // Build value: 7
/*
Optionals 是 Swift 的新特性,它允许你存储两种状态的值给 Optional 变量:有效值或 None
Swift 要求所有的 Optinal 属性都必须有明确的值,如果为空,则必须明确设定为 nil
Optional<T> 是个枚举类型
*/
var someOptionalString: String? = "optional" // 可以是 nil
// 下面的语句和上面完全等价,上面的写法更推荐,因为它更简洁,问号 (?) 是 Swift 提供的语法糖
var someOptionalString2: Optional<String> = "optional"
if someOptionalString != nil {
// 变量不为空
if someOptionalString!.hasPrefix("opt") {
println("has the prefix")
}
let empty = someOptionalString?.isEmpty
}
someOptionalString = nil
// 显式解包 optional 变量
var unwrappedString: String! = "Value is expected."
// 下面语句和上面完全等价,感叹号 (!) 是个后缀运算符,这也是个语法糖
var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Value is expected."
if let someOptionalStringConstant = someOptionalString {
// 由于变量 someOptinalString 有值,不为空,所以 if 条件为真
if !someOptionalStringConstant.hasPrefix("ok") {
// does not have the prefix
}
}
// Swift 支持可保存任何数据类型的变量
// AnyObject == id
// 和 Objective-C `id` 不一样, AnyObject 可以保存任何类型的值 (Class, Int, struct, 等)
var anyObjectVar: AnyObject = 7
anyObjectVar = "Changed value to a string, not good practice, but possible."
/*
这里是注释
/*
支持嵌套的注释
*/
*/
//
// Mark: 数组与字典(关联数组)
//
/*
Array 和 Dictionary 是结构体,不是类,他们作为函数参数时,是用值传递而不是指针传递。
可以用 `var` 和 `let` 来定义变量和常量。
*/
// Array
var shoppingList = ["catfish", "water", "lemons"]
shoppingList[1] = "bottle of water"
let emptyArray = [String]() // 使用 let 定义常量,此时 emptyArray 数组不能添加或删除内容
let emptyArray2 = Array<String>() // 与上一语句等价,上一语句更常用
var emptyMutableArray = [String]() // 使用 var 定义变量,可以向 emptyMutableArray 添加数组元素
// 字典
var occupations = [
"Malcolm": "Captain",
"kaylee": "Mechanic"
]
occupations["Jayne"] = "Public Relations" // 修改字典,如果 key 不存在,自动添加一个字典元素
let emptyDictionary = [String: Float]() // 使用 let 定义字典常量,字典常量不能修改里面的值
let emptyDictionary2 = Dictionary<String, Float>() // 与上一语句类型等价,上一语句更常用
var emptyMutableDictionary = [String: Float]() // 使用 var 定义字典变量
//
// MARK: 控制流
//
// 数组的 for 循环
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
if value == 1 {
println("One!")
} else {
println("Not one!")
}
}
// 字典的 for 循环
var dict = ["one": 1, "two": 2]
for (key, value) in dict {
println("\(key): \(value)")
}
// 区间的 loop 循环:其中 `...` 表示闭环区间,即[-1, 3];`..<` 表示半开闭区间,即[-1,3)
for i in -1...shoppingList.count {
println(i)
}
shoppingList[1...2] = ["steak", "peacons"]
// 可以使用 `..<` 来去掉最后一个元素
// while 循环
var i = 1
while i < 1000 {
i *= 2
}
// do-while 循环
do {
println("hello")
} while 1 == 2
// Switch 语句
// Swift 里的 Switch 语句功能异常强大,结合枚举类型,可以实现非常简洁的代码,可以把 switch 语句想象成 `if` 的语法糖
// 它支持字符串,类实例或原生数据类型 (Int, Double, etc)
let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let localScopeValue where localScopeValue.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(localScopeValue)?"
default: // 在 Swift 里,switch 语句的 case 必须处理所有可能的情况,如果 case 无法全部处理,则必须包含 default语句
let vegetableComment = "Everything tastes good in soup."
}
//
// MARK: 函数
//
// 函数是一个 first-class 类型,他们可以嵌套,可以作为函数参数传递
// 函数文档可使用 reStructedText 格式直接写在函数的头部
/**
A greet operation
- A bullet in docs
- Another bullet in the docs
:param: name A name
:param: day A day
:returns: A string containing the name and day value.
*/
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
// 函数参数前带 `#` 表示外部参数名和内部参数名使用同一个名称。
// 第二个参数表示外部参数名使用 `externalParamName` ,内部参数名使用 `localParamName`
func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
return "Hello \(requiredName), the day is \(localParamName)"
}
greet2(requiredName:"John", externalParamName: "Sunday") // 调用时,使用命名参数来指定参数的值
// 函数可以通过元组 (tuple) 返回多个值
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
let pricesTuple = getGasPrices()
let price = pricesTuple.2 // 3.79
// 通过下划线 (_) 来忽略不关心的值
let (_, price1, _) = pricesTuple // price1 == 3.69
println(price1 == pricesTuple.1) // true
println("Gas price: \(price)")
// 可变参数
func setup(numbers: Int...) {
// 可变参数是个数组
let number = numbers[0]
let argCount = numbers.count
}
// 函数变量以及函数作为返回值返回
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
// 强制进行指针传递 (引用传递),使用 `inout` 关键字修饰函数参数
func swapTwoInts(inout a: Int, inout b: Int) {
let tempA = a
a = b
b = tempA
}
var someIntA = 7
var someIntB = 3
swapTwoInts(&someIntA, &someIntB)
println(someIntB) // 7
//
// MARK: 闭包
//
var numbers = [1, 2, 6]
// 函数是闭包的一个特例
// 闭包实例
// `->` 分隔了闭包的参数和返回值
// `in` 分隔了闭包头 (包括参数及返回值) 和闭包体
// 下面例子中,`map` 的参数是一个函数类型,它的功能是把数组里的元素作为参数,逐个调用 `map` 参数传递进来的函数。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
// 当闭包的参数类型和返回值都是己知的情况下,且只有一个语句作为其返回值时,我们可以简化闭包的写法
numbers = numbers.map({ number in 3 * number })
// 我们也可以使用 $0, $1 来指代第 1 个,第 2 个参数,上面的语句最终可简写为如下形式
// numbers = numbers.map({ $0 * 3 })
print(numbers) // [3, 6, 18]
// 简洁的闭包
numbers = sorted(numbers) { $0 > $1 }
// 函数的最后一个参数可以放在括号之外,上面的语句是这个语句的简写形式
// numbers = sorted(numbers, { $0 > $1 })
print(numbers) // [18, 6, 3]
// 超级简洁的闭包,因为 `<` 是个操作符函数
numbers = sorted(numbers, < )
print(numbers) // [3, 6, 18]
//
// MARK: 结构体
//
// 结构体和类非常类似,可以有属性和方法
struct NamesTable {
let names = [String]()
// 自定义下标运算符
subscript(index: Int) -> String {
return names[index]
}
}
// 结构体有一个自动生成的隐含的命名构造函数
let namesTable = NamesTable(names: ["Me", "Them"])
let name = namesTable[1]
println("Name is \(name)") // Name is Them
//
// MARK: 类
//
// 类和结构体的有三个访问控制级别,他们分别是 internal (默认), public, private
// internal: 模块内部可以访问
// public: 其他模块可以访问
// private: 只有定义这个类或结构体的源文件才能访问
public class Shape {
public func getArea() -> Int {
return 0;
}
}
// 类的所有方法和属性都是 public 的
// 如果你只是需要把数据保存在一个结构化的实例里面,应该用结构体
internal class Rect: Shape {
// 值属性 (Stored properties)
var sideLength: Int = 1
// 计算属性 (Computed properties)
private var perimeter: Int {
get {
return 4 * sideLength
}
set {
// `newValue` 是个隐含的变量,它表示将要设置进来的新值
sideLength = newValue / 4
}
}
// 延时加载的属性,只有这个属性第一次被引用时才进行初始化,而不是定义时就初始化
// subShape 值为 nil ,直到 subShape 第一次被引用时才初始化为一个 Rect 实例
lazy var subShape = Rect(sideLength: 4)
// 监控属性值的变化。
// 当我们需要在属性值改变时做一些事情,可以使用 `willSet` 和 `didSet` 来设置监控函数
// `willSet`: 值改变之前被调用
// `didSet`: 值改变之后被调用
var identifier: String = "defaultID" {
// `willSet` 的参数是即将设置的新值,参数名可以指定,如果没有指定,就是 `newValue`
willSet(someIdentifier) {
println(someIdentifier)
}
// `didSet` 的参数是已经被覆盖掉的旧的值,参数名也可以指定,如果没有指定,就是 `oldValue`
didSet {
println(oldValue)
}
}
// 命名构造函数 (designated inits),它必须初始化所有的成员变量,
// 然后调用父类的命名构造函数继续初始化父类的所有变量。
init(sideLength: Int) {
self.sideLength = sideLength
// 必须显式地在构造函数最后调用父类的构造函数 super.init
super.init()
}
func shrink() {
if sideLength > 0 {
--sideLength
}
}
// 函数重载使用 override 关键字
override func getArea() -> Int {
return sideLength * sideLength
}
}
// 类 `Square` 从 `Rect` 继承
class Square: Rect {
// 便捷构造函数 (convenience inits) 是调用自己的命名构造函数 (designated inits) 的构造函数
// Square 自动继承了父类的命名构造函数
convenience init() {
self.init(sideLength: 5)
}
// 关于构造函数的继承,有以下几个规则:
// 1\. 如果你没有实现任何命名构造函数,那么你就继承了父类的所有命名构造函数
// 2\. 如果你重载了父类的所有命名构造函数,那么你就自动继承了所有的父类快捷构造函数
// 3\. 如果你没有实现任何构造函数,那么你继承了父类的所有构造函数,包括命名构造函数和便捷构造函数
}
var mySquare = Square()
println(mySquare.getArea()) // 25
mySquare.shrink()
println(mySquare.sideLength) // 4
// 类型转换
let aShape = mySquare as Shape
// 使用三个等号来比较是不是同一个实例
if mySquare === aShape {
println("Yep, it's mySquare")
}
class Circle: Shape {
var radius: Int
override func getArea() -> Int {
return 3 * radius * radius
}
// optional 构造函数,可能会返回 nil
init?(radius: Int) {
self.radius = radius
super.init()
if radius <= 0 {
return nil
}
}
}
// 根据 Swift 类型推断,myCircle 是 Optional<Circle> 类型的变量
var myCircle = Circle(radius: 1)
println(myCircle?.getArea()) // Optional(3)
println(myCircle!.getArea()) // 3
var myEmptyCircle = Circle(radius: -1)
println(myEmptyCircle?.getArea()) // "nil"
if let circle = myEmptyCircle {
// 此语句不会输出,因为 myEmptyCircle 变量值为 nil
println("circle is not nil")
}
//
// MARK: 枚举
//
// 枚举可以像类一样,拥有方法
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func getIcon() -> String {
switch self {
case .Spades: return "♤"
case .Hearts: return "♡"
case .Diamonds: return "♢"
case .Clubs: return "♧"
}
}
}
// 当变量类型明确指定为某个枚举类型时,赋值时可以省略枚举类型
var suitValue: Suit = .Hearts
// 非整型的枚举类型需要在定义时赋值
enum BookName: String {
case John = "John"
case Luke = "Luke"
}
println("Name: \(BookName.John.rawValue)")
// 与特定数据类型关联的枚举
enum Furniture {
// 和 Int 型数据关联的枚举记录
case Desk(height: Int)
// 和 String, Int 关联的枚举记录
case Chair(brand: String, height: Int)
func description() -> String {
switch self {
case .Desk(let height):
return "Desk with \(height) cm"
case .Chair(let brand, let height):
return "Chair of \(brand) with \(height) cm"
}
}
}
var desk: Furniture = .Desk(height: 80)
println(desk.description()) // "Desk with 80 cm"
var chair = Furniture.Chair(brand: "Foo", height: 40)
println(chair.description()) // "Chair of Foo with 40 cm"
//
// MARK: 协议
// 与 Java 的 interface 类似
//
// 协议可以让遵循同一协议的类型实例拥有相同的属性,方法,类方法,操作符或下标运算符等
// 下面代码定义一个协议,这个协议包含一个名为 enabled 的计算属性且包含 buildShape 方法
protocol ShapeGenerator {
var enabled: Bool { get set }
func buildShape() -> Shape
}
// 协议声明时可以添加 @objc 前缀,添加 @objc 前缀后,
// 可以使用 is, as, as? 等来检查协议兼容性
// 需要注意,添加 @objc 前缀后,协议就只能被类来实现,
// 结构体和枚举不能实现加了 @objc 的前缀
// 只有添加了 @objc 前缀的协议才能声明 optional 方法
// 一个类实现一个带 optional 方法的协议时,可以实现或不实现这个方法
// optional 方法可以使用 optional 规则来调用
@objc protocol TransformShape {
optional func reshaped()
optional func canReshape() -> Bool
}
class MyShape: Rect {
var delegate: TransformShape?
func grow() {
sideLength += 2
// 在 optional 属性,方法或下标运算符后面加一个问号,可以优雅地忽略 nil 值,返回 nil。
// 这样就不会引起运行时错误 (runtime error)
if let allow = self.delegate?.canReshape?() {
// 注意语句中的问号
self.delegate?.reshaped?()
}
}
}
//
// MARK: 其它
//
// 扩展: 给一个已经存在的数据类型添加功能
// 给 Square 类添加 `Printable` 协议的实现,现在其支持 `Printable` 协议
extension Square: Printable {
var description: String {
return "Area: \(self.getArea()) - ID: \(self.identifier)"
}
}
println("Square: \(mySquare)") // Area: 16 - ID: defaultID
// 也可以给系统内置类型添加功能支持
extension Int {
var customProperty: String {
return "This is \(self)"
}
func multiplyBy(num: Int) -> Int {
return num * self
}
}
println(7.customProperty) // "This is 7"
println(14.multiplyBy(3)) // 42
// 泛型: 和 Java 及 C# 的泛型类似,使用 `where` 关键字来限制类型。
// 如果只有一个类型限制,可以省略 `where` 关键字
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
let foundAtIndex = findIndex([1, 2, 3, 4], 3)
println(foundAtIndex == 2) // true
// 自定义运算符:
// 自定义运算符可以以下面的字符打头:
// / = - + * % < > ! & | ^ . ~
// 甚至是 Unicode 的数学运算符等
prefix operator !!! {}
// 定义一个前缀运算符,使矩形的边长放大三倍
prefix func !!! (inout shape: Square) -> Square {
shape.sideLength *= 3
return shape
}
// 当前值
println(mySquare.sideLength) // 4
// 使用自定义的 !!! 运算符来把矩形边长放大三倍
!!!mySquare
println(mySquare.sideLength) // 12
~~~
Scala
最后更新于:2022-04-01 05:33:14
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=Scala
源代码下载: [learnscala-zh.scala](http://learnxinyminutes.com/docs/files/learnscala-zh.scala)
Scala - 一门可拓展的语言
~~~
/*
自行设置:
1) 下载 Scala - http://www.scala-lang.org/downloads
2) unzip/untar 到您喜欢的地方,并把 bin 子目录添加到 path 环境变量
3) 在终端输入 scala,启动 Scala 的 REPL,您会看到提示符:
scala>
这就是所谓的 REPL (读取-求值-输出循环,英语: Read-Eval-Print Loop),
您可以在其中输入合法的表达式,结果会被打印。
在教程中我们会进一步解释 Scala 文件是怎样的,但现在先了解一点基础。
*/
/////////////////////////////////////////////////
// 1\. 基础
/////////////////////////////////////////////////
// 单行注释开始于两个斜杠
/*
多行注释,如您之前所见,看起来像这样
*/
// 打印并强制换行
println("Hello world!")
println(10)
// 没有强制换行的打印
print("Hello world")
// 通过 var 或者 val 来声明变量
// val 声明是不可变的,var 声明是可修改的。不可变性是好事。
val x = 10 // x 现在是 10
x = 20 // 错误: 对 val 声明的变量重新赋值
var y = 10
y = 20 // y 现在是 20
/*
Scala 是静态语言,但注意上面的声明方式,我们没有指定类型。
这是因为类型推导的语言特性。大多数情况, Scala 编译器可以推测变量的类型,
所以您不需要每次都输入。可以像这样明确声明变量类型:
*/
val z: Int = 10
val a: Double = 1.0
// 注意从 Int 到 Double 的自动转型,结果是 10.0, 不是 10
val b: Double = 10
// 布尔值
true
false
// 布尔操作
!true // false
!false // true
true == false // false
10 > 5 // true
// 数学运算像平常一样
1 + 1 // 2
2 - 1 // 1
5 * 3 // 15
6 / 2 // 3
6 / 4 // 1
6.0 / 4 // 1.5
// 在 REPL 计算一个表达式会返回给您结果的类型和值
1 + 7
/* 上行的结果是:
scala> 1 + 7
res29: Int = 8
这意味着计算 1 + 7 的结果是一个 Int 类型的对象,其值为 8
注意 "res29" 是一个连续生成的变量名,用以存储您输入的表达式结果,
您看到的输出可能不一样。
*/
"Scala strings are surrounded by double quotes"
'a' // Scala 的字符
// '不存在单引号字符串' <= 这会导致错误
// String 有常见的 Java 字符串方法
"hello world".length
"hello world".substring(2, 6)
"hello world".replace("C", "3")
// 也有一些额外的 Scala 方法,另请参见:scala.collection.immutable.StringOps
"hello world".take(5)
"hello world".drop(5)
// 字符串改写:留意前缀 "s"
val n = 45
s"We have $n apples" // => "We have 45 apples"
// 在要改写的字符串中使用表达式也是可以的
val a = Array(11, 9, 6)
s"My second daughter is ${a(0) - a(2)} years old." // => "My second daughter is 5 years old."
s"We have double the amount of ${n / 2.0} in apples." // => "We have double the amount of 22.5 in apples."
s"Power of 2: ${math.pow(2, 2)}" // => "Power of 2: 4"
// 添加 "f" 前缀对要改写的字符串进行格式化
f"Power of 5: ${math.pow(5, 2)}%1.0f" // "Power of 5: 25"
f"Square root of 122: ${math.sqrt(122)}%1.4f" // "Square root of 122: 11.0454"
// 未处理的字符串,忽略特殊字符。
raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r."
// 一些字符需要转义,比如字符串中的双引号
"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown""
// 三个双引号可以使字符串跨越多行,并包含引号
val html = """<form id="daform">
<p>Press belo', Joe</p>
<input type="submit">
</form>"""
/////////////////////////////////////////////////
// 2\. 函数
/////////////////////////////////////////////////
// 函数可以这样定义:
//
// def functionName(args...): ReturnType = { body... }
//
// 如果您以前学习过传统的编程语言,注意 return 关键字的省略。
// 在 Scala 中, 函数代码块最后一条表达式就是返回值。
def sumOfSquares(x: Int, y: Int): Int = {
val x2 = x * x
val y2 = y * y
x2 + y2
}
// 如果函数体是单行表达式,{ } 可以省略:
def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y
// 函数调用的语法是熟知的:
sumOfSquares(3, 4) // => 25
// 在多数情况下 (递归函数是需要注意的例外), 函数返回值可以省略,
// 变量所用的类型推导一样会应用到函数返回值中:
def sq(x: Int) = x * x // 编译器会推断得知返回值是 Int
// 函数可以有默认参数
def addWithDefault(x: Int, y: Int = 5) = x + y
addWithDefault(1, 2) // => 3
addWithDefault(1) // => 6
// 匿名函数是这样的:
(x:Int) => x * x
// 和 def 不同,如果语义清晰,匿名函数的参数类型也可以省略。
// 类型 "Int => Int" 意味着这个函数接收一个 Int 并返回一个 Int。
val sq: Int => Int = x => x * x
// 匿名函数的调用也是类似的:
sq(10) // => 100
// 如果您的匿名函数中每个参数仅使用一次,
// Scala 提供一个更简洁的方式来定义他们。这样的匿名函数极为常见,
// 在数据结构部分会明显可见。
val addOne: Int => Int = _ + 1
val weirdSum: (Int, Int) => Int = (_ * 2 + _ * 3)
addOne(5) // => 6
weirdSum(2, 4) // => 16
// return 关键字是存在的,但它只从最里面包裹了 return 的 def 函数中返回。
// 警告: 在 Scala 中使用 return 容易出错,应该避免使用。
// 在匿名函数中没有效果,例如:
def foo(x: Int): Int = {
val anonFunc: Int => Int = { z =>
if (z > 5)
return z // 这一行令 z 成为 foo 函数的返回值!
else
z + 2 // 这一行是 anonFunc 函数的返回值
}
anonFunc(x) // 这一行是 foo 函数的返回值
}
/*
* 译者注:此处是指匿名函数中的 return z 成为最后执行的语句,
* 在 anonFunc(x) 下面的表达式(假设存在)不再执行。如果 anonFunc
* 是用 def 定义的函数, return z 仅返回到 anonFunc(x) ,
* 在 anonFunc(x) 下面的表达式(假设存在)会继续执行。
*/
/////////////////////////////////////////////////
// 3\. 控制语句
/////////////////////////////////////////////////
1 to 5
val r = 1 to 5
r.foreach( println )
r foreach println
// 附注: Scala 对点和括号的要求想当宽松,注意其规则是不同的。
// 这有助于写出读起来像英语的 DSL(领域特定语言) 和 API(应用编程接口)。
(5 to 1 by -1) foreach ( println )
// while 循环
var i = 0
while (i < 10) { println("i " + i); i+=1 }
while (i < 10) { println("i " + i); i+=1 } // 没错,再执行一次,发生了什么?为什么?
i // 显示 i 的值。注意 while 是经典的循环方式,它连续执行并改变循环中的变量。
// while 执行很快,比 Java 的循环快,但像上面所看到的那样用组合子和推导式
// 更易于理解和并行化。
// do while 循环
do {
println("x is still less than 10");
x += 1
} while (x < 10)
// Scala 中尾递归是一种符合语言习惯的递归方式。
// 递归函数需要清晰的返回类型,编译器不能推断得知。
// 这是一个 Unit。
def showNumbersInRange(a:Int, b:Int):Unit = {
print(a)
if (a < b)
showNumbersInRange(a + 1, b)
}
showNumbersInRange(1,14)
// 条件语句
val x = 10
if (x == 1) println("yeah")
if (x == 10) println("yeah")
if (x == 11) println("yeah")
if (x == 11) println ("yeah") else println("nay")
println(if (x == 10) "yeah" else "nope")
val text = if (x == 10) "yeah" else "nope"
/////////////////////////////////////////////////
// 4\. 数据结构
/////////////////////////////////////////////////
val a = Array(1, 2, 3, 5, 8, 13)
a(0)
a(3)
a(21) // 抛出异常
val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo")
m("fork")
m("spoon")
m("bottle") // 抛出异常
val safeM = m.withDefaultValue("no lo se")
safeM("bottle")
val s = Set(1, 3, 7)
s(0)
s(1)
/* 这里查看 map 的文档 -
* http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map
* 并确保你会阅读
*/
// 元组
(1, 2)
(4, 3, 2)
(1, 2, "three")
(a, 2, "three")
// 为什么有这个?
val divideInts = (x:Int, y:Int) => (x / y, x % y)
divideInts(10,3) // 函数 divideInts 同时返回结果和余数
// 要读取元组的元素,使用 _._n,n是从1开始的元素索引
val d = divideInts(10,3)
d._1
d._2
/////////////////////////////////////////////////
// 5\. 面向对象编程
/////////////////////////////////////////////////
/*
旁白: 教程中到现在为止我们所做的一切只是简单的表达式(值,函数等)。
这些表达式可以输入到命令行解释器中作为快速测试,但它们不能独立存在于 Scala
文件。举个例子,您不能在 Scala 文件上简单的写上 "val x = 5"。相反 Scala 文件
允许的顶级结构是:
- objects
- classes
- case classes
- traits
现在来解释这些是什么。
*/
// 类和其他语言的类相似,构造器参数在类名后声明,初始化在类结构体中完成。
class Dog(br: String) {
// 构造器代码在此
var breed: String = br
// 定义名为 bark 的方法,返回字符串
def bark = "Woof, woof!"
// 值和方法作用域假定为 public。"protected" 和 "private" 关键字也是可用的。
private def sleep(hours: Int) =
println(s"I'm sleeping for $hours hours")
// 抽象方法是没有方法体的方法。如果取消下面那行注释,Dog 类必须被声明为 abstract
// abstract class Dog(...) { ... }
// def chaseAfter(what: String): String
}
val mydog = new Dog("greyhound")
println(mydog.breed) // => "greyhound"
println(mydog.bark) // => "Woof, woof!"
// "object" 关键字创造一种类型和该类型的单例。
// Scala 的 class 常常也含有一个 “伴生对象”,class 中包含每个实例的行为,所有实例
// 共用的行为则放入 object 中。两者的区别和其他语言中类方法和静态方法类似。
// 请注意 object 和 class 可以同名。
object Dog {
def allKnownBreeds = List("pitbull", "shepherd", "retriever")
def createDog(breed: String) = new Dog(breed)
}
// Case 类是有额外内建功能的类。Scala 初学者常遇到的问题之一便是何时用类
// 和何时用 case 类。界线比较模糊,但通常类倾向于封装,多态和行为。类中的值
// 的作用域一般为 private , 只有方向是暴露的。case 类的主要目的是放置不可变
// 数据。它们通常只有几个方法,且方法几乎没有副作用。
case class Person(name: String, phoneNumber: String)
// 创造新实例,注意 case 类不需要使用 "new" 关键字
val george = Person("George", "1234")
val kate = Person("Kate", "4567")
// 使用 case 类,您可以轻松得到一些功能,像 getters:
george.phoneNumber // => "1234"
// 每个字段的相等性比较(无需覆盖 .equals)
Person("George", "1234") == Person("Kate", "1236") // => false
// 简单的拷贝方式
// otherGeorge == Person("george", "9876")
val otherGeorge = george.copy(phoneNumber = "9876")
// 还有很多。case 类同时可以用于模式匹配,接下来会看到。
// 敬请期待 Traits !
/////////////////////////////////////////////////
// 6\. 模式匹配
/////////////////////////////////////////////////
// 模式匹配是一个强大和常用的 Scala 特性。这是用模式匹配一个 case 类的例子。
// 附注:不像其他语言, Scala 的 case 不需要 break, 其他语言中 switch 语句的
// fall-through 现象不会发生。
def matchPerson(person: Person): String = person match {
// Then you specify the patterns:
case Person("George", number) => "We found George! His number is " + number
case Person("Kate", number) => "We found Kate! Her number is " + number
case Person(name, number) => "We matched someone : " + name + ", phone : " + number
}
val email = "(.*)@(.*)".r // 定义下一个例子会用到的正则
// 模式匹配看起来和 C语言家族的 switch 语句相似,但更为强大。
// Scala 中您可以匹配很多东西:
def matchEverything(obj: Any): String = obj match {
// 匹配值:
case "Hello world" => "Got the string Hello world"
// 匹配类型:
case x: Double => "Got a Double: " + x
// 匹配时指定条件
case x: Int if x > 10000 => "Got a pretty big number!"
// 像之前一样匹配 case 类:
case Person(name, number) => s"Got contact info for $name!"
// 匹配正则表达式:
case email(name, domain) => s"Got email address $name@$domain"
// 匹配元组:
case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c"
// 匹配数据结构:
case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c"
// 模式可以嵌套
case List(List((1, 2,"YAY"))) => "Got a list of list of tuple"
}
// 事实上,你可以对任何有 "unapply" 方法的对象进行模式匹配。
// 这个特性如此强大以致于 Scala 允许定义一个函数作为模式匹配:
val patternFunc: Person => String = {
case Person("George", number) => s"George's number: $number"
case Person(name, number) => s"Random person's number: $number"
}
/////////////////////////////////////////////////
// 7\. 函数式编程
/////////////////////////////////////////////////
// Scala 允许方法和函数作为其他方法和函数的参数和返回值。
val add10: Int => Int = _ + 10 // 一个接受一个 Int 类型参数并返回一个 Int 类型值的函数
List(1, 2, 3) map add10 // List(11, 12, 13) - add10 被应用到每一个元素
// 匿名函数可以被使用来代替有命名的函数:
List(1, 2, 3) map (x => x + 10)
// 如果匿名函数只有一个参数可以用下划线作为变量
List(1, 2, 3) map (_ + 10)
// 如果您所应用的匿名块和匿名函数都接受一个参数,那么你甚至可以省略下划线
List("Dom", "Bob", "Natalia") foreach println
// 组合子
// 译注: val sq: Int => Int = x => x * x
s.map(sq)
val sSquared = s. map(sq)
sSquared.filter(_ < 10)
sSquared.reduce (_+_)
// filter 函数接受一个 predicate (函数根据条件 A 返回 Boolean)并选择
// 所有满足 predicate 的元素
List(1, 2, 3) filter (_ > 2) // List(3)
case class Person(name:String, age:Int)
List(
Person(name = "Dom", age = 23),
Person(name = "Bob", age = 30)
).filter(_.age > 25) // List(Person("Bob", 30))
// Scala 的 foreach 方法定义在某些集合中,接受一个函数并返回 Unit (void 方法)
// 另请参见:
// http://www.scala-lang.org/api/current/index.html#scala.collection.IterableLike@foreach(f:A=>Unit):Unit
val aListOfNumbers = List(1, 2, 3, 4, 10, 20, 100)
aListOfNumbers foreach (x => println(x))
aListOfNumbers foreach println
// For 推导式
for { n <- s } yield sq(n)
val nSquared2 = for { n <- s } yield sq(n)
for { n <- nSquared2 if n < 10 } yield n
for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
/* 注意,这些不是 for 循环,for 循环的语义是‘重复’,然而 for 推导式定义
两个数据集合的关系。 */
/////////////////////////////////////////////////
// 8\. 隐式转换
/////////////////////////////////////////////////
/* 警告 警告: 隐式转换是 Scala 中一套强大的特性,因此容易被滥用。
* Scala 初学者在理解它们的工作原理和最佳实践之前,应抵制使用它的诱惑。
* 我们加入这一章节仅因为它们在 Scala 的库中太过常见,导致没有用隐式转换的库
* 就不可能做有意义的事情。这章节主要让你理解和使用隐式转换,而不是自己声明。
*/
// 可以通过 "implicit" 声明任何值(val, 函数,对象等)为隐式值,
// 请注意这些例子中,我们用到第5部分的 Dog 类。
implicit val myImplicitInt = 100
implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed)
// implicit 关键字本身不改变值的行为,所以上面的值可以照常使用。
myImplicitInt + 2 // => 102
myImplicitFunction("Pitbull").breed // => "Golden Pitbull"
// 区别在于,当另一段代码“需要”隐式值时,这些值现在有资格作为隐式值。
// 一种情况是隐式函数参数。
def sendGreetings(toWhom: String)(implicit howMany: Int) =
s"Hello $toWhom, $howMany blessings to you and yours!"
// 如果提供值给 “howMany”,函数正常运行
sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!"
// 如果省略隐式参数,会传一个和参数类型相同的隐式值,
// 在这个例子中, 是 “myImplicitInt":
sendGreetings("Jane") // => "Hello Jane, 100 blessings to you and yours!"
// 隐式的函数参数使我们可以模拟其他函数式语言的 type 类(type classes)。
// 它经常被用到所以有特定的简写。这两行代码是一样的:
def foo[T](implicit c: C[T]) = ...
def foo[T : C] = ...
// 编译器寻找隐式值另一种情况是你调用方法时
// obj.method(...)
// 但 "obj" 没有一个名为 "method" 的方法。这样的话,如果有一个参数类型为 A
// 返回值类型为 B 的隐式转换,obj 的类型是 A,B 有一个方法叫 "method" ,这样
// 转换就会被应用。所以作用域里有上面的 myImplicitFunction, 我们可以这样做:
"Retriever".breed // => "Golden Retriever"
"Sheperd".bark // => "Woof, woof!"
// 这里字符串先被上面的函数转换为 Dog 对象,然后调用相应的方法。
// 这是相当强大的特性,但再次提醒,请勿轻率使用。
// 事实上,当你定义上面的隐式函数时,编译器会作出警告,除非你真的了解
// 你正在做什么否则不要使用。
/////////////////////////////////////////////////
// 9\. 杂项
/////////////////////////////////////////////////
// 导入类
import scala.collection.immutable.List
// 导入所有子包
import scala.collection.immutable._
// 一条语句导入多个类
import scala.collection.immutable.{List, Map}
// 使用 ‘=>’ 对导入进行重命名
import scala.collection.immutable.{ List => ImmutableList }
// 导入所有类,排除其中一些。下面的语句排除了 Map 和 Set:
import scala.collection.immutable.{Map => _, Set => _, _}
// 在 Scala 文件用 object 和单一的 main 方法定义程序入口:
object Application {
def main(args: Array[String]): Unit = {
// stuff goes here.
}
}
// 文件可以包含多个 class 和 object,用 scalac 编译源文件
// 输入和输出
// 按行读文件
import scala.io.Source
for(line <- Source.fromFile("myfile.txt").getLines())
println(line)
// 用 Java 的 PrintWriter 写文件
val writer = new PrintWriter("myfile.txt")
writer.write("Writing line for line" + util.Properties.lineSeparator)
writer.write("Another line here" + util.Properties.lineSeparator)
writer.close()
~~~
## 更多的资源
[为没耐心的人准备的 Scala](http://horstmann.com/scala/)
[Twitter Scala school](http://twitter.github.io/scala_school/)
[The Scala documentation](http://www.scala-lang.org/documentation/)
[在浏览器尝试 Scala](http://scalatutorials.com/tour/)
加入 [Scala 用户组](https://groups.google.com/forum/#!forum/scala-user)
Rust
最后更新于:2022-04-01 05:33:11
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=rust
源代码下载: [learnrust-cn.rs](http://learnxinyminutes.com/docs/files/learnrust-cn.rs)
Rust 是由 Mozilla 研究院开发的编程语言。Rust 将底层的性能控制与高级语言的便利性和安全保障结合在了一起。
而 Rust 并不需要一个垃圾回收器或者运行时即可实现这个目的,这使得 Rust 库可以成为一种 C 语言的替代品。
Rust 第一版(0.1 版)发布于 2012 年 1 月,3 年以来一直在紧锣密鼓地迭代。 因为更新太频繁,一般建议使用每夜构建版而不是稳定版,直到最近 1.0 版本的发布。
2015 年 3 月 15 日,Rust 1.0 发布,完美向后兼容,最新的每夜构建版提供了缩短编译时间等新特性。 Rust 采用了持续迭代模型,每 6 周一个发布版。Rust 1.1 beta 版在 1.0 发布时同时发布。
尽管 Rust 相对来说是一门底层语言,它提供了一些常见于高级语言的函数式编程的特性。这让 Rust 不仅高效,并且易用。
~~~
// 这是注释,单行注释...
/* ...这是多行注释 */
///////////////
// 1\. 基础 //
///////////////
// 函数 (Functions)
// `i32` 是有符号 32 位整数类型(32-bit signed integers)
fn add2(x: i32, y: i32) -> i32 {
// 隐式返回 (不要分号)
x + y
}
// 主函数(Main function)
fn main() {
// 数字 (Numbers) //
// 不可变绑定
let x: i32 = 1;
// 整形/浮点型数 后缀
let y: i32 = 13i32;
let f: f64 = 1.3f64;
// 类型推导
// 大部分时间,Rust 编译器会推导变量类型,所以不必把类型显式写出来。
// 这个教程里面很多地方都显式写了类型,但是只是为了示范。
// 绝大部分时间可以交给类型推导。
let implicit_x = 1;
let implicit_f = 1.3;
// 算术运算
let sum = x + y + 13;
// 可变变量
let mut mutable = 1;
mutable = 4;
mutable += 2;
// 字符串 (Strings) //
// 字符串字面量
let x: &str = "hello world!";
// 输出
println!("{} {}", f, x); // 1.3 hello world
// 一个 `String` – 在堆上分配空间的字符串
let s: String = "hello world".to_string();
// 字符串分片(slice) - 另一个字符串的不可变视图
// 基本上就是指向一个字符串的不可变指针,它不包含字符串里任何内容,只是一个指向某个东西的指针
// 比如这里就是 `s`
let s_slice: &str = &s;
println!("{} {}", s, s_slice); // hello world hello world
// 数组 (Vectors/arrays) //
// 长度固定的数组 (array)
let four_ints: [i32; 4] = [1, 2, 3, 4];
// 变长数组 (vector)
let mut vector: Vec<i32> = vec![1, 2, 3, 4];
vector.push(5);
// 分片 - 某个数组(vector/array)的不可变视图
// 和字符串分片基本一样,只不过是针对数组的
let slice: &[i32] = &vector;
// 使用 `{:?}` 按调试样式输出
println!("{:?} {:?}", vector, slice); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
// 元组 (Tuples) //
// 元组是固定大小的一组值,可以是不同类型
let x: (i32, &str, f64) = (1, "hello", 3.4);
// 解构 `let`
let (a, b, c) = x;
println!("{} {} {}", a, b, c); // 1 hello 3.4
// 索引
println!("{}", x.1); // hello
//////////////
// 2\. 类型 (Type) //
//////////////
// 结构体(Sturct)
struct Point {
x: i32,
y: i32,
}
let origin: Point = Point { x: 0, y: 0 };
// 匿名成员结构体,又叫“元组结构体”(‘tuple struct’)
struct Point2(i32, i32);
let origin2 = Point2(0, 0);
// 基础的 C 风格枚举类型(enum)
enum Direction {
Left,
Right,
Up,
Down,
}
let up = Direction::Up;
// 有成员的枚举类型
enum OptionalI32 {
AnI32(i32),
Nothing,
}
let two: OptionalI32 = OptionalI32::AnI32(2);
let nothing = OptionalI32::Nothing;
// 泛型 (Generics) //
struct Foo<T> { bar: T }
// 这个在标准库里面有实现,叫 `Option`
enum Optional<T> {
SomeVal(T),
NoVal,
}
// 方法 (Methods) //
impl<T> Foo<T> {
// 方法需要一个显式的 `self` 参数
fn get_bar(self) -> T {
self.bar
}
}
let a_foo = Foo { bar: 1 };
println!("{}", a_foo.get_bar()); // 1
// 接口(Traits) (其他语言里叫 interfaces 或 typeclasses) //
trait Frobnicate<T> {
fn frobnicate(self) -> Option<T>;
}
impl<T> Frobnicate<T> for Foo<T> {
fn frobnicate(self) -> Option<T> {
Some(self.bar)
}
}
let another_foo = Foo { bar: 1 };
println!("{:?}", another_foo.frobnicate()); // Some(1)
///////////////////////////////////
// 3\. 模式匹配 (Pattern matching) //
///////////////////////////////////
let foo = OptionalI32::AnI32(1);
match foo {
OptionalI32::AnI32(n) => println!("it’s an i32: {}", n),
OptionalI32::Nothing => println!("it’s nothing!"),
}
// 高级模式匹配
struct FooBar { x: i32, y: OptionalI32 }
let bar = FooBar { x: 15, y: OptionalI32::AnI32(32) };
match bar {
FooBar { x: 0, y: OptionalI32::AnI32(0) } =>
println!("The numbers are zero!"),
FooBar { x: n, y: OptionalI32::AnI32(m) } if n == m =>
println!("The numbers are the same"),
FooBar { x: n, y: OptionalI32::AnI32(m) } =>
println!("Different numbers: {} {}", n, m),
FooBar { x: _, y: OptionalI32::Nothing } =>
println!("The second number is Nothing!"),
}
///////////////////////////////
// 4\. 流程控制 (Control flow) //
///////////////////////////////
// `for` 循环
let array = [1, 2, 3];
for i in array.iter() {
println!("{}", i);
}
// 区间 (Ranges)
for i in 0u32..10 {
print!("{} ", i);
}
println!("");
// 输出 `0 1 2 3 4 5 6 7 8 9 `
// `if`
if 1 == 1 {
println!("Maths is working!");
} else {
println!("Oh no...");
}
// `if` 可以当表达式
let value = if true {
"good"
} else {
"bad"
};
// `while` 循环
while 1 == 1 {
println!("The universe is operating normally.");
}
// 无限循环
loop {
println!("Hello!");
}
////////////////////////////////////////////////
// 5\. 内存安全和指针 (Memory safety & pointers) //
////////////////////////////////////////////////
// 独占指针 (Owned pointer) - 同一时刻只能有一个对象能“拥有”这个指针
// 意味着 `Box` 离开他的作用域后,会被安全地释放
let mut mine: Box<i32> = Box::new(3);
*mine = 5; // 解引用
// `now_its_mine` 获取了 `mine` 的所有权。换句话说,`mine` 移动 (move) 了
let mut now_its_mine = mine;
*now_its_mine += 2;
println!("{}", now_its_mine); // 7
// println!("{}", mine); // 编译报错,因为现在 `now_its_mine` 独占那个指针
// 引用 (Reference) – 引用其他数据的不可变指针
// 当引用指向某个值,我们称为“借用”这个值,因为是被不可变的借用,所以不能被修改,也不能移动
// 借用一直持续到生命周期结束,即离开作用域
let mut var = 4;
var = 3;
let ref_var: &i32 = &var;
println!("{}", var); //不像 `box`, `var` 还可以继续使用
println!("{}", *ref_var);
// var = 5; // 编译报错,因为 `var` 被借用了
// *ref_var = 6; // 编译报错,因为 `ref_var` 是不可变引用
// 可变引用 (Mutable reference)
// 当一个变量被可变地借用时,也不可使用
let mut var2 = 4;
let ref_var2: &mut i32 = &mut var2;
*ref_var2 += 2;
println!("{}", *ref_var2); // 6
// var2 = 2; // 编译报错,因为 `var2` 被借用了
}
~~~
## 更深入的资料
Rust 还有很多很多其他内容 - 这只是 Rust 最基本的功能,帮助你了解 Rust 里面最重要的东西。 如果想深入学习 Rust,可以去读 [The Rust Programming Language](http://doc.rust-lang.org/book/index.html) 或者上 reddit [/r/rust](http://reddit.com/r/rust) 订阅。 同时 irc.mozilla.org 的 #rust 频道上的小伙伴们也非常欢迎新来的朋友。
你可以在这个在线编译器 [Rust playpen](http://play.rust-lang.org/) 上尝试 Rust 的一些特性 或者上[官方网站](http://rust-lang.org/).
Ruby
最后更新于:2022-04-01 05:33:09
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=ruby
源代码下载: [learnruby-zh.rb](http://learnxinyminutes.com/docs/files/learnruby-zh.rb)
~~~
# 这是单行注释
=begin
这是多行注释
没人用这个
你也不该用
=end
# 首先,也是最重要的,所有东西都是对象
# 数字是对象
3.class #=> Fixnum
3.to_s #=> "3"
# 一些基本的算术符号
1 + 1 #=> 2
8 - 1 #=> 7
10 * 2 #=> 20
35 / 5 #=> 7
# 算术符号只是语法糖而已
# 实际上是调用对象的方法
1.+(3) #=> 4
10.* 5 #=> 50
# 特殊的值也是对象
nil # 空
true # 真
false # 假
nil.class #=> NilClass
true.class #=> TrueClass
false.class #=> FalseClass
# 相等运算符
1 == 1 #=> true
2 == 1 #=> false
# 不等运算符
1 != 1 #=> false
2 != 1 #=> true
!true #=> false
!false #=> true
# 除了false自己,nil是唯一的值为false的对象
!nil #=> true
!false #=> true
!0 #=> false
# 更多比较
1 < 10 #=> true
1 > 10 #=> false
2 <= 2 #=> true
2 >= 2 #=> true
# 字符串是对象
'I am a string'.class #=> String
"I am a string too".class #=> String
placeholder = "use string interpolation"
"I can #{placeholder} when using double quoted strings"
#=> "I can use string interpolation when using double quoted strings"
# 输出值
puts "I'm printing!"
# 变量
x = 25 #=> 25
x #=> 25
# 注意赋值语句返回了赋的值
# 这意味着你可以用多重赋值语句
x = y = 10 #=> 10
x #=> 10
y #=> 10
# 按照惯例,用 snake_case 作为变量名
snake_case = true
# 使用具有描述性的运算符
path_to_project_root = '/good/name/'
path = '/bad/name/'
# 符号(Symbols,也是对象)
# 符号是不可变的,内部用整数类型表示的可重用的值。
# 通常用它代替字符串来有效地表示有意义的值。
:pending.class #=> Symbol
status = :pending
status == :pending #=> true
status == 'pending' #=> false
status == :approved #=> false
# 数组
# 这是一个数组
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
# 数组可以包含不同类型的元素
[1, "hello", false] #=> [1, "hello", false]
# 数组可以被索引
# 从前面开始
array[0] #=> 1
array[12] #=> nil
# 像运算符一样,[var]形式的访问
# 也就是一个语法糖
# 实际上是调用对象的[] 方法
array.[] 0 #=> 1
array.[] 12 #=> nil
# 从尾部开始
array[-1] #=> 5
# 同时指定开始的位置和长度
array[2, 3] #=> [3, 4, 5]
# 或者指定一个范围
array[1..3] #=> [2, 3, 4]
# 像这样往数组增加一个元素
array << 6 #=> [1, 2, 3, 4, 5, 6]
# 哈希表是Ruby的键值对的基本数据结构
# 哈希表由大括号定义
hash = {'color' => 'green', 'number' => 5}
hash.keys #=> ['color', 'number']
# 哈希表可以通过键快速地查询
hash['color'] #=> 'green'
hash['number'] #=> 5
# 查询一个不存在地键将会返回nil
hash['nothing here'] #=> nil
# 用 #each 方法来枚举哈希表:
hash.each do |k, v|
puts "#{k} is #{v}"
end
# 从Ruby 1.9开始, 用符号作为键的时候有特别的记号表示:
new_hash = { defcon: 3, action: true}
new_hash.keys #=> [:defcon, :action]
# 小贴士:数组和哈希表都是可枚举的
# 它们可以共享一些有用的方法,比如each, map, count 等等
# 控制流
if true
"if statement"
elsif false
"else if, optional"
else
"else, also optional"
end
for counter in 1..5
puts "iteration #{counter}"
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
# 然而
# 没人用for循环
# 用`each`来代替,就像这样
(1..5).each do |counter|
puts "iteration #{counter}"
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
counter = 1
while counter <= 5 do
puts "iteration #{counter}"
counter += 1
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
grade = 'B'
case grade
when 'A'
puts "Way to go kiddo"
when 'B'
puts "Better luck next time"
when 'C'
puts "You can do better"
when 'D'
puts "Scraping through"
when 'F'
puts "You failed!"
else
puts "Alternative grading system, eh?"
end
# 函数
def double(x)
x * 2
end
# 函数 (以及所有的方法块) 隐式地返回了最后语句的值
double(2) #=> 4
# 当不存在歧义的时候括号是可有可无的
double 3 #=> 6
double double 3 #=> 12
def sum(x,y)
x + y
end
# 方法的参数通过逗号分隔
sum 3, 4 #=> 7
sum sum(3,4), 5 #=> 12
# yield
# 所有的方法都有一个隐式的块参数
# 可以用yield参数调用
def surround
puts "{"
yield
puts "}"
end
surround { puts 'hello world' }
# {
# hello world
# }
# 用class关键字定义一个类
class Human
# 一个类变量,它被这个类地所有实例变量共享
@@species = "H. sapiens"
# 构造函数
def initialize(name, age=0)
# 将参数name的值赋给实例变量@name
@name = name
# 如果没有给出age, 那么会采用参数列表中地默认地值
@age = age
end
# 基本的 setter 方法
def name=(name)
@name = name
end
# 基本地 getter 方法
def name
@name
end
# 一个类方法以self.开头
# 它可以被类调用,但不能被类的实例调用
def self.say(msg)
puts "#{msg}"
end
def species
@@species
end
end
# 类的例子
jim = Human.new("Jim Halpert")
dwight = Human.new("Dwight K. Schrute")
# 让我们来调用一些方法
jim.species #=> "H. sapiens"
jim.name #=> "Jim Halpert"
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
jim.name #=> "Jim Halpert II"
dwight.species #=> "H. sapiens"
dwight.name #=> "Dwight K. Schrute"
# 调用对象的方法
Human.say("Hi") #=> "Hi"
~~~
Racket
最后更新于:2022-04-01 05:33:07
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=racket
源代码下载: [learnracket-zh.rkt](http://learnxinyminutes.com/docs/files/learnracket-zh.rkt)
Racket是Lisp/Scheme家族中的一个通用的,多范式的编程语言。 非常期待您的反馈!你可以通过[@th3rac25](http://twitter.com/th3rac25)或以用户名为 th3rac25 的Google邮箱服务和我取得联系
~~~
#lang racket ; 声明我们使用的语言
;;; 注释
;; 单行注释以分号开始
#| 块注释
可以横跨很多行而且...
#|
可以嵌套
|#
|#
;; S表达式注释忽略剩下的表达式
;; 在调试的时候会非常有用
#; (被忽略的表达式)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1\. 原始数据类型和操作符
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 数字
9999999999999999999999 ; 整数
#b111 ; 二进制数字 => 7
#o111 ; 八进制数字 => 73
#x111 ; 十六进制数字 => 273
3.14 ; 实数
6.02e+23
1/2 ; 有理数
1+2i ; 复数
;; 函数调用写作(f x y z ...)
;; 在这里 f 是一个函数, x, y, z, ... 是参数
;; 如果你想创建一个列表数据的字面量, 使用 ' 来阻止它们
;; 被求值
'(+ 1 2) ; => (+ 1 2)
;; 接下来,是一些数学运算
(+ 1 1) ; => 2
(- 8 1) ; => 7
(* 10 2) ; => 20
(expt 2 3) ; => 8
(quotient 5 2) ; => 2
(remainder 5 2) ; => 1
(/ 35 5) ; => 7
(/ 1 3) ; => 1/3
(exact->inexact 1/3) ; => 0.3333333333333333
(+ 1+2i 2-3i) ; => 3-1i
;;; 布尔类型
#t ; 为真
#f ; 为假,#f 之外的任何值都是真
(not #t) ; => #f
(and 0 #f (error "doesn't get here")) ; => #f
(or #f 0 (error "doesn't get here")) ; => 0
;;; 字符
#\A ; => #\A
#\λ ; => #\λ
#\u03BB ; => #\λ
;;; 字符串是字符组成的定长数组
"Hello, world!"
"Benjamin \"Bugsy\" Siegel" ; \是转义字符
"Foo\tbar\41\x21\u0021\a\r\n" ; 包含C语言的转义字符,和Unicode
"λx:(μα.α→α).xx" ; 字符串可以包含Unicode字符
;; 字符串可以相加
(string-append "Hello " "world!") ; => "Hello world!"
;; 一个字符串可以看做是一个包含字符的列表
(string-ref "Apple" 0) ; => #\A
;; format 可以用来格式化字符串
(format "~a can be ~a" "strings" "formatted")
;; 打印字符串非常简单
(printf "I'm Racket. Nice to meet you!\n")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2\. 变量
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 你可以使用 define 定义一个变量
;; 变量的名字可以使用任何字符除了: ()[]{}",'`;#|\
(define some-var 5)
some-var ; => 5
;; 你也可以使用Unicode字符
(define ⊆ subset?)
(⊆ (set 3 2) (set 1 2 3)) ; => #t
;; 访问未赋值的变量会引发一个异常
; x ; => x: undefined ...
;; 本地绑定: `me' 被绑定到 "Bob",并且只在 let 中生效
(let ([me "Bob"])
"Alice"
me) ; => "Bob"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3\. 结构和集合
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 结构体
(struct dog (name breed age))
(define my-pet
(dog "lassie" "collie" 5))
my-pet ; => #<dog>
(dog? my-pet) ; => #t
(dog-name my-pet) ; => "lassie"
;;; 对 (不可变的)
;; `cons' 返回对, `car' 和 `cdr' 从对中提取第1个
;; 和第2个元素
(cons 1 2) ; => '(1 . 2)
(car (cons 1 2)) ; => 1
(cdr (cons 1 2)) ; => 2
;;; 列表
;; 列表由链表构成, 由 `cons' 的结果
;; 和一个 `null' (或者 '()) 构成,后者标记了这个列表的结束
(cons 1 (cons 2 (cons 3 null))) ; => '(1 2 3)
;; `list' 给列表提供了一个非常方便的可变参数的生成器
(list 1 2 3) ; => '(1 2 3)
;; 一个单引号也可以用来表示一个列表字面量
'(1 2 3) ; => '(1 2 3)
;; 仍然可以使用 `cons' 在列表的开始处添加一项
(cons 4 '(1 2 3)) ; => '(4 1 2 3)
;; `append' 函数可以将两个列表合并
(append '(1 2) '(3 4)) ; => '(1 2 3 4)
;; 列表是非常基础的类型,所以有*很多*操作列表的方法
;; 下面是一些例子:
(map add1 '(1 2 3)) ; => '(2 3 4)
(map + '(1 2 3) '(10 20 30)) ; => '(11 22 33)
(filter even? '(1 2 3 4)) ; => '(2 4)
(count even? '(1 2 3 4)) ; => 2
(take '(1 2 3 4) 2) ; => '(1 2)
(drop '(1 2 3 4) 2) ; => '(3 4)
;;; 向量
;; 向量是定长的数组
#(1 2 3) ; => '#(1 2 3)
;; 使用 `vector-append' 方法将2个向量合并
(vector-append #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
;;; Set(翻译成集合也不太合适,所以不翻译了..)
;; 从一个列表创建一个Set
(list->set '(1 2 3 1 2 3 3 2 1 3 2 1)) ; => (set 1 2 3)
;; 使用 `set-add' 增加一个成员
;; (函数式特性: 这里会返回一个扩展后的Set,而不是修改输入的值)
(set-add (set 1 2 3) 4) ; => (set 1 2 3 4)
;; 使用 `set-remove' 移除一个成员
(set-remove (set 1 2 3) 1) ; => (set 2 3)
;; 使用 `set-member?' 测试成员是否存在
(set-member? (set 1 2 3) 1) ; => #t
(set-member? (set 1 2 3) 4) ; => #f
;;; 散列表
;; 创建一个不变的散列表 (可变散列表的例子在下面)
(define m (hash 'a 1 'b 2 'c 3))
;; 根据键取得值
(hash-ref m 'a) ; => 1
;; 获取一个不存在的键是一个异常
; (hash-ref m 'd) => 没有找到元素
;; 你可以给不存在的键提供一个默认值
(hash-ref m 'd 0) ; => 0
;; 使用 `hash-set' 来扩展一个不可变的散列表
;; (返回的是扩展后的散列表而不是修改它)
(define m2 (hash-set m 'd 4))
m2 ; => '#hash((b . 2) (a . 1) (d . 4) (c . 3))
;; 记住,使用 `hash` 创建的散列表是不可变的
m ; => '#hash((b . 2) (a . 1) (c . 3)) <-- no `d'
;; 使用 `hash-remove' 移除一个键值对 (函数式特性,m并不变)
(hash-remove m 'a) ; => '#hash((b . 2) (c . 3))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3\. 函数
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 使用 `lambda' 创建函数
;; 函数总是返回它最后一个表达式的值
(lambda () "Hello World") ; => #<procedure>
;; 也可以使用 Unicode 字符 `λ'
(λ () "Hello World") ; => 同样的函数
;; 使用括号调用一个函数,也可以直接调用一个 lambda 表达式
((lambda () "Hello World")) ; => "Hello World"
((λ () "Hello World")) ; => "Hello World"
;; 将函数赋值为一个变量
(define hello-world (lambda () "Hello World"))
(hello-world) ; => "Hello World"
;; 你可以使用函数定义的语法糖来简化代码
(define (hello-world2) "Hello World")
;; `()`是函数的参数列表
(define hello
(lambda (name)
(string-append "Hello " name)))
(hello "Steve") ; => "Hello Steve"
;; 同样的,可以使用语法糖来定义:
(define (hello2 name)
(string-append "Hello " name))
;; 你也可以使用可变参数, `case-lambda'
(define hello3
(case-lambda
[() "Hello World"]
[(name) (string-append "Hello " name)]))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
;; ... 或者给参数指定一个可选的默认值
(define (hello4 [name "World"])
(string-append "Hello " name))
;; 函数可以将多余的参数放到一个列表里
(define (count-args . args)
(format "You passed ~a args: ~a" (length args) args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
;; ... 也可以使用不带语法糖的 `lambda' 形式:
(define count-args2
(lambda args
(format "You passed ~a args: ~a" (length args) args)))
;; 你可以混用两种用法
(define (hello-count name . args)
(format "Hello ~a, you passed ~a extra args" name (length args)))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
;; ... 不带语法糖的形式:
(define hello-count2
(lambda (name . args)
(format "Hello ~a, you passed ~a extra args" name (length args))))
;; 使用关键字
(define (hello-k #:name [name "World"] #:greeting [g "Hello"] . args)
(format "~a ~a, ~a extra args" g name (length args)))
(hello-k) ; => "Hello World, 0 extra args"
(hello-k 1 2 3) ; => "Hello World, 3 extra args"
(hello-k #:greeting "Hi") ; => "Hi World, 0 extra args"
(hello-k #:name "Finn" #:greeting "Hey") ; => "Hey Finn, 0 extra args"
(hello-k 1 2 3 #:greeting "Hi" #:name "Finn" 4 5 6)
; => "Hi Finn, 6 extra args"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4\. 判断是否相等
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 判断数字使用 `='
(= 3 3.0) ; => #t
(= 2 1) ; => #f
;; 判断对象使用 `eq?'
(eq? 3 3) ; => #t
(eq? 3 3.0) ; => #f
(eq? (list 3) (list 3)) ; => #f
;; 判断集合使用 `equal?'
(equal? (list 'a 'b) (list 'a 'b)) ; => #t
(equal? (list 'a 'b) (list 'b 'a)) ; => #f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5\. 控制结构
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 条件判断
(if #t ; 测试表达式
"this is true" ; 为真的表达式
"this is false") ; 为假的表达式
; => "this is true"
;; 注意, 除 `#f` 之外的所有值都认为是真
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo)
(if (member 'Groucho '(Harpo Groucho Zeppo))
'yep
'nope)
; => 'yep
;; `cond' 会进行一系列的判断来选择一个结果
(cond [(> 2 2) (error "wrong!")]
[(< 2 2) (error "wrong again!")]
[else 'ok]) ; => 'ok
;;; 模式匹配
(define (fizzbuzz? n)
(match (list (remainder n 3) (remainder n 5))
[(list 0 0) 'fizzbuzz]
[(list 0 _) 'fizz]
[(list _ 0) 'buzz]
[_ #f]))
(fizzbuzz? 15) ; => 'fizzbuzz
(fizzbuzz? 37) ; => #f
;;; 循环
;; 循环可以使用递归(尾递归)
(define (loop i)
(when (< i 10)
(printf "i=~a\n" i)
(loop (add1 i))))
(loop 5) ; => i=5, i=6, ...
;; 类似的,可以使用 `let` 定义
(let loop ((i 0))
(when (< i 10)
(printf "i=~a\n" i)
(loop (add1 i)))) ; => i=0, i=1, ...
;; 看上面的例子怎么增加一个新的 `loop' 形式, 但是 Racket 已经有了一个非常
;; 灵活的 `for' 了:
(for ([i 10])
(printf "i=~a\n" i)) ; => i=0, i=1, ...
(for ([i (in-range 5 10)])
(printf "i=~a\n" i)) ; => i=5, i=6, ...
;;; 其他形式的迭代
;; `for' 允许在很多数据结构中迭代:
;; 列表, 向量, 字符串, Set, 散列表, 等...
(for ([i (in-list '(l i s t))])
(displayln i))
(for ([i (in-vector #(v e c t o r))])
(displayln i))
(for ([i (in-string "string")])
(displayln i))
(for ([i (in-set (set 'x 'y 'z))])
(displayln i))
(for ([(k v) (in-hash (hash 'a 1 'b 2 'c 3 ))])
(printf "key:~a value:~a\n" k v))
;;; 更多复杂的迭代
;; 并行扫描多个序列 (遇到长度小的就停止)
(for ([i 10] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x 1:y 2:z
;; 嵌套循环
(for* ([i 2] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x, 0:y, 0:z, 1:x, 1:y, 1:z
;; 带有条件判断的 `for`
(for ([i 1000]
#:when (> i 5)
#:unless (odd? i)
#:break (> i 10))
(printf "i=~a\n" i))
; => i=6, i=8, i=10
;;; 更多的例子帮助你加深理解..
;; 和 `for' 循环非常像 -- 收集结果
(for/list ([i '(1 2 3)])
(add1 i)) ; => '(2 3 4)
(for/list ([i '(1 2 3)] #:when (even? i))
i) ; => '(2)
(for/list ([i 10] [j '(x y z)])
(list i j)) ; => '((0 x) (1 y) (2 z))
(for/list ([i 1000] #:when (> i 5) #:unless (odd? i) #:break (> i 10))
i) ; => '(6 8 10)
(for/hash ([i '(1 2 3)])
(values i (number->string i)))
; => '#hash((1 . "1") (2 . "2") (3 . "3"))
;; 也有很多其他的内置方法来收集循环中的值:
(for/sum ([i 10]) (* i i)) ; => 285
(for/product ([i (in-range 1 11)]) (* i i)) ; => 13168189440000
(for/and ([i 10] [j (in-range 10 20)]) (< i j)) ; => #t
(for/or ([i 10] [j (in-range 0 20 2)]) (= i j)) ; => #t
;; 如果需要合并计算结果, 使用 `for/fold'
(for/fold ([sum 0]) ([i '(1 2 3 4)]) (+ sum i)) ; => 10
;; (这个函数可以在大部分情况下替代普通的命令式循环)
;;; 异常
;; 要捕获一个异常,使用 `with-handlers' 形式
(with-handlers ([exn:fail? (lambda (exn) 999)])
(+ 1 "2")) ; => 999
(with-handlers ([exn:break? (lambda (exn) "no time")])
(sleep 3)
"phew") ; => "phew", 如果你打断了它,那么结果 => "no time"
;; 使用 `raise' 抛出一个异常后者其他任何值
(with-handlers ([number? ; 捕获抛出的数字类型的值
identity]) ; 将它们作为普通值
(+ 1 (raise 2))) ; => 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6\. 可变的值
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 使用 `set!' 给一个已经存在的变量赋一个新值
(define n 5)
(set! n (add1 n))
n ; => 6
;; 给那些明确地需要变化的值使用 `boxes` (在其他语言里类似指针
;; 或者引用)
(define n* (box 5))
(set-box! n* (add1 (unbox n*)))
(unbox n*) ; => 6
;; 很多 Racket 诗句类型是不可变的 (对,列表,等),有一些既是可变的
;; 又是不可变的 (字符串,向量,散列表
;; 等...)
;; 使用 `vector' 或者 `make-vector' 创建一个可变的向量
(define vec (vector 2 2 3 4))
(define wall (make-vector 100 'bottle-of-beer))
;; 使用 `vector-set!` 更新一项
(vector-set! vec 0 1)
(vector-set! wall 99 'down)
vec ; => #(1 2 3 4)
;; 创建一个空的可变散列表,然后操作它
(define m3 (make-hash))
(hash-set! m3 'a 1)
(hash-set! m3 'b 2)
(hash-set! m3 'c 3)
(hash-ref m3 'a) ; => 1
(hash-ref m3 'd 0) ; => 0
(hash-remove! m3 'a)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7\. 模块
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 模块让你将你的代码组织为多个文件,成为可重用的模块,
;; 在这里,我们使用嵌套在本文的整个大模块
;; 里的子模块(从 "#lang" 这一行开始)
(module cake racket/base ; 基于 racket/base 定义一个 `cake` 模块
(provide print-cake) ; 这个模块导出的函数
(define (print-cake n)
(show " ~a " n #\.)
(show " .-~a-. " n #\|)
(show " | ~a | " n #\space)
(show "---~a---" n #\-))
(define (show fmt n ch) ; 内部函数
(printf fmt (make-string n ch))
(newline)))
;; 使用 `require` 从模块中得到所有 `provide` 的函数
(require 'cake) ; 这里的 `'`表示是本地的子模块
(print-cake 3)
; (show "~a" 1 #\A) ; => 报错, `show' 没有被导出,不存在
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 8\. 类和对象
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 创建一个 fish% 类(%是给类绑定用的)
(define fish%
(class object%
(init size) ; 初始化的参数
(super-new) ; 父类的初始化
;; 域
(define current-size size)
;; 公共方法
(define/public (get-size)
current-size)
(define/public (grow amt)
(set! current-size (+ amt current-size)))
(define/public (eat other-fish)
(grow (send other-fish get-size)))))
;; 创建一个 fish% 类的示例
(define charlie
(new fish% [size 10]))
;; 使用 `send' 调用一个对象的方法
(send charlie get-size) ; => 10
(send charlie grow 6)
(send charlie get-size) ; => 16
;; `fish%' 是一个普通的值,我们可以用它来混入
(define (add-color c%)
(class c%
(init color)
(super-new)
(define my-color color)
(define/public (get-color) my-color)))
(define colored-fish% (add-color fish%))
(define charlie2 (new colored-fish% [size 10] [color 'red]))
(send charlie2 get-color)
;; 或者,不带名字
(send (new (add-color fish%) [size 10] [color 'red]) get-color)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 9\. 宏
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 宏让你扩展这门语言的语法
;; 让我们定义一个while循环
(define-syntax-rule (while condition body ...)
(let loop ()
(when condition
body ...
(loop))))
(let ([i 0])
(while (< i 10)
(displayln i)
(set! i (add1 i))))
;; 宏是安全的,你不能修改现有的变量
(define-syntax-rule (swap! x y) ; !表示会修改
(let ([tmp x])
(set! x y)
(set! y tmp)))
(define tmp 2)
(define other 3)
(swap! tmp other)
(printf "tmp = ~a; other = ~a\n" tmp other)
;; 变量 `tmp` 被重命名为 `tmp_1`
;; 避免名字冲突
;; (let ([tmp_1 tmp])
;; (set! tmp other)
;; (set! other tmp_1))
;; 但它们仍然会导致错误代码,比如:
(define-syntax-rule (bad-while condition body ...)
(when condition
body ...
(bad-while condition body ...)))
;; 这个宏会挂掉,它产生了一个无限循环,如果你试图去使用它
;; 编译器会进入死循环
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10\. 契约
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 契约限制变量从模块中导入
(module bank-account racket
(provide (contract-out
[deposit (-> positive? any)] ; 数量一直是正值
[balance (-> positive?)]))
(define amount 0)
(define (deposit a) (set! amount (+ amount a)))
(define (balance) amount)
)
(require 'bank-account)
(deposit 5)
(balance) ; => 5
;; 客户端尝试存储一个负值时会出错
;; (deposit -5) ; => deposit: contract violation
;; expected: positive?
;; given: -5
;; more details....
~~~
## 进一步阅读
想知道更多吗? 尝试 [Getting Started with Racket](http://docs.racket-lang.org/getting-started/)
R
最后更新于:2022-04-01 05:33:04
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=R
源代码下载: [learnr-zh.r](http://learnxinyminutes.com/docs/files/learnr-zh.r)
R 是一门统计语言。它有很多数据分析和挖掘程序包。可以用来统计、分析和制图。 你也可以在 LaTeX 文档中运行 `R` 命令。
~~~
# 评论以 # 开始
# R 语言原生不支持 多行注释
# 但是你可以像这样来多行注释
# 在窗口里按回车键可以执行一条命令
###################################################################
# 不用懂编程就可以开始动手了
###################################################################
data() # 浏览内建的数据集
data(rivers) # 北美主要河流的长度(数据集)
ls() # 在工作空间中查看「河流」是否出现
head(rivers) # 撇一眼数据集
# 735 320 325 392 524 450
length(rivers) # 我们测量了多少条河流?
# 141
summary(rivers)
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 135.0 310.0 425.0 591.2 680.0 3710.0
stem(rivers) # 茎叶图(一种类似于直方图的展现形式)
#
# The decimal point is 2 digit(s) to the right of the |
#
# 0 | 4
# 2 | 011223334555566667778888899900001111223333344455555666688888999
# 4 | 111222333445566779001233344567
# 6 | 000112233578012234468
# 8 | 045790018
# 10 | 04507
# 12 | 1471
# 14 | 56
# 16 | 7
# 18 | 9
# 20 |
# 22 | 25
# 24 | 3
# 26 |
# 28 |
# 30 |
# 32 |
# 34 |
# 36 | 1
stem(log(rivers)) # 查看数据集的方式既不是标准形式,也不是取log后的结果! 看起来,是钟形曲线形式的基本数据集
# The decimal point is 1 digit(s) to the left of the |
#
# 48 | 1
# 50 |
# 52 | 15578
# 54 | 44571222466689
# 56 | 023334677000124455789
# 58 | 00122366666999933445777
# 60 | 122445567800133459
# 62 | 112666799035
# 64 | 00011334581257889
# 66 | 003683579
# 68 | 0019156
# 70 | 079357
# 72 | 89
# 74 | 84
# 76 | 56
# 78 | 4
# 80 |
# 82 | 2
hist(rivers, col="#333333", border="white", breaks=25) # 试试用这些参数画画 (译者注:给 river 做统计频数直方图,包含了这些参数:数据源,颜色,边框,空格)
hist(log(rivers), col="#333333", border="white", breaks=25) #你还可以做更多式样的绘图
# 还有其他一些简单的数据集可以被用来加载。R 语言包括了大量这种 data()
data(discoveries)
plot(discoveries, col="#333333", lwd=3, xlab="Year", main="Number of important discoveries per year")
# 译者注:参数为(数据源,颜色,线条宽度,X 轴名称,标题)
plot(discoveries, col="#333333", lwd=3, type = "h", xlab="Year", main="Number of important discoveries per year")
# 除了按照默认的年份排序,我们还可以排序来发现特征
sort(discoveries)
# [1] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2
# [26] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3
# [51] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4
# [76] 4 4 4 4 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 8 9 10 12
stem(discoveries, scale=2) # 译者注:茎叶图(数据,放大系数)
#
# The decimal point is at the |
#
# 0 | 000000000
# 1 | 000000000000
# 2 | 00000000000000000000000000
# 3 | 00000000000000000000
# 4 | 000000000000
# 5 | 0000000
# 6 | 000000
# 7 | 0000
# 8 | 0
# 9 | 0
# 10 | 0
# 11 |
# 12 | 0
max(discoveries)
# 12
summary(discoveries)
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 0.0 2.0 3.0 3.1 4.0 12.0
#基本的统计学操作也不需要任何编程知识
#随机生成数据
round(runif(7, min=.5, max=6.5))
# 译者注:runif 产生随机数,round 四舍五入
# 1 4 6 1 4 6 4
# 你输出的结果会和我们给出的不同,除非我们设置了相同的随机种子 random.seed(31337)
#从标准高斯函数中随机生成 9 次
rnorm(9)
# [1] 0.07528471 1.03499859 1.34809556 -0.82356087 0.61638975 -1.88757271
# [7] -0.59975593 0.57629164 1.08455362
#########################
# 基础编程
#########################
# 数值
#“数值”指的是双精度的浮点数
5 # 5
class(5) # "numeric"
5e4 # 50000 # 用科学技术法方便的处理极大值、极小值或者可变的量级
6.02e23 # 阿伏伽德罗常数#
1.6e-35 # 布朗克长度
# 长整数并用 L 结尾
5L # 5
#输出5L
class(5L) # "integer"
# 可以自己试一试?用 class() 函数获取更多信息
# 事实上,你可以找一些文件查阅 `xyz` 以及xyz的差别
# `xyz` 用来查看源码实现,?xyz 用来看帮助
# 算法
10 + 66 # 76
53.2 - 4 # 49.2
2 * 2.0 # 4
3L / 4 # 0.75
3 %% 2 # 1
# 特殊数值类型
class(NaN) # "numeric"
class(Inf) # "numeric"
class(-Inf) # "numeric" # 在以下场景中会用到 integrate( dnorm(x), 3, Inf ) -- 消除 Z 轴数据
# 但要注意,NaN 并不是唯一的特殊数值类型……
class(NA) # 看上面
class(NULL) # NULL
# 简单列表
c(6, 8, 7, 5, 3, 0, 9) # 6 8 7 5 3 0 9
c('alef', 'bet', 'gimmel', 'dalet', 'he')
c('Z', 'o', 'r', 'o') == "Zoro" # FALSE FALSE FALSE FALSE
# 一些优雅的内置功能
5:15 # 5 6 7 8 9 10 11 12 13 14 15
seq(from=0, to=31337, by=1337)
# [1] 0 1337 2674 4011 5348 6685 8022 9359 10696 12033 13370 14707
# [13] 16044 17381 18718 20055 21392 22729 24066 25403 26740 28077 29414 30751
letters
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
# [20] "t" "u" "v" "w" "x" "y" "z"
month.abb # "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
# Access the n'th element of a list with list.name[n] or sometimes list.name[[n]]
# 使用 list.name[n] 来访问第 n 个列表元素,有时候需要使用 list.name[[n]]
letters[18] # "r"
LETTERS[13] # "M"
month.name[9] # "September"
c(6, 8, 7, 5, 3, 0, 9)[3] # 7
# 字符串
# 字符串和字符在 R 语言中没有区别
"Horatio" # "Horatio"
class("Horatio") # "character"
substr("Fortuna multis dat nimis, nulli satis.", 9, 15) # "multis "
gsub('u', 'ø', "Fortuna multis dat nimis, nulli satis.") # "Fortøna møltis dat nimis, nølli satis."
# 逻辑值
# 布尔值
class(TRUE) # "logical"
class(FALSE) # "logical"
# 和我们预想的一样
TRUE == TRUE # TRUE
TRUE == FALSE # FALSE
FALSE != FALSE # FALSE
FALSE != TRUE # TRUE
# 缺失数据(NA)也是逻辑值
class(NA) # "logical"
#定义NA为逻辑型
# 因子
# 因子是为数据分类排序设计的(像是排序小朋友们的年级或性别)
levels(factor(c("female", "male", "male", "female", "NA", "female"))) # "female" "male" "NA"
factor(c("female", "female", "male", "NA", "female"))
# female female male NA female
# Levels: female male NA
data(infert) # 自然以及引产导致的不育症
levels(infert$education) # "0-5yrs" "6-11yrs" "12+ yrs"
# 变量
# 有许多种方式用来赋值
x = 5 # 这样可以
y <- "1" # 更推荐这样
TRUE -> z # 这样可行,但是很怪
#我们还可以使用强制转型
as.numeric(y) # 1
as.character(x) # "5"
# 循环
# for 循环语句
for (i in 1:4) {
print(i)
}
# while 循环
a <- 10
while (a > 4) {
cat(a, "...", sep = "")
a <- a - 1
}
# 记住,在 R 语言中 for / while 循环都很慢
# 建议使用 apply()(我们一会介绍)来错做一串数据(比如一列或者一行数据)
# IF/ELSE
# 再来看这些优雅的标准
if (4 > 3) {
print("Huzzah! It worked!")
} else {
print("Noooo! This is blatantly illogical!")
}
# =>
# [1] "Huzzah! It worked!"
# 函数
# 定义如下
jiggle <- function(x) {
x + rnorm(x, sd=.1) #add in a bit of (controlled) noise
return(x)
}
# 和其他 R 语言函数一样调用
jiggle(5) # 5±ε. 使用 set.seed(2716057) 后, jiggle(5)==5.005043
#########################
# 数据容器:vectors, matrices, data frames, and arrays
#########################
# 单维度
# 你可以将目前我们学习到的任何类型矢量化,只要它们拥有相同的类型
vec <- c(8, 9, 10, 11)
vec # 8 9 10 11
# 矢量的类型是这一组数据元素的类型
class(vec) # "numeric"
# If you vectorize items of different classes, weird coercions happen
#如果你强制的将不同类型数值矢量化,会出现特殊值
c(TRUE, 4) # 1 4
c("dog", TRUE, 4) # "dog" "TRUE" "4"
#我们这样来取内部数据,(R 的下标索引顺序 1 开始)
vec[1] # 8
# 我们可以根据条件查找特定数据
which(vec %% 2 == 0) # 1 3
# 抓取矢量中第一个和最后一个字符
head(vec, 1) # 8
tail(vec, 1) # 11
#如果下标溢出或不存会得到 NA
vec[6] # NA
# 你可以使用 length() 获取矢量的长度
length(vec) # 4
# 你可以直接操作矢量或者矢量的子集
vec * 4 # 16 20 24 28
vec[2:3] * 5 # 25 30
# 这里有许多内置的函数,来表现向量
mean(vec) # 9.5
var(vec) # 1.666667
sd(vec) # 1.290994
max(vec) # 11
min(vec) # 8
sum(vec) # 38
# 二维(相同元素类型)
#你可以为同样类型的变量建立矩阵
mat <- matrix(nrow = 3, ncol = 2, c(1,2,3,4,5,6))
mat
# =>
# [,1] [,2]
# [1,] 1 4
# [2,] 2 5
# [3,] 3 6
# 和 vector 不一样的是,一个矩阵的类型真的是 「matrix」,而不是内部元素的类型
class(mat) # => "matrix"
# 访问第一行的字符
mat[1,] # 1 4
# 操作第一行数据
3 * mat[,1] # 3 6 9
# 访问一个特定数据
mat[3,2] # 6
# 转置整个矩阵(译者注:变成 2 行 3 列)
t(mat)
# =>
# [,1] [,2] [,3]
# [1,] 1 2 3
# [2,] 4 5 6
# 使用 cbind() 函数把两个矩阵按列合并,形成新的矩阵
mat2 <- cbind(1:4, c("dog", "cat", "bird", "dog"))
mat2
# =>
# [,1] [,2]
# [1,] "1" "dog"
# [2,] "2" "cat"
# [3,] "3" "bird"
# [4,] "4" "dog"
class(mat2) # matrix
# Again, note what happened!
# 注意
# 因为矩阵内部元素必须包含同样的类型
# 所以现在每一个元素都转化成字符串
c(class(mat2[,1]), class(mat2[,2]))
# 按行合并两个向量,建立新的矩阵
mat3 <- rbind(c(1,2,4,5), c(6,7,0,4))
mat3
# =>
# [,1] [,2] [,3] [,4]
# [1,] 1 2 4 5
# [2,] 6 7 0 4
# 哈哈,数据类型都一样的,没有发生强制转换,生活真美好
# 二维(不同的元素类型)
# 利用 data frame 可以将不同类型数据放在一起
dat <- data.frame(c(5,2,1,4), c("dog", "cat", "bird", "dog"))
names(dat) <- c("number", "species") # 给数据列命名
class(dat) # "data.frame"
dat
# =>
# number species
# 1 5 dog
# 2 2 cat
# 3 1 bird
# 4 4 dog
class(dat$number) # "numeric"
class(dat[,2]) # "factor"
# data.frame() 会将字符向量转换为 factor 向量
# 有很多精妙的方法来获取 data frame 的子数据集
dat$number # 5 2 1 4
dat[,1] # 5 2 1 4
dat[,"number"] # 5 2 1 4
# 多维(相同元素类型)
# 使用 arry 创造一个 n 维的表格
# You can make a two-dimensional table (sort of like a matrix)
# 你可以建立一个 2 维表格(有点像矩阵)
array(c(c(1,2,4,5),c(8,9,3,6)), dim=c(2,4))
# =>
# [,1] [,2] [,3] [,4]
# [1,] 1 4 8 3
# [2,] 2 5 9 6
#你也可以利用数组建立一个三维的矩阵
array(c(c(c(2,300,4),c(8,9,0)),c(c(5,60,0),c(66,7,847))), dim=c(3,2,2))
# =>
# , , 1
#
# [,1] [,2]
# [1,] 2 8
# [2,] 300 9
# [3,] 4 0
#
# , , 2
#
# [,1] [,2]
# [1,] 5 66
# [2,] 60 7
# [3,] 0 847
#列表(多维的,不同类型的)
# R语言有列表的形式
list1 <- list(time = 1:40)
list1$price = c(rnorm(40,.5*list1$time,4)) # 随机
list1
# You can get items in the list like so
# 你可以这样获得列表的元素
list1$time
# You can subset list items like vectors
# 你也可以和矢量一样获取他们的子集
list1$price[4]
#########################
# apply()函数家族
#########################
# 还记得 mat 么?
mat
# =>
# [,1] [,2]
# [1,] 1 4
# [2,] 2 5
# [3,] 3 6
# Use apply(X, MARGIN, FUN) to apply function FUN to a matrix X
# 使用(X, MARGIN, FUN)将函数 FUN 应用到矩阵 X 的行 (MAR = 1) 或者 列 (MAR = 2)
# That is, R does FUN to each row (or column) of X, much faster than a
# R 在 X 的每一行/列使用 FUN,比循环要快很多
apply(mat, MAR = 2, myFunc)
# =>
# [,1] [,2]
# [1,] 3 15
# [2,] 7 19
# [3,] 11 23
# 还有其他家族函数 ?lapply, ?sapply
# 不要被吓到,虽然许多人在此都被搞混
# plyr 程序包的作用是用来改进 apply() 函数家族
install.packages("plyr")
require(plyr)
?plyr
#########################
# 载入数据
#########################
# "pets.csv" 是网上的一个文本
pets <- read.csv("http://learnxinyminutes.com/docs/pets.csv")
pets
head(pets, 2) # 前两行
tail(pets, 1) # 最后一行
# 以 .csv 格式来保存数据集或者矩阵
write.csv(pets, "pets2.csv") # 保存到新的文件 pets2.csv
# set working directory with setwd(), look it up with getwd()
# 使用 setwd() 改变工作目录,使用 getwd() 查看当前工作目录
# 尝试使用 ?read.csv 和 ?write.csv 来查看更多信息
#########################
# 画图
#########################
# 散点图
plot(list1$time, list1$price, main = "fake data") # 译者注:横轴 list1$time,纵轴 wlist1$price,标题 fake data
# 回归图
linearModel <- lm(price ~ time, data = list1) # 译者注:线性模型,数据集为list1,以价格对时间做相关分析模型
linearModel # 拟合结果
# 将拟合结果展示在图上,颜色设为红色
abline(linearModel, col = "red")
# 也可以获取各种各样漂亮的分析图
plot(linearModel)
# 直方图
hist(rpois(n = 10000, lambda = 5), col = "thistle") # 译者注:统计频数直方图
# 柱状图
barplot(c(1,4,5,1,2), names.arg = c("red","blue","purple","green","yellow"))
# 可以尝试着使用 ggplot2 程序包来美化图片
install.packages("ggplot2")
require(ggplot2)
?ggplot2
~~~
## 获得 R
* 从 [http://www.r-project.org/](http://www.r-project.org/) 获得安装包和图形化界面
* [RStudio](http://www.rstudio.com/ide/) 是另一个图形化界面
Python3
最后更新于:2022-04-01 05:33:02
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=python3
源代码下载: [learnpython3-cn.py](http://learnxinyminutes.com/docs/files/learnpython3-cn.py)
Python是由吉多·范罗苏姆(Guido Van Rossum)在90年代早期设计。它是如今最常用的编程 语言之一。它的语法简洁且优美,几乎就是可执行的伪代码。
欢迎大家斧正。英文版原作Louie Dinh [@louiedinh](http://twitter.com/louiedinh) 或着Email louiedinh [at] [谷歌的信箱服务]。中文翻译Geoff Liu。
注意:这篇教程是特别为Python3写的。如果你想学旧版Python2,我们特别有另一篇教程。
~~~
# 用井字符开头的是单行注释
""" 多行字符串用三个引号
包裹,也常被用来做多
行注释
"""
####################################################
## 1\. 原始数据类型和运算符
####################################################
# 整数
3 # => 3
# 算术没有什么出乎意料的
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
# 但是除法例外,会自动转换成浮点数
35 / 5 # => 7.0
5 / 3 # => 1.6666666666666667
# 整数除法的结果都是向下取整
5 // 3 # => 1
5.0 // 3.0 # => 1.0 # 浮点数也可以
-5 // 3 # => -2
-5.0 // 3.0 # => -2.0
# 浮点数的运算结果也是浮点数
3 * 2.0 # => 6.0
# 模除
7 % 3 # => 1
# x的y次方
2**4 # => 16
# 用括号决定优先级
(1 + 3) * 2 # => 8
# 布尔值
True
False
# 用not取非
not True # => False
not False # => True
# 逻辑运算符,注意and和or都是小写
True and False #=> False
False or True #=> True
# 整数也可以当作布尔值
0 and 2 #=> 0
-5 or 0 #=> -5
0 == False #=> True
2 == True #=> False
1 == True #=> True
# 用==判断相等
1 == 1 # => True
2 == 1 # => False
# 用!=判断不等
1 != 1 # => False
2 != 1 # => True
# 比较大小
1 < 10 # => True
1 > 10 # => False
2 <= 2 # => True
2 >= 2 # => True
# 大小比较可以连起来!
1 < 2 < 3 # => True
2 < 3 < 2 # => False
# 字符串用单引双引都可以
"这是个字符串"
'这也是个字符串'
# 用加号连接字符串
"Hello " + "world!" # => "Hello world!"
# 字符串可以被当作字符列表
"This is a string"[0] # => 'T'
# 用.format来格式化字符串
"{} can be {}".format("strings", "interpolated")
# 可以重复参数以节省时间
"{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick")
#=> "Jack be nimble, Jack be quick, Jack jump over the candle stick"
# 如果不想数参数,可以用关键字
"{name} wants to eat {food}".format(name="Bob", food="lasagna") #=> "Bob wants to eat lasagna"
# 如果你的Python3程序也要在Python2.5以下环境运行,也可以用老式的格式化语法
"%s can be %s the %s way" % ("strings", "interpolated", "old")
# None是一个对象
None # => None
# 当与None进行比较时不要用 ==,要用is。is是用来比较两个变量是否指向同一个对象。
"etc" is None # => False
None is None # => True
# None,0,空字符串,空列表,空字典都算是False
# 所有其他值都是True
bool(0) # => False
bool("") # => False
bool([]) #=> False
bool({}) #=> False
####################################################
## 2\. 变量和集合
####################################################
# print是内置的打印函数
print("I'm Python. Nice to meet you!")
# 在给变量赋值前不用提前声明
# 传统的变量命名是小写,用下划线分隔单词
some_var = 5
some_var # => 5
# 访问未赋值的变量会抛出异常
# 参考流程控制一段来学习异常处理
some_unknown_var # 抛出NameError
# 用列表(list)储存序列
li = []
# 创建列表时也可以同时赋给元素
other_li = [4, 5, 6]
# 用append在列表最后追加元素
li.append(1) # li现在是[1]
li.append(2) # li现在是[1, 2]
li.append(4) # li现在是[1, 2, 4]
li.append(3) # li现在是[1, 2, 4, 3]
# 用pop从列表尾部删除
li.pop() # => 3 且li现在是[1, 2, 4]
# 把3再放回去
li.append(3) # li变回[1, 2, 4, 3]
# 列表存取跟数组一样
li[0] # => 1
# 取出最后一个元素
li[-1] # => 3
# 越界存取会造成IndexError
li[4] # 抛出IndexError
# 列表有切割语法
li[1:3] # => [2, 4]
# 取尾
li[2:] # => [4, 3]
# 取头
li[:3] # => [1, 2, 4]
# 隔一个取一个
li[::2] # =>[1, 4]
# 倒排列表
li[::-1] # => [3, 4, 2, 1]
# 可以用三个参数的任何组合来构建切割
# li[始:终:步伐]
# 用del删除任何一个元素
del li[2] # li is now [1, 2, 3]
# 列表可以相加
# 注意:li和other_li的值都不变
li + other_li # => [1, 2, 3, 4, 5, 6]
# 用extend拼接列表
li.extend(other_li) # li现在是[1, 2, 3, 4, 5, 6]
# 用in测试列表是否包含值
1 in li # => True
# 用len取列表长度
len(li) # => 6
# 元组是不可改变的序列
tup = (1, 2, 3)
tup[0] # => 1
tup[0] = 3 # 抛出TypeError
# 列表允许的操作元组大都可以
len(tup) # => 3
tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6)
tup[:2] # => (1, 2)
2 in tup # => True
# 可以把元组合列表解包,赋值给变量
a, b, c = (1, 2, 3) # 现在a是1,b是2,c是3
# 元组周围的括号是可以省略的
d, e, f = 4, 5, 6
# 交换两个变量的值就这么简单
e, d = d, e # 现在d是5,e是4
# 用字典表达映射关系
empty_dict = {}
# 初始化的字典
filled_dict = {"one": 1, "two": 2, "three": 3}
# 用[]取值
filled_dict["one"] # => 1
# 用keys获得所有的键。因为keys返回一个可迭代对象,所以在这里把结果包在list里。我们下面会详细介绍可迭代。
# 注意:字典键的顺序是不定的,你得到的结果可能和以下不同。
list(filled_dict.keys()) # => ["three", "two", "one"]
# 用values获得所有的值。跟keys一样,要用list包起来,顺序也可能不同。
list(filled_dict.values()) # => [3, 2, 1]
# 用in测试一个字典是否包含一个键
"one" in filled_dict # => True
1 in filled_dict # => False
# 访问不存在的键会导致KeyError
filled_dict["four"] # KeyError
# 用get来避免KeyError
filled_dict.get("one") # => 1
filled_dict.get("four") # => None
# 当键不存在的时候get方法可以返回默认值
filled_dict.get("one", 4) # => 1
filled_dict.get("four", 4) # => 4
# setdefault方法只有当键不存在的时候插入新值
filled_dict.setdefault("five", 5) # filled_dict["five"]设为5
filled_dict.setdefault("five", 6) # filled_dict["five"]还是5
# 字典赋值
filled_dict.update({"four":4}) #=> {"one": 1, "two": 2, "three": 3, "four": 4}
filled_dict["four"] = 4 # 另一种赋值方法
# 用del删除
del filled_dict["one"] # 从filled_dict中把one删除
# 用set表达集合
empty_set = set()
# 初始化一个集合,语法跟字典相似。
some_set = {1, 1, 2, 2, 3, 4} # some_set现在是{1, 2, 3, 4}
# 可以把集合赋值于变量
filled_set = some_set
# 为集合添加元素
filled_set.add(5) # filled_set现在是{1, 2, 3, 4, 5}
# & 取交集
other_set = {3, 4, 5, 6}
filled_set & other_set # => {3, 4, 5}
# | 取并集
filled_set | other_set # => {1, 2, 3, 4, 5, 6}
# - 取补集
{1, 2, 3, 4} - {2, 3, 5} # => {1, 4}
# in 测试集合是否包含元素
2 in filled_set # => True
10 in filled_set # => False
####################################################
## 3\. 流程控制和迭代器
####################################################
# 先随便定义一个变量
some_var = 5
# 这是个if语句。注意缩进在Python里是有意义的
# 印出"some_var比10小"
if some_var > 10:
print("some_var比10大")
elif some_var < 10: # elif句是可选的
print("some_var比10小")
else: # else也是可选的
print("some_var就是10")
"""
用for循环语句遍历列表
打印:
dog is a mammal
cat is a mammal
mouse is a mammal
"""
for animal in ["dog", "cat", "mouse"]:
print("{} is a mammal".format(animal))
"""
"range(number)"返回数字列表从0到给的数字
打印:
0
1
2
3
"""
for i in range(4):
print(i)
"""
while循环直到条件不满足
打印:
0
1
2
3
"""
x = 0
while x < 4:
print(x)
x += 1 # x = x + 1 的简写
# 用try/except块处理异常状况
try:
# 用raise抛出异常
raise IndexError("This is an index error")
except IndexError as e:
pass # pass是无操作,但是应该在这里处理错误
except (TypeError, NameError):
pass # 可以同时处理不同类的错误
else: # else语句是可选的,必须在所有的except之后
print("All good!") # 只有当try运行完没有错误的时候这句才会运行
# Python提供一个叫做可迭代(iterable)的基本抽象。一个可迭代对象是可以被当作序列
# 的对象。比如说上面range返回的对象就是可迭代的。
filled_dict = {"one": 1, "two": 2, "three": 3}
our_iterable = filled_dict.keys()
print(our_iterable) # => range(1,10) 是一个实现可迭代接口的对象
# 可迭代对象可以遍历
for i in our_iterable:
print(i) # 打印 one, two, three
# 但是不可以随机访问
our_iterable[1] # 抛出TypeError
# 可迭代对象知道怎么生成迭代器
our_iterator = iter(our_iterable)
# 迭代器是一个可以记住遍历的位置的对象
# 用__next__可以取得下一个元素
our_iterator.__next__() #=> "one"
# 再一次调取__next__时会记得位置
our_iterator.__next__() #=> "two"
our_iterator.__next__() #=> "three"
# 当迭代器所有元素都取出后,会抛出StopIteration
our_iterator.__next__() # 抛出StopIteration
# 可以用list一次取出迭代器所有的元素
list(filled_dict.keys()) #=> Returns ["one", "two", "three"]
####################################################
## 4\. 函数
####################################################
# 用def定义新函数
def add(x, y):
print("x is {} and y is {}".format(x, y))
return x + y # 用return语句返回
# 调用函数
add(5, 6) # => 印出"x is 5 and y is 6"并且返回11
# 也可以用关键字参数来调用函数
add(y=6, x=5) # 关键字参数可以用任何顺序
# 我们可以定义一个可变参数函数
def varargs(*args):
return args
varargs(1, 2, 3) # => (1, 2, 3)
# 我们也可以定义一个关键字可变参数函数
def keyword_args(**kwargs):
return kwargs
# 我们来看看结果是什么:
keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"}
# 这两种可变参数可以混着用
def all_the_args(*args, **kwargs):
print(args)
print(kwargs)
"""
all_the_args(1, 2, a=3, b=4) prints:
(1, 2)
{"a": 3, "b": 4}
"""
# 调用可变参数函数时可以做跟上面相反的,用*展开序列,用**展开字典。
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args) # 相当于 foo(1, 2, 3, 4)
all_the_args(**kwargs) # 相当于 foo(a=3, b=4)
all_the_args(*args, **kwargs) # 相当于 foo(1, 2, 3, 4, a=3, b=4)
# 函数作用域
x = 5
def setX(num):
# 局部作用域的x和全局域的x是不同的
x = num # => 43
print (x) # => 43
def setGlobalX(num):
global x
print (x) # => 5
x = num # 现在全局域的x被赋值
print (x) # => 6
setX(43)
setGlobalX(6)
# 函数在Python是一等公民
def create_adder(x):
def adder(y):
return x + y
return adder
add_10 = create_adder(10)
add_10(3) # => 13
# 也有匿名函数
(lambda x: x > 2)(3) # => True
# 内置的高阶函数
map(add_10, [1, 2, 3]) # => [11, 12, 13]
filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7]
# 用列表推导式可以简化映射和过滤。列表推导式的返回值是另一个列表。
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
####################################################
## 5\. 类
####################################################
# 定义一个继承object的类
class Human(object):
# 类属性,被所有此类的实例共用。
species = "H. sapiens"
# 构造方法,当实例被初始化时被调用。注意名字前后的双下划线,这是表明这个属
# 性或方法对Python有特殊意义,但是允许用户自行定义。你自己取名时不应该用这
# 种格式。
def __init__(self, name):
# Assign the argument to the instance's name attribute
self.name = name
# 实例方法,第一个参数总是self,就是这个实例对象
def say(self, msg):
return "{name}: {message}".format(name=self.name, message=msg)
# 类方法,被所有此类的实例共用。第一个参数是这个类对象。
@classmethod
def get_species(cls):
return cls.species
# 静态方法。调用时没有实例或类的绑定。
@staticmethod
def grunt():
return "*grunt*"
# 构造一个实例
i = Human(name="Ian")
print(i.say("hi")) # 印出 "Ian: hi"
j = Human("Joel")
print(j.say("hello")) # 印出 "Joel: hello"
# 调用一个类方法
i.get_species() # => "H. sapiens"
# 改一个共用的类属性
Human.species = "H. neanderthalensis"
i.get_species() # => "H. neanderthalensis"
j.get_species() # => "H. neanderthalensis"
# 调用静态方法
Human.grunt() # => "*grunt*"
####################################################
## 6\. 模块
####################################################
# 用import导入模块
import math
print(math.sqrt(16)) # => 4
# 也可以从模块中导入个别值
from math import ceil, floor
print(ceil(3.7)) # => 4.0
print(floor(3.7)) # => 3.0
# 可以导入一个模块中所有值
# 警告:不建议这么做
from math import *
# 如此缩写模块名字
import math as m
math.sqrt(16) == m.sqrt(16) # => True
# Python模块其实就是普通的Python文件。你可以自己写,然后导入,
# 模块的名字就是文件的名字。
# 你可以这样列出一个模块里所有的值
import math
dir(math)
####################################################
## 7\. 高级用法
####################################################
# 用生成器(generators)方便地写惰性运算
def double_numbers(iterable):
for i in iterable:
yield i + i
# 生成器只有在需要时才计算下一个值。它们每一次循环只生成一个值,而不是把所有的
# 值全部算好。这意味着double_numbers不会生成大于15的数字。
#
# range的返回值也是一个生成器,不然一个1到900000000的列表会花很多时间和内存。
#
# 如果你想用一个Python的关键字当作变量名,可以加一个下划线来区分。
range_ = range(1, 900000000)
# 当找到一个 >=30 的结果就会停
for i in double_numbers(range_):
print(i)
if i >= 30:
break
# 装饰器(decorators)
# 这个例子中,beg装饰say
# beg会先调用say。如果返回的say_please为真,beg会改变返回的字符串。
from functools import wraps
def beg(target_function):
@wraps(target_function)
def wrapper(*args, **kwargs):
msg, say_please = target_function(*args, **kwargs)
if say_please:
return "{} {}".format(msg, "Please! I am poor :(")
return msg
return wrapper
@beg
def say(say_please=False):
msg = "Can you buy me a beer?"
return msg, say_please
print(say()) # Can you buy me a beer?
print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :(
~~~
## 想继续学吗?
### 线上免费材料(英文)
* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/)
* [Dive Into Python](http://www.diveintopython.net/)
* [Ideas for Python Projects](http://pythonpracticeprojects.com/)
* [The Official Docs](http://docs.python.org/3/)
* [Hitchhiker’s Guide to Python](http://docs.python-guide.org/en/latest/)
* [Python Module of the Week](http://pymotw.com/3/)
* [A Crash Course in Python for Scientists](http://nbviewer.ipython.org/5920182)
### 书籍(也是英文)
* [Programming Python](http://www.amazon.com/gp/product/0596158106/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596158106&linkCode=as2&tag=homebits04-20)
* [Dive Into Python](http://www.amazon.com/gp/product/1441413022/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441413022&linkCode=as2&tag=homebits04-20)
* [Python Essential Reference](http://www.amazon.com/gp/product/0672329786/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0672329786&linkCode=as2&tag=homebits04-20)
Python
最后更新于:2022-04-01 05:33:00
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=python
源代码下载: [learnpython-zh.py](http://learnxinyminutes.com/docs/files/learnpython-zh.py)
Python 由 Guido Van Rossum 在90年代初创建。 它现在是最流行的语言之一 我喜爱python是因为它有极为清晰的语法,甚至可以说,它就是可以执行的伪代码
很欢迎来自您的反馈,你可以在[@louiedinh](http://twitter.com/louiedinh) 和 louiedinh [at] [google’s email service] 这里找到我
注意: 这篇文章针对的版本是Python 2.7,但大多也可使用于其他Python 2的版本 如果是Python 3,请在网络上寻找其他教程
~~~
# 单行注释
""" 多行字符串可以用
三个引号包裹,不过这也可以被当做
多行注释
"""
####################################################
## 1\. 原始数据类型和操作符
####################################################
# 数字类型
3 # => 3
# 简单的算数
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
35 / 5 # => 7
# 整数的除法会自动取整
5 / 2 # => 2
# 要做精确的除法,我们需要引入浮点数
2.0 # 浮点数
11.0 / 4.0 # => 2.75 精确多了
# 括号具有最高优先级
(1 + 3) * 2 # => 8
# 布尔值也是基本的数据类型
True
False
# 用 not 来取非
not True # => False
not False # => True
# 相等
1 == 1 # => True
2 == 1 # => False
# 不等
1 != 1 # => False
2 != 1 # => True
# 更多的比较操作符
1 < 10 # => True
1 > 10 # => False
2 <= 2 # => True
2 >= 2 # => True
# 比较运算可以连起来写!
1 < 2 < 3 # => True
2 < 3 < 2 # => False
# 字符串通过 " 或 ' 括起来
"This is a string."
'This is also a string.'
# 字符串通过加号拼接
"Hello " + "world!" # => "Hello world!"
# 字符串可以被视为字符的列表
"This is a string"[0] # => 'T'
# % 可以用来格式化字符串
"%s can be %s" % ("strings", "interpolated")
# 也可以用 format 方法来格式化字符串
# 推荐使用这个方法
"{0} can be {1}".format("strings", "formatted")
# 也可以用变量名代替数字
"{name} wants to eat {food}".format(name="Bob", food="lasagna")
# None 是对象
None # => None
# 不要用相等 `==` 符号来和None进行比较
# 要用 `is`
"etc" is None # => False
None is None # => True
# 'is' 可以用来比较对象的相等性
# 这个操作符在比较原始数据时没多少用,但是比较对象时必不可少
# None, 0, 和空字符串都被算作 False
# 其他的均为 True
0 == False # => True
"" == False # => True
####################################################
## 2\. 变量和集合
####################################################
# 很方便的输出
print "I'm Python. Nice to meet you!"
# 给变量赋值前不需要事先声明
some_var = 5 # 一般建议使用小写字母和下划线组合来做为变量名
some_var # => 5
# 访问未赋值的变量会抛出异常
# 可以查看控制流程一节来了解如何异常处理
some_other_var # 抛出 NameError
# if 语句可以作为表达式来使用
"yahoo!" if 3 > 2 else 2 # => "yahoo!"
# 列表用来保存序列
li = []
# 可以直接初始化列表
other_li = [4, 5, 6]
# 在列表末尾添加元素
li.append(1) # li 现在是 [1]
li.append(2) # li 现在是 [1, 2]
li.append(4) # li 现在是 [1, 2, 4]
li.append(3) # li 现在是 [1, 2, 4, 3]
# 移除列表末尾元素
li.pop() # => 3 li 现在是 [1, 2, 4]
# 重新加进去
li.append(3) # li is now [1, 2, 4, 3] again.
# 像其他语言访问数组一样访问列表
li[0] # => 1
# 访问最后一个元素
li[-1] # => 3
# 越界会抛出异常
li[4] # 抛出越界异常
# 切片语法需要用到列表的索引访问
# 可以看做数学之中左闭右开区间
li[1:3] # => [2, 4]
# 省略开头的元素
li[2:] # => [4, 3]
# 省略末尾的元素
li[:3] # => [1, 2, 4]
# 删除特定元素
del li[2] # li 现在是 [1, 2, 3]
# 合并列表
li + other_li # => [1, 2, 3, 4, 5, 6] - 并不会不改变这两个列表
# 通过拼接来合并列表
li.extend(other_li) # li 是 [1, 2, 3, 4, 5, 6]
# 用 in 来返回元素是否在列表中
1 in li # => True
# 返回列表长度
len(li) # => 6
# 元组类似于列表,但它是不可改变的
tup = (1, 2, 3)
tup[0] # => 1
tup[0] = 3 # 类型错误
# 对于大多数的列表操作,也适用于元组
len(tup) # => 3
tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6)
tup[:2] # => (1, 2)
2 in tup # => True
# 你可以将元组解包赋给多个变量
a, b, c = (1, 2, 3) # a 是 1,b 是 2,c 是 3
# 如果不加括号,将会被自动视为元组
d, e, f = 4, 5, 6
# 现在我们可以看看交换两个数字是多么容易的事
e, d = d, e # d 是 5,e 是 4
# 字典用来储存映射关系
empty_dict = {}
# 字典初始化
filled_dict = {"one": 1, "two": 2, "three": 3}
# 字典也用中括号访问元素
filled_dict["one"] # => 1
# 把所有的键保存在列表中
filled_dict.keys() # => ["three", "two", "one"]
# 键的顺序并不是唯一的,得到的不一定是这个顺序
# 把所有的值保存在列表中
filled_dict.values() # => [3, 2, 1]
# 和键的顺序相同
# 判断一个键是否存在
"one" in filled_dict # => True
1 in filled_dict # => False
# 查询一个不存在的键会抛出 KeyError
filled_dict["four"] # KeyError
# 用 get 方法来避免 KeyError
filled_dict.get("one") # => 1
filled_dict.get("four") # => None
# get 方法支持在不存在的时候返回一个默认值
filled_dict.get("one", 4) # => 1
filled_dict.get("four", 4) # => 4
# setdefault 是一个更安全的添加字典元素的方法
filled_dict.setdefault("five", 5) # filled_dict["five"] 的值为 5
filled_dict.setdefault("five", 6) # filled_dict["five"] 的值仍然是 5
# 集合储存无顺序的元素
empty_set = set()
# 初始化一个集合
some_set = set([1, 2, 2, 3, 4]) # some_set 现在是 set([1, 2, 3, 4])
# Python 2.7 之后,大括号可以用来表示集合
filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4}
# 向集合添加元素
filled_set.add(5) # filled_set 现在是 {1, 2, 3, 4, 5}
# 用 & 来计算集合的交
other_set = {3, 4, 5, 6}
filled_set & other_set # => {3, 4, 5}
# 用 | 来计算集合的并
filled_set | other_set # => {1, 2, 3, 4, 5, 6}
# 用 - 来计算集合的差
{1, 2, 3, 4} - {2, 3, 5} # => {1, 4}
# 用 in 来判断元素是否存在于集合中
2 in filled_set # => True
10 in filled_set # => False
####################################################
## 3\. 控制流程
####################################################
# 新建一个变量
some_var = 5
# 这是个 if 语句,在 python 中缩进是很重要的。
# 下面的代码片段将会输出 "some var is smaller than 10"
if some_var > 10:
print "some_var is totally bigger than 10."
elif some_var < 10: # 这个 elif 语句是不必须的
print "some_var is smaller than 10."
else: # 这个 else 也不是必须的
print "some_var is indeed 10."
"""
用for循环遍历列表
输出:
dog is a mammal
cat is a mammal
mouse is a mammal
"""
for animal in ["dog", "cat", "mouse"]:
# 你可以用 % 来格式化字符串
print "%s is a mammal" % animal
"""
`range(number)` 返回从0到给定数字的列表
输出:
0
1
2
3
"""
for i in range(4):
print i
"""
while 循环
输出:
0
1
2
3
"""
x = 0
while x < 4:
print x
x += 1 # x = x + 1 的简写
# 用 try/except 块来处理异常
# Python 2.6 及以上适用:
try:
# 用 raise 来抛出异常
raise IndexError("This is an index error")
except IndexError as e:
pass # pass 就是什么都不做,不过通常这里会做一些恢复工作
####################################################
## 4\. 函数
####################################################
# 用 def 来新建函数
def add(x, y):
print "x is %s and y is %s" % (x, y)
return x + y # 通过 return 来返回值
# 调用带参数的函数
add(5, 6) # => 输出 "x is 5 and y is 6" 返回 11
# 通过关键字赋值来调用函数
add(y=6, x=5) # 顺序是无所谓的
# 我们也可以定义接受多个变量的函数,这些变量是按照顺序排列的
def varargs(*args):
return args
varargs(1, 2, 3) # => (1,2,3)
# 我们也可以定义接受多个变量的函数,这些变量是按照关键字排列的
def keyword_args(**kwargs):
return kwargs
# 实际效果:
keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"}
# 你也可以同时将一个函数定义成两种形式
def all_the_args(*args, **kwargs):
print args
print kwargs
"""
all_the_args(1, 2, a=3, b=4) prints:
(1, 2)
{"a": 3, "b": 4}
"""
# 当调用函数的时候,我们也可以进行相反的操作,把元组和字典展开为参数
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args) # 等价于 foo(1, 2, 3, 4)
all_the_args(**kwargs) # 等价于 foo(a=3, b=4)
all_the_args(*args, **kwargs) # 等价于 foo(1, 2, 3, 4, a=3, b=4)
# 函数在 python 中是一等公民
def create_adder(x):
def adder(y):
return x + y
return adder
add_10 = create_adder(10)
add_10(3) # => 13
# 匿名函数
(lambda x: x > 2)(3) # => True
# 内置高阶函数
map(add_10, [1, 2, 3]) # => [11, 12, 13]
filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7]
# 可以用列表方法来对高阶函数进行更巧妙的引用
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
####################################################
## 5\. 类
####################################################
# 我们新建的类是从 object 类中继承的
class Human(object):
# 类属性,由所有类的对象共享
species = "H. sapiens"
# 基本构造函数
def __init__(self, name):
# 将参数赋给对象成员属性
self.name = name
# 成员方法,参数要有 self
def say(self, msg):
return "%s: %s" % (self.name, msg)
# 类方法由所有类的对象共享
# 这类方法在调用时,会把类本身传给第一个参数
@classmethod
def get_species(cls):
return cls.species
# 静态方法是不需要类和对象的引用就可以调用的方法
@staticmethod
def grunt():
return "*grunt*"
# 实例化一个类
i = Human(name="Ian")
print i.say("hi") # 输出 "Ian: hi"
j = Human("Joel")
print j.say("hello") # 输出 "Joel: hello"
# 访问类的方法
i.get_species() # => "H. sapiens"
# 改变共享属性
Human.species = "H. neanderthalensis"
i.get_species() # => "H. neanderthalensis"
j.get_species() # => "H. neanderthalensis"
# 访问静态变量
Human.grunt() # => "*grunt*"
####################################################
## 6\. 模块
####################################################
# 我们可以导入其他模块
import math
print math.sqrt(16) # => 4
# 我们也可以从一个模块中导入特定的函数
from math import ceil, floor
print ceil(3.7) # => 4.0
print floor(3.7) # => 3.0
# 从模块中导入所有的函数
# 警告:不推荐使用
from math import *
# 简写模块名
import math as m
math.sqrt(16) == m.sqrt(16) # => True
# Python的模块其实只是普通的python文件
# 你也可以创建自己的模块,并且导入它们
# 模块的名字就和文件的名字相同
# 也可以通过下面的方法查看模块中有什么属性和方法
import math
dir(math)
~~~
## 更多阅读
希望学到更多?试试下面的链接:
* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/)
* [Dive Into Python](http://www.diveintopython.net/)
* [The Official Docs](http://docs.python.org/2.6/)
* [Hitchhiker’s Guide to Python](http://docs.python-guide.org/en/latest/)
* [Python Module of the Week](http://pymotw.com/2/)
PHP
最后更新于:2022-04-01 05:32:57
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=PHP
源代码下载: [learnphp-zh.php](http://learnxinyminutes.com/docs/files/learnphp-zh.php)
这份教程所使用的版本是 PHP 5+.
~~~
<?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).
Perl
最后更新于:2022-04-01 05:32:55
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=perl
源代码下载: [learnperl-cn.pl](http://learnxinyminutes.com/docs/files/learnperl-cn.pl)
Perl 5是一个功能强大、特性齐全的编程语言,有25年的历史。
Perl 5可以在包括便携式设备和大型机的超过100个平台上运行,既适用于快速原型构建,也适用于大型项目开发。
~~~
# 单行注释以#号开头
#### Perl的变量类型
# 变量以$号开头。
# 合法变量名以英文字母或者下划线起始,
# 后接任意数目的字母、数字或下划线。
### Perl有三种主要的变量类型:标量、数组和哈希。
## 标量
# 标量类型代表单个值:
my $animal = "camel";
my $answer = 42;
# 标量类型值可以是字符串、整型或浮点类型,Perl会根据需要自动进行类型转换。
## 数组
# 数组类型代表一列值:
my @animals = ("camel", "llama", "owl");
my @numbers = (23, 42, 69);
my @mixed = ("camel", 42, 1.23);
## 哈希
# 哈希类型代表一个键/值对的集合:
my %fruit_color = ("apple", "red", "banana", "yellow");
# 可以使用空格和“=>”操作符更清晰的定义哈希:
my %fruit_color = (
apple => "red",
banana => "yellow",
);
# perldata中有标量、数组和哈希更详细的介绍。 (perldoc perldata).
# 可以用引用构建更复杂的数据类型,比如嵌套的列表和哈希。
#### 逻辑和循环结构
# Perl有大多数常见的逻辑和循环控制结构
if ( $var ) {
...
} elsif ( $var eq 'bar' ) {
...
} else {
...
}
unless ( condition ) {
...
}
# 上面这个比"if (!condition)"更可读。
# 有Perl特色的后置逻辑结构
print "Yow!" if $zippy;
print "We have no bananas" unless $bananas;
# while
while ( condition ) {
...
}
# for和foreach
for ($i = 0; $i <= $max; $i++) {
...
}
foreach (@array) {
print "This element is $_\n";
}
#### 正则表达式
# Perl对正则表达式有深入广泛的支持,perlrequick和perlretut等文档有详细介绍。简单来说:
# 简单匹配
if (/foo/) { ... } # 如果 $_ 包含"foo"逻辑为真
if ($a =~ /foo/) { ... } # 如果 $a 包含"foo"逻辑为真
# 简单替换
$a =~ s/foo/bar/; # 将$a中的foo替换为bar
$a =~ s/foo/bar/g; # 将$a中所有的foo替换为bar
#### 文件和输入输出
# 可以使用“open()”函数打开文件用于输入输出。
open(my $in, "<", "input.txt") or die "Can't open input.txt: $!";
open(my $out, ">", "output.txt") or die "Can't open output.txt: $!";
open(my $log, ">>", "my.log") or die "Can't open my.log: $!";
# 可以用"<>"操作符读取一个打开的文件句柄。 在标量语境下会读取一行,
# 在列表环境下会将整个文件读入并将每一行赋给列表的一个元素:
my $line = <$in>;
my @lines = <$in>;
#### 子程序
# 写子程序很简单:
sub logger {
my $logmessage = shift;
open my $logfile, ">>", "my.log" or die "Could not open my.log: $!";
print $logfile $logmessage;
}
# 现在可以像内置函数一样调用子程序:
logger("We have a logger subroutine!");
~~~
#### 使用Perl模块
Perl模块提供一系列特性来帮助你避免重新发明轮子,CPAN是下载模块的好地方( http://www.cpan.org/ )。Perl发行版本身也包含很多流行的模块。
perlfaq有很多常见问题和相应回答,也经常有对优秀CPAN模块的推荐介绍。
#### 深入阅读
- [perl-tutorial](http://perl-tutorial.org/)
- [www.perl.com的learn站点](http://www.perl.org/learn.html)
- [perldoc](http://perldoc.perl.org/)
- 以及 perl 内置的: `perldoc perlintro`
Matlab
最后更新于:2022-04-01 05:32:53
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=Matlab
MATLAB 是 MATrix LABoratory (矩阵实验室)的缩写,它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
如果您有任何需要反馈或交流的内容,请联系本教程作者[@the_ozzinator](https://twitter.com/the_ozzinator)、[osvaldo.t.mendoza@gmail.com](mailto:osvaldo.t.mendoza@gmail.com)。
~~~
% 以百分号作为注释符
%{
多行注释
可以
这样
表示
%}
% 指令可以随意跨行,但需要在跨行处用 '...' 标明:
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 of % 关闭 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' - 返回一个元组
char(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) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复数)
% 元素运算 vs. 矩阵运算
% 单独运算符就是对矩阵整体进行矩阵运算
% 在运算符加上英文句点就是对矩阵中的元素进行元素计算
% 示例如下:
A * B % 矩阵乘法,要求 A 的列数等于 B 的行数
A .* B % 元素乘法,要求 A 和 B 形状一致(A 的行数等于 B 的行数, A 的列数等于 B 的列数)
% 元素乘法的结果是与 A 和 B 形状一致的矩阵,其每个元素等于 A 对应位置的元素乘 B 对应位置的元素
% 以下函数中,函数名以 m 结尾的执行矩阵运算,其余执行元素运算:
exp(A) % 对矩阵中每个元素做指数运算
expm(A) % 对矩阵整体做指数运算
sqrt(A) % 对矩阵中每个元素做开方运算
sqrtm(A) % 对矩阵整体做开放运算(即试图求出一个矩阵,该矩阵与自身的乘积等于 A 矩阵)
% 绘图
x = 0:.10: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 的转置,等价于 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 元素的全排列
~~~
## 相关资料
* 官方网页:[http://http://www.mathworks.com/products/matlab/](http://www.mathworks.com/products/matlab/)
* 官方论坛:[http://www.mathworks.com/matlabcentral/answers/](http://www.mathworks.com/matlabcentral/answers/)
Markdown
最后更新于:2022-04-01 05:32:50
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=markdown
源代码下载: [learnmarkdown-cn.md](http://learnxinyminutes.com/docs/files/learnmarkdown-cn.md)
Markdown 由 John Gruber 于 2004年创立. 它旨在成为一门容易读写的语法结构,并可以便利地转换成 HTML(以及其他很多)格式。
欢迎您多多反馈以及分支和请求合并。
~~~
<!-- Markdown 是 HTML 的父集,所以任何 HTML 文件都是有效的 Markdown。
这意味着我们可以在 Markdown 里使用任何 HTML 元素,比如注释元素,
且不会被 Markdown 解析器所影响。不过如果你在 Markdown 文件内创建了 HTML 元素,
你将无法在 HTML 元素的内容中使用 Markdown 语法。-->
<!-- 在不同的解析器中,Markdown 的实现方法有所不同。
此教程会指出当某功能是否通用及是否只对某一解析器有效。 -->
<!-- 标头 -->
<!-- 通过在文本前加上不同数量的hash(#), 你可以创建相对应的 <h1>
到 <h6> HTML元素。-->
# 这是一个 <h1>
## 这是一个 <h2>
### 这是一个 <h3>
#### 这是一个 <h4>
##### 这是一个 <h5>
###### 这是一个 <h6>
<!-- 对于 <h1> 和 <h2> 元素,Markdown 额外提供了两种添加方式。 -->
这是一个 h1
=============
这是一个 h2
-------------
<!-- 简易文本样式 -->
<!-- 文本的斜体,粗体,和删除线在 Markdown 中可以轻易地被实现。-->
*此文本为斜体。*
_此文本也是。_
**此文本为粗体。**
__此文本也是__
***此文本是斜体加粗体。***
**_或者这样。_**
*__这个也是!__*
<!-- 在 Github 采用的 Markdown 中 -->
~~此文本为删除线效果。~~
<!-- 单个段落由一句或多句邻近的句子组成,这些句子由一个或多个空格分隔。-->
这是第一段落. 这句话在同一个段落里,好玩么?
现在我是第二段落。
这句话也在第二段落!
这句话在第三段落!
<!-- 如果你插入一个 HTML中的<br />标签,你可以在段末加入两个以上的空格,
然后另起一段。-->
此段落结尾有两个空格(选中以显示)。
上文有一个 <br /> !
<!-- 段落引用可由 > 字符轻松实现。-->
> 这是一个段落引用. 你可以
> 手动断开你的句子,然后在每句句子前面添加 “>” 字符。或者让你的句子变得很长,以至于他们自动得断开。
> 只要你的文字以“>” 字符开头,两种方式无异。
> 你也对文本进行
>> 多层引用
> 这多机智啊!
<!-- 序列 -->
<!-- 无序序列可由星号,加号或者减号来建立 -->
* 项目
* 项目
* 另一个项目
或者
+ 项目
+ 项目
+ 另一个项目
或者
- 项目
- 项目
- 最后一个项目
<!-- 有序序列可由数字加点来实现 -->
1. 项目一
2. 项目二
3. 项目三
<!-- 即使你的标签数字有误,Markdown 依旧会呈现出正确的序号,
不过这并不是一个好主意-->
1. 项目一
1. 项目二
1. 项目三
<!-- (此段与前例一模一样) -->
<!-- 你也可以使用子序列 -->
1. 项目一
2. 项目二
3. 项目三
* 子项目
* 子项目
4. 项目四
<!-- 代码段落 -->
<!-- 代码段落(HTML中 <code>标签)可以由缩进四格(spaces)
或者一个制表符(tab)实现-->
This is code
So is this
<!-- 在你的代码中,你仍然使用tab可以进行缩进操作 -->
my_array.each do |item|
puts item
end
<!-- 内联代码可由反引号 ` 实现 -->
John 甚至不知道 `go_to()` 方程是干嘛的!
<!-- 在Github的 Markdown中,对于代码你可以使用特殊的语法 -->
\`\`\`ruby <!-- 插入时记得移除反斜线, 仅留```ruby ! -->
def foobar
puts "Hello world!"
end
\`\`\` <!-- 这里也是,移除反斜线,仅留 ``` -->
<!-- 以上代码不需要缩进,而且 Github 会根据```后表明的语言来进行语法高亮 -->
<!-- 水平线 (<hr />) -->
<!-- 水平线可由三个或以上的星号或者减号创建,可带可不带空格。 -->
***
---
- - -
****************
<!-- 链接 -->
<!-- Markdown 最棒的地方就是简易的链接制作。链接文字放在中括号[]内,
在随后的括弧()内加入url。-->
[点我点我!](http://test.com/)
<!-- 你也可以为链接加入一个标题:在括弧内使用引号 -->
[点我点我!](http://test.com/ "连接到Test.com")
<!-- 相对路径也可以有 -->
[去 music](/music/).
<!-- Markdown同样支持引用样式的链接 -->
[点此链接][link1]以获取更多信息!
[看一看这个链接][foobar] 如果你愿意的话.
<!-- 链接的标题可以处于单引号中,括弧中或是被忽略。引用名可以在文档的任意何处,
并且可以随意命名,只要名称不重复。-->
<!-- “隐含式命名” 的功能可以让链接文字作为引用名 -->
[This][] is a link.
<!-- 但这并不常用 -->
<!-- 图像 -->
<!-- 图像与链接相似,只需在前添加一个感叹号 -->
![这是我图像的悬停文本(alt text)](http://imgur.com/myimage.jpg "可选命名")
<!-- 引用样式也同样起作用 -->
![这是我的悬停文本.][myimage]
<!-- 杂项 -->
<!-- 自动链接 -->
<http://testwebsite.com/> 与
[http://testwebsite.com/](http://testwebsite.com/) 等同
<!-- 电子邮件的自动链接 -->
<foo@bar.com>
<!-- 转义字符 -->
我希望 *将这段文字置于星号之间* 但是我不希望它被
斜体化, 所以我就: \*这段置文字于星号之间\*。
<!-- 表格 -->
<!-- 表格只被 Github 的 Markdown 支持,并且有一点笨重,但如果你真的要用的话: -->
| 第一列 | 第二列 | 第三列 |
| :---------- | :------: | ----------: |
| 左对齐 | 居个中 | 右对齐 |
| 某某某 | 某某某 | 某某某 |
<!-- 或者, 同样的 -->
第一列 | 第二列 | 第三列
:-- | :-: | --:
这太丑了 | 药不能 | 停
<!-- 结束! -->
~~~
更多信息, 请于[此处](http://daringfireball.net/projects/Markdown/syntax)参见 John Gruber 关于语法的官方帖子,及于[此处](https://github.com/adam-p/Markdown-here/wiki/Markdown-Cheatsheet) 参见 Adam Pritchard 的摘要笔记。
Lua
最后更新于:2022-04-01 05:32:48
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=Lua
源代码下载: [lua-cn.lua](http://learnxinyminutes.com/docs/files/lua-cn.lua)
~~~
-- 单行注释以两个连字符开头
--[[
多行注释
--]]
----------------------------------------------------
-- 1\. 变量和流程控制
----------------------------------------------------
num = 42 -- 所有的数字都是双精度浮点型。
-- 别害怕,64位的双精度浮点型数字中有52位用于
-- 保存精确的整型值; 对于52位以内的整型值,
-- 不用担心精度问题。
s = 'walternate' -- 和Python一样,字符串不可变。
t = "也可以用双引号"
u = [[ 多行的字符串
以两个方括号
开始和结尾。]]
t = nil -- 撤销t的定义; Lua 支持垃圾回收。
-- 块使用do/end之类的关键字标识:
while num < 50 do
num = num + 1 -- 不支持 ++ 或 += 运算符。
end
-- If语句:
if num > 40 then
print('over 40')
elseif s ~= 'walternate' then -- ~= 表示不等于。
-- 像Python一样,用 == 检查是否相等 ;字符串同样适用。
io.write('not over 40\n') -- 默认标准输出。
else
-- 默认全局变量。
thisIsGlobal = 5 -- 通常使用驼峰。
-- 如何定义局部变量:
local line = io.read() -- 读取标准输入的下一行。
-- ..操作符用于连接字符串:
print('Winter is coming, ' .. line)
end
-- 未定义的变量返回nil。
-- 这不是错误:
foo = anUnknownVariable -- 现在 foo = nil.
aBoolValue = false
--只有nil和false为假; 0和 ''均为真!
if not aBoolValue then print('false') end
-- 'or'和 'and'短路
-- 类似于C/js里的 a?b:c 操作符:
ans = aBoolValue and 'yes' or 'no' --> 'no'
karlSum = 0
for i = 1, 100 do -- 范围包含两端
karlSum = karlSum + i
end
-- 使用 "100, 1, -1" 表示递减的范围:
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end
-- 通常,范围表达式为begin, end[, step].
-- 循环的另一种结构:
repeat
print('the way of the future')
num = num - 1
until num == 0
----------------------------------------------------
-- 2\. 函数。
----------------------------------------------------
function fib(n)
if n < 2 then return 1 end
return fib(n - 2) + fib(n - 1)
end
-- 支持闭包及匿名函数:
function adder(x)
-- 调用adder时,会创建返回的函数,
-- 并且会记住x的值:
return function (y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16)) --> 25
print(a2(64)) --> 100
-- 返回值、函数调用和赋值都可以
-- 使用长度不匹配的list。
-- 不匹配的接收方会被赋值nil;
-- 不匹配的发送方会被丢弃。
x, y, z = 1, 2, 3, 4
-- x = 1、y = 2、z = 3, 而 4 会被丢弃。
function bar(a, b, c)
print(a, b, c)
return 4, 8, 15, 16, 23, 42
end
x, y = bar('zaphod') --> 打印 "zaphod nil nil"
-- 现在 x = 4, y = 8, 而值15..42被丢弃。
-- 函数是一等公民,可以是局部的,也可以是全局的。
-- 以下表达式等价:
function f(x) return x * x end
f = function (x) return x * x end
-- 这些也是等价的:
local function g(x) return math.sin(x) end
local g; g = function (x) return math.sin(x) end
-- 'local g'使得g可以自引用。
-- 顺便提下,三角函数以弧度为单位。
-- 用一个字符串参数调用函数,可以省略括号:
print 'hello' --可以工作。
-- 调用函数时,如果只有一个table参数,
-- 同样可以省略括号(table详情见下):
print {} -- 一样可以工作。
----------------------------------------------------
-- 3\. Table。
----------------------------------------------------
-- Table = Lua唯一的组合数据结构;
-- 它们是关联数组。
-- 类似于PHP的数组或者js的对象,
-- 它们是哈希表或者字典,也可以当列表使用。
-- 按字典/map的方式使用Table:
-- Dict字面量默认使用字符串类型的key:
t = {key1 = 'value1', key2 = false}
-- 字符串key可以使用类似js的点标记:
print(t.key1) -- 打印 'value1'.
t.newKey = {} -- 添加新的键值对。
t.key2 = nil -- 从table删除 key2。
-- 使用任何非nil的值作为key:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- 打印 "tau"
-- 数字和字符串的key按值匹配的
-- table按id匹配。
a = u['@!#'] -- 现在 a = 'qbert'.
b = u[{}] -- 我们或许期待的是 1729, 但是得到的是nil:
-- b = nil ,因为没有找到。
-- 之所以没找到,是因为我们用的key与保存数据时用的不是同
-- 一个对象。
-- 所以字符串和数字是移植性更好的key。
-- 只需要一个table参数的函数调用不需要括号:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -- 打印'Sonmi~451'.
for key, val in pairs(u) do -- 遍历Table
print(key, val)
end
-- _G 是一个特殊的table,用于保存所有的全局变量
print(_G['_G'] == _G) -- 打印'true'.
-- 按列表/数组的方式使用:
-- 列表字面量隐式添加整数键:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do -- #v 是列表的大小
print(v[i]) -- 索引从 1 开始!! 太疯狂了!
end
-- 'list'并非真正的类型,v 其实是一个table,
-- 只不过它用连续的整数作为key,可以像list那样去使用。
----------------------------------------------------
-- 3.1 元表(metatable) 和元方法(metamethod)。
----------------------------------------------------
-- table的元表提供了一种机制,支持类似操作符重载的行为。
-- 稍后我们会看到元表如何支持类似js prototype的行为。
f1 = {a = 1, b = 2} -- 表示一个分数 a/b.
f2 = {a = 2, b = 3}
-- 这会失败:
-- s = f1 + f2
metafraction = {}
function metafraction.__add(f1, f2)
sum = {}
sum.b = f1.b * f2.b
sum.a = f1.a * f2.b + f2.a * f1.b
return sum
end
setmetatable(f1, metafraction)
setmetatable(f2, metafraction)
s = f1 + f2 -- 调用在f1的元表上的__add(f1, f2) 方法
-- f1, f2 没有关于元表的key,这点和js的prototype不一样。
-- 因此你必须用getmetatable(f1)获取元表。
-- 元表是一个普通的table,
-- 元表的key是普通的Lua中的key,例如__add。
-- 但是下面一行代码会失败,因为s没有元表:
-- t = s + s
-- 下面提供的与类相似的模式可以解决这个问题:
-- 元表的__index 可以重载用于查找的点操作符:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal -- 可以工作!感谢元表
-- 如果在table中直接查找key失败,会使用
-- 元表的__index 递归地重试。
-- __index的值也可以是function(tbl, key)
-- 这样可以支持自定义查找。
-- __index、__add等的值,被称为元方法。
-- 这里是一个table元方法的清单:
-- __add(a, b) for a + b
-- __sub(a, b) for a - b
-- __mul(a, b) for a * b
-- __div(a, b) for a / b
-- __mod(a, b) for a % b
-- __pow(a, b) for a ^ b
-- __unm(a) for -a
-- __concat(a, b) for a .. b
-- __len(a) for #a
-- __eq(a, b) for a == b
-- __lt(a, b) for a < b
-- __le(a, b) for a <= b
-- __index(a, b) <fn or a table> for a.b
-- __newindex(a, b, c) for a.b = c
-- __call(a, ...) for a(...)
----------------------------------------------------
-- 3.2 与类相似的table和继承。
----------------------------------------------------
-- Lua没有内建的类;可以通过不同的方法,利用表和元表
-- 来实现类。
-- 下面是一个例子,解释在后面:
Dog = {} -- 1\.
function Dog:new() -- 2\.
newObj = {sound = 'woof'} -- 3\.
self.__index = self -- 4\.
return setmetatable(newObj, self) -- 5\.
end
function Dog:makeSound() -- 6\.
print('I say ' .. self.sound)
end
mrDog = Dog:new() -- 7\.
mrDog:makeSound() -- 'I say woof' -- 8\.
-- 1\. Dog看上去像一个类;其实它是一个table。
-- 2\. 函数tablename:fn(...) 等价于
-- 函数tablename.fn(self, ...)
-- 冒号(:)只是添加了self作为第一个参数。
-- 阅读7 & 8条 了解self变量是如何得到其值的。
-- 3\. newObj是类Dog的一个实例。
-- 4\. self = 被继承的类。通常self = Dog,不过继承可以改变它。
-- 如果把newObj的元表和__index都设置为self,
-- newObj就可以得到self的函数。
-- 5\. 备忘:setmetatable返回其第一个参数。
-- 6\. 冒号(:)的作用和第2条一样,不过这里
-- self是一个实例,而不是类
-- 7\. 等价于Dog.new(Dog),所以在new()中,self = Dog。
-- 8\. 等价于mrDog.makeSound(mrDog); self = mrDog。
----------------------------------------------------
-- 继承的例子:
LoudDog = Dog:new() -- 1\.
function LoudDog:makeSound()
s = self.sound .. ' ' -- 2\.
print(s .. s .. s)
end
seymour = LoudDog:new() -- 3\.
seymour:makeSound() -- 'woof woof woof' -- 4\.
-- 1\. LoudDog获得Dog的方法和变量列表。
-- 2\. 因为new()的缘故,self拥有了一个'sound' key,参见第3条。
-- 3\. 等价于LoudDog.new(LoudDog),转换一下就是
-- Dog.new(LoudDog),这是因为LoudDog没有'new' key,
-- 但是它的元表中有 __index = Dog。
-- 结果: seymour的元表是LoudDog,并且
-- LoudDog.__index = Dog。所以有seymour.key
-- = seymour.key, LoudDog.key, Dog.key
-- 从其中第一个有指定key的table获取。
-- 4\. 在LoudDog可以找到'makeSound'的key;
-- 等价于LoudDog.makeSound(seymour)。
-- 如果有必要,子类也可以有new(),与基类相似:
function LoudDog:new()
newObj = {}
-- 初始化newObj
self.__index = self
return setmetatable(newObj, self)
end
----------------------------------------------------
-- 4\. 模块
----------------------------------------------------
--[[ 我把这部分给注释了,这样脚本剩下的部分可以运行
-- 假设文件mod.lua的内容类似这样:
local M = {}
local function sayMyName()
print('Hrunkner')
end
function M.sayHello()
print('Why hello there')
sayMyName()
end
return M
-- 另一个文件可以使用mod.lua的功能:
local mod = require('mod') -- 运行文件mod.lua.
-- require是包含模块的标准做法。
-- require等价于: (针对没有被缓存的情况;参见后面的内容)
local mod = (function ()
<contents of mod.lua>
end)()
-- mod.lua被包在一个函数体中,因此mod.lua的局部变量
-- 对外不可见。
-- 下面的代码可以工作,因为在这里mod = mod.lua 中的 M:
mod.sayHello() -- Says hello to Hrunkner.
-- 这是错误的;sayMyName只在mod.lua中存在:
mod.sayMyName() -- 错误
-- require返回的值会被缓存,所以一个文件只会被运行一次,
-- 即使它被require了多次。
-- 假设mod2.lua包含代码"print('Hi!')"。
local a = require('mod2') -- 打印Hi!
local b = require('mod2') -- 不再打印; a=b.
-- dofile与require类似,但是不缓存:
dofile('mod2') --> Hi!
dofile('mod2') --> Hi! (再次运行,与require不同)
-- loadfile加载一个lua文件,但是并不运行它。
f = loadfile('mod2') -- Calling f() runs mod2.lua.
-- loadstring是loadfile的字符串版本。
g = loadstring('print(343)') --返回一个函数。
g() -- 打印343; 在此之前什么也不打印。
--]]
~~~
## 参考
为什么?我非常兴奋地学习lua, 这样我就可以使用[Löve 2D游戏引擎](http://love2d.org/)来编游戏。
怎么做?我从[BlackBulletIV的面向程序员的Lua指南](http://nova-fusion.com/2012/08/27/lua-for-programmers-part-1/)入门。接着我阅读了官方的[Lua编程](http://www.lua.org/pil/contents.html)一书。
lua-users.org上的[Lua简明参考](http://lua-users.org/files/wiki_insecure/users/thomasl/luarefv51.pdf)应该值得一看。
本文没有涉及标准库的内容:
* [string library](http://lua-users.org/wiki/StringLibraryTutorial)
* [table library](http://lua-users.org/wiki/TableLibraryTutorial)
* [math library](http://lua-users.org/wiki/MathLibraryTutorial)
* [io library](http://lua-users.org/wiki/IoLibraryTutorial)
* [os library](http://lua-users.org/wiki/OsLibraryTutorial)
使用Lua,欢乐常在!
LiveScript
最后更新于:2022-04-01 05:32:46
# [X分钟速成Y](http://learnxinyminutes.com/)
## 其中 Y=LiveScript
源代码下载: [learnLivescript.ls](http://learnxinyminutes.com/docs/files/learnLivescript.ls)
LiveScript 是一种具有函数式特性且编译成 JavaScript 的语言,能对应 JavaScript 的基本语法。 还有些额外的特性如:柯里化,组合函数,模式匹配,还有借镜于 Haskell,F# 和 Scala 的许多特点。
LiveScript 诞生于 [Coco](http://satyr.github.io/coco/),而 Coco 诞生于 [CoffeeScript](http://coffeescript.org/)。 LiveScript 目前已释出稳定版本,开发中的新版本将会加入更多特性。
非常期待您的反馈,你可以通过 [@kurisuwhyte](https://twitter.com/kurisuwhyte) 与我连系 :)
~~~
# 与 CoffeeScript 一样,LiveScript 使用 # 单行注解。
/*
多行注解与 C 相同。使用注解可以避免被当成 JavaScript 输出。
*/
~~~
~~~
# 语法的部份,LiveScript 使用缩进取代 {} 来定义区块,
# 使用空白取代 () 来执行函数。
########################################################################
## 1\. 值类型
########################################################################
# `void` 取代 `undefined` 表示未定义的值
void # 与 `undefined` 等价但更安全(不会被覆写)
# 空值则表示成 Null。
null
# 最基本的值类型数据是罗辑类型:
true
false
# 罗辑类型的一些别名,等价于前者:
on; off
yes; no
# 数字与 JS 一样,使用倍精度浮点数表示。
10
0.4 # 开头的 0 是必要的
# 可以使用底线及单位后缀提高可读性,编译器会自动略过底线及单位后缀。
12_344km
# 字串与 JS 一样,是一种不可变的字元序列:
"Christina" # 单引号也可以!
"""Multi-line
strings
are
okay
too."""
# 在前面加上 \ 符号也可以表示字串:
\keyword # => 'keyword'
# 数组是值的有序集合。
fruits =
* \apple
* \orange
* \pear
# 可以用 [] 简洁地表示数组:
fruits = [ \apple, \orange, \pear ]
# 你可以更方便地建立字串数组,并使用空白区隔元素。
fruits = <[ apple orange pear ]>
# 以 0 为起始值的数组下标获取元素:
fruits[0] # => "apple"
# 对象是无序键值对集合(更多给节将在下面章节讨论)。
person =
name: "Christina"
likes:
* "kittens"
* "and other cute stuff"
# 你也可以用更简洁的方式表示对象:
person = {name: "Christina", likes: ["kittens", "and other cute stuff"]}
# 可以通过键值获取值:
person.name # => "Christina"
person["name"] # => "Christina"
# 正则表达式的使用跟 JavaScript 一样:
trailing-space = /\s$/ # dashed-words 变成 dashedWords
# 你也可以用多行描述表达式!(注解和空白会被忽略)
funRE = //
function\s+(.+) # name
\s* \((.*)\) \s* # arguments
{ (.*) } # body
//
########################################################################
## 2\. 基本运算
########################################################################
# 数值操作符与 JavaScript 一样:
1 + 2 # => 3
2 - 1 # => 1
2 * 3 # => 6
4 / 2 # => 2
3 % 2 # => 1
# 比较操作符大部份也一样,除了 `==` 等价于 JS 中的 `===`,
# JS 中的 `==` 在 LiveScript 里等价于 `~=`,
# `===` 能进行对象、数组和严格比较。
2 == 2 # => true
2 == "2" # => false
2 ~= "2" # => true
2 === "2" # => false
[1,2,3] == [1,2,3] # => false
[1,2,3] === [1,2,3] # => true
+0 == -0 # => true
+0 === -0 # => false
# 其它关系操作符包括 <、<=、> 和 >=
# 罗辑值可以通过 `or`、`and` 和 `not` 结合:
true and false # => false
false or true # => true
not false # => true
# 集合也有一些便利的操作符
[1, 2] ++ [3, 4] # => [1, 2, 3, 4]
'a' in <[ a b c ]> # => true
'name' of { name: 'Chris' } # => true
########################################################################
## 3\. 函数
########################################################################
# 因为 LiveScript 是函数式特性的语言,你可以期待函数在语言里被高规格的对待。
add = (left, right) -> left + right
add 1, 2 # => 3
# 加上 ! 防止函数执行后的返回值
two = -> 2
two!
# LiveScript 与 JavaScript 一样使用函式作用域,且一样拥有闭包的特性。
# 与 JavaScript 不同的地方在于,`=` 变量赋值时,左边的对象永远不用变量宣告。
# `:=` 操作符允许*重新賦值*父作用域里的变量。
# 你可以解构函数的参数,从不定长度的参数结构里获取感兴趣的值。
tail = ([head, ...rest]) -> rest
tail [1, 2, 3] # => [2, 3]
# 你也可以使用一元或二元操作符转换参数。当然也可以预设传入的参数值。
foo = (a = 1, b = 2) -> a + b
foo! # => 3
# 你可以以拷贝的方式传入参数来避免副作用,例如:
copy = (^^target, source) ->
for k,v of source => target[k] = v
target
a = { a: 1 }
copy a, { b: 2 } # => { a: 1, b: 2 }
a # => { a: 1 }
# 使用长箭号取代短箭号来柯里化一个函数:
add = (left, right) --> left + right
add1 = add 1
add1 2 # => 3
# 函式里有一个隐式的 `it` 变量,意谓着你不用宣告它。
identity = -> it
identity 1 # => 1
# 操作符在 LiveScript 里不是一個函数,但你可以简单地将它们转换成函数!
# Enter the operator sectioning:
divide-by-2 = (/ 2)
[2, 4, 8, 16].map(divide-by-2) .reduce (+)
# LiveScript 里不只有应用函数,如同其它良好的函数式语言,你可以合并函数获得更多发挥:
double-minus-one = (- 1) . (* 2)
# 除了普通的数学公式合并 `f . g` 之外,还有 `>>` 和 `<<` 操作符定义函数的合并顺序。
double-minus-one = (* 2) >> (- 1)
double-minus-one = (- 1) << (* 2)
# 说到合并函数的参数, LiveScript 使用 `|>` 和 `<|` 操作符将参数传入:
map = (f, xs) --> xs.map f
[1 2 3] |> map (* 2) # => [2 4 6]
# 你也可以选择填入值的位置,只需要使用底线 _ 标记:
reduce = (f, xs, initial) --> xs.reduce f, initial
[1 2 3] |> reduce (+), _, 0 # => 6
# 你也能使 _ 让任何函数变成偏函数应用:
div = (left, right) -> left / right
div-by-2 = div _, 2
div-by-2 4 # => 2
# 最后,也很重要的,LiveScript 拥有後呼叫特性, 可以是基於回调的代码
# (你可以试试其它函数式特性的解法,比如 Promises):
readFile = (name, f) -> f name
a <- readFile 'foo'
b <- readFile 'bar'
console.log a + b
# 等同於:
readFile 'foo', (a) -> readFile 'bar', (b) -> console.log a + b
########################################################################
## 4\. 模式、判断和流程控制
########################################################################
# 流程控制可以使用 `if...else` 表达式:
x = if n > 0 then \positive else \negative
# 除了 `then` 你也可以使用 `=>`
x = if n > 0 => \positive
else \negative
# 过於复杂的流程可以用 `switch` 表达式代替:
y = {}
x = switch
| (typeof y) is \number => \number
| (typeof y) is \string => \string
| 'length' of y => \array
| otherwise => \object # `otherwise` 和 `_` 是等价的。
# 函数主体、宣告式和赋值式可以表式成 `switch`,这可以省去一些代码:
take = (n, [x, ...xs]) -->
| n == 0 => []
| _ => [x] ++ take (n - 1), xs
########################################################################
## 5\. 推导式
########################################################################
# 在 JavaScript 的标准函式库里有一些辅助函数能帮助处理列表及对象
#(LiveScript 则带有一个 prelude.ls ,作为标准函式库的补充 ),
# 推导式能让你使用优雅的语法且快速地处理这些事:
oneToTwenty = [1 to 20]
evens = [x for x in oneToTwenty when x % 2 == 0]
# 在推导式里 `when` 和 `unless` 可以当成过滤器使用。
# 对象推导式在使用上也是同样的方式,差别在于你使用的是对象而不是数组:
copy = { [k, v] for k, v of source }
########################################################################
## 6\. OOP
########################################################################
# 虽然 LiveScript 是一门函数式语言,但具有一些命令式及面向对象的特性。
# 像是 class 语法和一些借镜於 CoffeeScript 的类别继承语法糖:
class Animal
(@name, kind) ->
@kind = kind
action: (what) -> "*#{@name} (a #{@kind}) #{what}*"
class Cat extends Animal
(@name) -> super @name, 'cat'
purr: -> @action 'purrs'
kitten = new Cat 'Mei'
kitten.purr! # => "*Mei (a cat) purrs*"
# 除了类别的单一继承模式之外,还提供了像混入 (Mixins) 这种特性。
# Mixins 在语言里被当成普通对象:
Huggable =
hug: -> @action 'is hugged'
class SnugglyCat extends Cat implements Huggable
kitten = new SnugglyCat 'Purr'
kitten.hug! # => "*Mei (a cat) is hugged*"
~~~
## 延伸阅读
LiveScript 还有许多强大之处,但这些应该足够启发你写些小型函数式程式了。 [LiveScript](http://livescript.net/)有更多关于 LiveScript 的资讯 和线上编译器等着你来试!
你也可以参考 [prelude.ls](http://gkz.github.io/prelude-ls/),和一些 `#livescript` 的网络聊天室频道。