diff options
author | Suzane Sant Ana <tetestonaldo@gmail.com> | 2017-12-31 14:27:06 -0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-31 14:27:06 -0200 |
commit | 42f9329bb3a028d374d6397991ac48b44064741e (patch) | |
tree | 1e75e2b3e122aeb863e3ffa037f6f64c4027fbf8 /zh-cn | |
parent | e6b77595f2669d66ac7be43c6e6083cbff80a9a7 (diff) | |
parent | 70a36c9bd970b928adde06afb2bd69f6ba8e5d5c (diff) |
Merge pull request #1 from adambard/master
update
Diffstat (limited to 'zh-cn')
38 files changed, 7711 insertions, 851 deletions
diff --git a/zh-cn/angularjs-cn.html.markdown b/zh-cn/angularjs-cn.html.markdown new file mode 100644 index 00000000..418a817d --- /dev/null +++ b/zh-cn/angularjs-cn.html.markdown @@ -0,0 +1,710 @@ +--- +category: tool +tool: AngularJS +contributors: + - ["Walter Cordero", "http://waltercordero.com"] +filename: learnangular-cn.html +translators: + - ["Jiang Haiyun", "http://www.atjiang.com"] +lang: zh-cn +--- + +## AngularJS 教程。 + +AngularJS 1.0 版在 2012 年发布。 +Miško Hevery, 一位 Google 员工, 从 2009 年开始开发 AngularJS。 +结果发现这个想法很好,从而该项目现在也被 Google 官方所支持了。 + +AngularJS 是一个 JavaScript 框架。它可以通过一个 "script" 标签添加到一个 HTML 页面中。 +AngularJS 通过指令扩展了 HTML 属性,并且通过表达式将数据绑定到 HTML。 + +## 你应该已经了解了的知识 + +在学习 AngularJS 之前, 你应该对以下知识有了基本的了解: + +- HTML +- CSS +- JavaScript + +```html +// AngularJS 是一个 JavaScript 框架。它是一个用 JavaScript 写的库。 +// AngularJS 以一个 JavaScript 文件的形式发布,并且能通过一个 script 标签添加到一个网页中: +// <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> + +/////////////////////////////////// +// AngularJS 扩展 HTML + +//AngularJS 通过 ng-directives 扩展 HTML。 +//ng-app 指令定义一个 AngularJS 应用。 +//ng-model 指令将 HTML 控件 (input, select, textarea) 的值绑定到应用的数据上。 +//ng-bind 指令将应用的数据绑定到 HTML 视图上。 +<!DOCTYPE html> +<html> + <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> + <body> + <div ng-app=""> + <p>Name: <input type="text" ng-model="name"></p> + <p ng-bind="name"></p> + </div> + </body> +</html> + +/* + * 例子解析: + * AngularJS 在网页加载后自动开启。 + * ng-app 指令告诉 AngularJS: <div> 元素是 AngularJS 应用的 "所有者"。 + * ng-model 指令将 input 输入框的值绑定到应用的 name 变量上。 + * ng-bind 指令将 <p> 元素的 innerHTML 绑定到应用的 name 变量上。 +*/ +<tag> 这里是要解析的内容 </tag> + +/////////////////////////////////// +// AngularJS 表达式 + +// AngularJS 表达式写在双括号内: {{ 表达式 }}。 +// AngularJS 表达式采用和 ng-bind 指令一样的方式将数据绑定到 HTML。 +// AngularJS 将在编写表达式的原样位置上 "输出" 数据。 +// AngularJS 表达式非常像 JavaScript 表达式:它们能包含文本,运算符和变量。 +// 例如 {{ 5 + 5 }} 或 {{ firstName + " " + lastName }} +<!DOCTYPE html> +<html> + <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> + <body> + <div ng-app=""> + <p>My first expression: {{ 5 + 5 }}</p> + </div> + </body> +</html> + +//如果你删除了 ng-app 指令, HTML 将原样显示表达式,不对它进行解析: +<!DOCTYPE html> +<html> + <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> + <body> + <div> + <p>My first expression: {{ 5 + 5 }}</p> + </div> + </body> +</html> + +// AngularJS 表达式采用和 ng-bind 指令一样的方式将 AngularJS 数据绑定到 HTML。 +<!DOCTYPE html> +<html> +<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> + <body> + <div ng-app=""> + <p>Name: <input type="text" ng-model="name"></p> + <p>{{name}}</p> + </div> + </body> +</html> + +// AngularJS 的数字类似 JavaScript 的数字: +<div ng-app="" ng-init="quantity=1;cost=5"> + <p>Total in dollar: {{ quantity * cost }}</p> +</div> + +//AngularJS 的字符串类似 JavaScript 的字符串: +<div ng-app="" ng-init="firstName='John';lastName='Doe'"> + <p>The name is <span ng-bind="firstName + ' ' + lastName"></span></p> +</div> + +//AngularJS 的对象类似 JavaScript 的对象: +<div ng-app="" ng-init="person={firstName:'John',lastName:'Doe'}"> + <p>The name is {{ person.lastName }}</p> +</div> + +//AngularJS 的数组类似 JavaScript 的数组: +<div ng-app="" ng-init="points=[1,15,19,2,40]"> + <p>The third result is {{ points[2] }}</p> +</div> + +// 和 JavaScript 表达式一样, AngularJS 表达式能包含文本,运算符和变量。 +// 和 JavaScript 表达式不同, AngularJS 表达式能写在 HTML 内。 +// AngularJS 表达式不支持条件,循环和异常,而 JavaScript 表达式却支持。 +// AngularJS 表达式支持过滤器,而 JavaScript 表达式不支持。 + +/////////////////////////////////// +// AngularJS 指令 + + +//AngularJS 指令使用前缀 ng- 扩展 HTML 属性。 +//ng-app 指令初始化一个 AngularJS 应用。 +//ng-init 指令初始化应用的数据。 +//ng-model 指令将 HTML 控件 (input, select, textarea) 的值绑定到应用的数据上。 +<div ng-app="" ng-init="firstName='John'"> + <p>Name: <input type="text" ng-model="firstName"></p> + <p>You wrote: {{ firstName }}</p> +</div> + +//使用 ng-init 并不常见。你将在有关控制器的章节中学习如何初始化数据。 + +//ng-repeat 指令会重复一个 HTML 元素: +<div ng-app="" ng-init="names=['Jani','Hege','Kai']"> + <ul> + <li ng-repeat="x in names"> + {{ x }} + </li> + </ul> +</div> + +//ng-repeat 指令用在一个对象数组上: +<div ng-app="" ng-init="names=[ +{name:'Jani',country:'Norway'}, +{name:'Hege',country:'Sweden'}, +{name:'Kai',country:'Denmark'}]"> + <ul> + <li ng-repeat="x in names"> + {{ x.name + ', ' + x.country }} + </li> + </ul> +</div> + +// AngularJS 最适合用于数据库 CRUD (Create Read Update Delete) 的应用。 +// 只需设想这些对象都是来自一个数据库的记录。 + +// ng-app 指令定义一个 AngularJS 应用的根元素。 +// ng-app 指令将在页面加载后自动启动(自动初始化)应用。 +// 稍后你将学习如何为 ng-app 设置一个值(如 ng-app="myModule"), 来连接代码模块。 + +// ng-init 指令为一个 AngularJS 应用定义初始值。 +// 通常,你不太使用 ng-init。你会转而使用一个控制器或模块。 +// 你将在稍后学到更多有关控制器和模块的内容。 + +//ng-model 指令将 HTML 控件 (input, select, textarea) 的值绑定到应用的数据上。 +//ng-model 指令还能: +//为应用的数据提供类型验证 (number, email, required)。 +//为应用的数据提供状态信息 (invalid, dirty, touched, error)。 +//为 HTML 元素提供 CSS 类。 +//将 HTML 元素绑定到 HTML 表单。 + +//ng-repeat 指令为集合(一个数组)中的每个元素克隆出 HTML 元素。 + +/////////////////////////////////// +// AngularJS 控制器 + +// AngularJS 控制器控制 AngularJS 应用中的数据。 +// AngularJS 控制器就是常规的 JavaScript 对象。 + +// AngularJS 应用由控制器控制。 +// ng-controller 指令定义应用的控制器。 +// 一个控制器就是一个 JavaScript 对象, 通过标准的 JavaScript 对象构建器创建。 + +<div ng-app="myApp" ng-controller="myCtrl"> + +First Name: <input type="text" ng-model="firstName"><br> +Last Name: <input type="text" ng-model="lastName"><br> +<br> +Full Name: {{firstName + " " + lastName}} + +</div> + +<script> +var app = angular.module('myApp', []); +app.controller('myCtrl', function($scope) { + $scope.firstName = "John"; + $scope.lastName = "Doe"; +}); +</script> + +//应用的解析: + +//AngularJS 应用通过 ng-app="myApp" 定义。该应用运行在 <div> 内。 +//ng-controller="myCtrl" 属性是一个 AngularJS 指令。它定义了一个控制器。 +//myCtrl 函数是一个 JavaScript 函数。 +//AngularJS 将使用一个 $scope 对象来调用控制器。 +//AngularJS 中, $scope 就是该应用对象(应用的变量和函数的所有者)。 +//该控制器在 $scope 内创建了两个属性(即变量 firstName 和 lastName)。 +//ng-model 指令将输入表单项绑定到控制器的属性上(firstName 和 lastName)。 + +//以上的例子演示了一个包含有两个属性 lastName 和 firstName 的控制器。 +//一个控制器也可以有方法(函数的变量): +<div ng-app="myApp" ng-controller="personCtrl"> + +First Name: <input type="text" ng-model="firstName"><br> +Last Name: <input type="text" ng-model="lastName"><br> +<br> +Full Name: {{fullName()}} + +</div> + +<script> +var app = angular.module('myApp', []); +app.controller('personCtrl', function($scope) { + $scope.firstName = "John"; + $scope.lastName = "Doe"; + $scope.fullName = function() { + return $scope.firstName + " " + $scope.lastName; + } +}); +</script> + +//在较大型的应用中, 通常是将控制器代码保存在外部文件中。 +//只需将 <script> </script> 标签之间的代码复制到一个名为 personController.js 的外部文件中: + +<div ng-app="myApp" ng-controller="personCtrl"> + +First Name: <input type="text" ng-model="firstName"><br> +Last Name: <input type="text" ng-model="lastName"><br> +<br> +Full Name: {{firstName + " " + lastName}} + +</div> + +<script src="personController.js"></script> + +// 为方便下个例子使用,我们将创建一个新的控制器文件: +angular.module('myApp', []).controller('namesCtrl', function($scope) { + $scope.names = [ + {name:'Jani',country:'Norway'}, + {name:'Hege',country:'Sweden'}, + {name:'Kai',country:'Denmark'} + ]; +}); + +//将文件保存为 namesController.js: +//然后在一个应用中使用该控制器: + +<div ng-app="myApp" ng-controller="namesCtrl"> + +<ul> + <li ng-repeat="x in names"> + {{ x.name + ', ' + x.country }} + </li> +</ul> + +</div> + +<script src="namesController.js"></script> + +/////////////////////////////////// +// AngularJS 过滤器 + +// 过滤器可以通过一个管道符添加到表达式和指令上。 +// AngularJS 过滤器能用来转换数据: + +- **currency**: 将一个数字格式化成货币格式。 +- **filter**: 从一个数组中选择一组子集元素。 +- **lowercase**: 将一个字符串格式化成小写形式。 +- **orderBy**: 依据一个表达式排序一个数组。 +- **upper**: 将一个字符串格式化成大写形式。 + +//一个过滤器可以通过一个管道符 (|) 及一个过滤器表达式添加到一个表达式上。 +//(在下面的两个例子中,我们将使用前一章中的 person 控制器) +//uppercase 过滤器将字符串格式化成大写格式: +<div ng-app="myApp" ng-controller="personCtrl"> + +<p>The name is {{ lastName | uppercase }}</p> + +</div> + +//lowercase 过滤器将字符串格式化成小写格式: +<div ng-app="myApp" ng-controller="personCtrl"> + +<p>The name is {{ lastName | lowercase }}</p> + +</div> + +//currency 过滤器将一个数字格式化成货币格式: +<div ng-app="myApp" ng-controller="costCtrl"> + +<input type="number" ng-model="quantity"> +<input type="number" ng-model="price"> + +<p>Total = {{ (quantity * price) | currency }}</p> + +</div> + +//一个过滤器可以通过一个管道符 (|) 及一个过滤器表达式添加到一个指令上。 +//orderBy 过滤器根据一个表达式排序一个数组: +<div ng-app="myApp" ng-controller="namesCtrl"> + +<ul> + <li ng-repeat="x in names | orderBy:'country'"> + {{ x.name + ', ' + x.country }} + </li> +</ul> + +<div> + +//一个输入框过滤器可以通过一个管道符 (|) +//以及后跟一个冒号和模式名的 filter 添加到一个指令上。 +//该过滤器从一个数组中选择一个子集: + +<div ng-app="myApp" ng-controller="namesCtrl"> + +<p><input type="text" ng-model="test"></p> + +<ul> + <li ng-repeat="x in names | filter:test | orderBy:'country'"> + {{ (x.name | uppercase) + ', ' + x.country }} + </li> +</ul> + +</div> + +/////////////////////////////////// +// AngularJS AJAX - $http + +//$http 是一个从远程服务器读取数据的 AngularJS 服务。 + +// 以下数据可由一个 web 服务器提供: +// http://www.w3schools.com/angular/customers.php +// **访问 URL 来查看数据格式** + +// AngularJS $http 是一个从 web 服务器上读取数据的核心服务。 +// $http.get(url) 这个函数用来读取服务器数据。 +<div ng-app="myApp" ng-controller="customersCtrl"> + +<ul> + <li ng-repeat="x in names"> + {{ x.Name + ', ' + x.Country }} + </li> +</ul> + +</div> + +<script> +var app = angular.module('myApp', []); +app.controller('customersCtrl', function($scope, $http) { + $http.get("http://www.w3schools.com/angular/customers.php") + .success(function(response) {$scope.names = response.records;}); +}); +</script> + +// 应用解析: + +// AngularJS 应用由 ng-app 定义。该应用运行在一个 <div> 中。 +// ng-controller 指令命名控制器对象。 +// customersCtrl 函数是一个标准的 JavaScript 对象构造器。 +// AngularJS 会使用一个 $scope 和 $http 对象来调用 customersCtrl。 +// $scope 就是该应用对象(应用的变量和函数的所有者)。 +// $http 是一个用于请求外部数据的 XMLHttpRequest 对象。 +// $http.get() 从 http://www.w3schools.com/angular/customers.php 读取 JSON 数据。 +// 如果成功, 该控制器会根据来自服务器的 JSON 数据,在 $scope 中创建一个属性 (names)。 + + +// 向不同的服务器(不同于请求页)请求数据,称作跨站 HTTP 请求。 +// 跨站请求在网站上很普遍。许多网页会从不同的服务器加载 CSS,图片和脚本。 +// 在现代浏览器中,基于安全原因,从脚本内进行跨站 HTTP 请求是被禁止的。 +// 下面的这行代码,已被加入到我们的 PHP 例子中,以便允许跨站访问。 +header("Access-Control-Allow-Origin: *"); + + +/////////////////////////////////// +// AngularJS 表格 + +// 使用 angular 显示表格非常简单: +<div ng-app="myApp" ng-controller="customersCtrl"> + +<table> + <tr ng-repeat="x in names"> + <td>{{ x.Name }}</td> + <td>{{ x.Country }}</td> + </tr> +</table> + +</div> + +<script> +var app = angular.module('myApp', []); +app.controller('customersCtrl', function($scope, $http) { + $http.get("http://www.w3schools.com/angular/customers.php") + .success(function (response) {$scope.names = response.records;}); +}); +</script> + +// 要排序表格,添加一个 orderBy 过滤器: +<table> + <tr ng-repeat="x in names | orderBy : 'Country'"> + <td>{{ x.Name }}</td> + <td>{{ x.Country }}</td> + </tr> +</table> + +// 要显示表格索引值,添加一个带有 $index 的 <td>: +<table> + <tr ng-repeat="x in names"> + <td>{{ $index + 1 }}</td> + <td>{{ x.Name }}</td> + <td>{{ x.Country }}</td> + </tr> +</table> + +// 使用 $even 和 $odd +<table> + <tr ng-repeat="x in names"> + <td ng-if="$odd" style="background-color:#f1f1f1">{{ x.Name }}</td> + <td ng-if="$even">{{ x.Name }}</td> + <td ng-if="$odd" style="background-color:#f1f1f1">{{ x.Country }}</td> + <td ng-if="$even">{{ x.Country }}</td> + </tr> +</table> + +/////////////////////////////////// +// AngularJS HTML DOM + +//AngularJS 有用于将应用的数据绑定到 HTML DOM 元素属性的指令。 + +// ng-disabled 指令将 AngularJS 应用的数据绑定到 HTML 元素的 disabled 属性上。 + +<div ng-app="" ng-init="mySwitch=true"> + +<p> +<button ng-disabled="mySwitch">Click Me!</button> +</p> + +<p> +<input type="checkbox" ng-model="mySwitch">Button +</p> + +</div> + +//应用解析: + +// ng-disabled 指令将应用的 mySwitch 数据绑定到 HTML 按钮的 disabled 属性上。 +// ng-model 指令将 HTML checkbox 元素的值绑定到 mySwitch 的值上。 +// 如果 mySwitch 的值求值为 true,则该按钮将被禁用: +<p> +<button disabled>Click Me!</button> +</p> + +// 如果 mySwitch 的值求值为 false,则该按钮将不会被禁用: +<p> + <button>Click Me!</button> +</p> + +// ng-show 指令显示或隐藏一个 HTML 元素。 + +<div ng-app=""> + +<p ng-show="true">I am visible.</p> + +<p ng-show="false">I am not visible.</p> + +</div> + +// ng-show 指令基于 ng-show 的值显示(或隐藏)一个 HTML 元素。 +// 你可以使用任何能求值成 true 或 false 的表达式: +<div ng-app=""> +<p ng-show="hour > 12">I am visible.</p> +</div> + +/////////////////////////////////// +// AngularJS 事件 + +// AngularJS 有它自己的 HTML 事件指令。 + +// ng-click 指令定义一个 AngularJS 点击事件。 +<div ng-app="myApp" ng-controller="myCtrl"> + +<button ng-click="count = count + 1">Click me!</button> + +<p>{{ count }}</p> + +</div> +<script> +var app = angular.module('myApp', []); +app.controller('myCtrl', function($scope) { + $scope.count = 0; +}); +</script> + +// ng-hide 指令可用于设置一个应用的部分区域的可见性。 +// 值 ng-hide="true" 使得一个 HTML 元素不可见。 +// 值 ng-hide="false" 使得一个 HTML 元素可见。 +<div ng-app="myApp" ng-controller="personCtrl"> + +<button ng-click="toggle()">Toggle</button> + +<p ng-hide="myVar"> +First Name: <input type="text" ng-model="firstName"><br> +Last Name: <input type="text" ng-model="lastName"><br> +<br> +Full Name: {{firstName + " " + lastName}} +</p> + +</div> + +<script> +var app = angular.module('myApp', []); +app.controller('personCtrl', function($scope) { + $scope.firstName = "John", + $scope.lastName = "Doe" + $scope.myVar = false; + $scope.toggle = function() { + $scope.myVar = !$scope.myVar; + }; +}); +</script> + +//应用解析: + +// personController 的第一部分和讲述控制器章节中的一样。 +// 该应用有一个默认属性(一个变量):$scope.myVar = false: +// ng-hide 指令依据 myVar 的值(true 或 false), +// 设置 <p> 元素的可见性,该元素含有两个输入框。 +// 函数 toggle() 将 myVar 在 true 和 false 间进行切换。 +// 值 ng-hide="true" 使得该元素不可见。 + + +// ng-show 指令也能用来设置一个应用的某部分的可见性。 +// 值 ng-show="false" 使得一个 HTML 元素不可见。 +// 值 ng-show="true" 使得一个 HTML 元素可见。 +// 这个例子与上面的一样,但用 ng-show 替代了 ng-hide: +<div ng-app="myApp" ng-controller="personCtrl"> + +<button ng-click="toggle()">Toggle</button> + +<p ng-show="myVar"> +First Name: <input type="text" ng-model="firstName"><br> +Last Name: <input type="text" ng-model="lastName"><br> +<br> +Full Name: {{firstName + " " + lastName}} +</p> + +</div> + +<script> +var app = angular.module('myApp', []); +app.controller('personCtrl', function($scope) { + $scope.firstName = "John", + $scope.lastName = "Doe" + $scope.myVar = true; + $scope.toggle = function() { + $scope.myVar = !$scope.myVar; + } +}); +</script> + +/////////////////////////////////// +// AngularJS 模块 + +// 一个 AngularJS 模块定义一个应用。 +// 模块是一个应用的不同部分所在的一个容器。 +// 模块是应用控制器的一个容器。 +// 控制器总是隶属于一个模块。 + +// 这个应用 ("myApp") 有一个控制器 ("myCtrl"): + +<!DOCTYPE html> +<html> +<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> +<body> + +<div ng-app="myApp" ng-controller="myCtrl"> +{{ firstName + " " + lastName }} +</div> + +<script> +var app = angular.module("myApp", []); +app.controller("myCtrl", function($scope) { + $scope.firstName = "John"; + $scope.lastName = "Doe"; +}); +</script> + +</body> +</html> + +// 在 AngularJS 应用中通常将模块和控制器放置在 JavaScript 文件中。 +// 在本例中,"myApp.js" 包含了一个应用模块的定义,而 "myCtrl.js" 包含了控制器: + +<!DOCTYPE html> +<html> +<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> +<body> + +<div ng-app="myApp" ng-controller="myCtrl"> +{{ firstName + " " + lastName }} +</div> + +<script src="myApp.js"></script> +<script src="myCtrl.js"></script> + +</body> +</html> + +//myApp.js +var app = angular.module("myApp", []); + +// 模块定义中的 [] 参数可用来定义依赖的模块。 + +// myCtrl.js +app.controller("myCtrl", function($scope) { + $scope.firstName = "John"; + $scope.lastName= "Doe"; +}); + +// JavaScript 中应该避免使用全局函数。它们会非常容易地被覆盖 +// 或被其它脚本破坏。 + +// AngularJS 脚本通过将所有函数保存在模块内,缓解了这种问题。 + +// 虽然 HTML 应用中通常是将脚本放置在 +// <body> 元素的末尾,但还是推荐你要么在 +// <head> 中要么在 <body> 的开头处加载 AngularJS 库。 + +// 这是因为对 angular.module 的调用只有在库被加载后才能被编译。 + +<!DOCTYPE html> +<html> +<body> +<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> + +<div ng-app="myApp" ng-controller="myCtrl"> +{{ firstName + " " + lastName }} +</div> + +<script> +var app = angular.module("myApp", []); +app.controller("myCtrl", function($scope) { + $scope.firstName = "John"; + $scope.lastName = "Doe"; +}); +</script> + +</body> +</html> + + +/////////////////////////////////// +// AngularJS 应用 + +// AngularJS 模块定义 AngularJS 应用。 +// AngularJS 控制器控制 AngularJS 应用。 +// ng-app 指令定义该应用,ng-controller 定义该控制器。 +<div ng-app="myApp" ng-controller="myCtrl"> + First Name: <input type="text" ng-model="firstName"><br> + Last Name: <input type="text" ng-model="lastName"><br> + <br> + Full Name: {{firstName + " " + lastName}} +</div> +<script> + var app = angular.module('myApp', []); + app.controller('myCtrl', function($scope) { + $scope.firstName= "John"; + $scope.lastName= "Doe"; + }); +</script> + +// AngularJS 模块定义应用: +var app = angular.module('myApp', []); + +// AngularJS 控制器控制应用: +app.controller('myCtrl', function($scope) { + $scope.firstName= "John"; + $scope.lastName= "Doe"; +}); +``` + +## 来源 & 参考 + +**例子** + +- http://www.w3schools.com/angular/angular_examples.asp + +**参考** + +- http://www.w3schools.com/angular/angular_ref_directives.asp +- http://www.w3schools.com/angular/default.asp diff --git a/zh-cn/bash-cn.html.markdown b/zh-cn/bash-cn.html.markdown index 6afa659a..d85e5b8f 100644 --- a/zh-cn/bash-cn.html.markdown +++ b/zh-cn/bash-cn.html.markdown @@ -5,7 +5,14 @@ contributors: - ["Max Yankov", "https://github.com/golergka"] - ["Darren Lin", "https://github.com/CogBear"] - ["Alexandre Medeiros", "http://alemedeiros.sdf.org"] + - ["Denis Arh", "https://github.com/darh"] + - ["akirahirose", "https://twitter.com/akirahirose"] + - ["Anton Strömkvist", "http://lutic.org/"] + - ["Rahil Momin", "https://github.com/iamrahil"] + - ["Gregrory Kielian", "https://github.com/gskielian"] + - ["Etan Reisner", "https://github.com/deryni"] translators: + - ["Jinchang Ye", "https://github.com/Alwayswithme"] - ["Chunyang Xu", "https://github.com/XuChunyang"] filename: LearnBash-cn.sh lang: zh-cn @@ -23,31 +30,45 @@ Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 Mac OS X 下的 # 如你所见,注释以 # 开头,shebang 也是注释。 # 显示 “Hello world!” -echo Hello, world! +echo Hello world! # 每一句指令以换行或分号隔开: echo 'This is the first line'; echo 'This is the second line' # 声明一个变量: -VARIABLE="Some string" +Variable="Some string" # 下面是错误的做法: -VARIABLE = "Some string" -# Bash 会把 VARIABLE 当做一个指令,由于找不到该指令,因此这里会报错。 +Variable = "Some string" +# Bash 会把 Variable 当做一个指令,由于找不到该指令,因此这里会报错。 +# 也不可以这样: +Variable= 'Some string' +# Bash 会认为 'Some string' 是一条指令,由于找不到该指令,这里再次报错。 +# (这个例子中 'Variable=' 这部分会被当作仅对 'Some string' 起作用的赋值。) # 使用变量: -echo $VARIABLE -echo "$VARIABLE" -echo '$VARIABLE' +echo $Variable +echo "$Variable" +echo '$Variable' # 当你赋值 (assign) 、导出 (export),或者以其他方式使用变量时,变量名前不加 $。 # 如果要使用变量的值, 则要加 $。 # 注意: ' (单引号) 不会展开变量(即会屏蔽掉变量)。 # 在变量内部进行字符串代换 -echo ${VARIABLE/Some/A} -# 会把 VARIABLE 中首次出现的 "some" 替换成 “A”。 +echo ${Variable/Some/A} +# 会把 Variable 中首次出现的 "some" 替换成 “A”。 + +# 变量的截取 +Length=7 +echo ${Variable:0:Length} +# 这样会仅返回变量值的前7个字符 + +# 变量的默认值 +echo ${Foo:-"DefaultValueIfFooIsMissingOrEmpty"} +# 对 null (Foo=) 和空串 (Foo="") 起作用; 零(Foo=0)时返回0 +# 注意这仅返回默认值而不是改变变量的值 # 内置变量: # 下面的内置变量很有用 @@ -55,26 +76,37 @@ echo "Last program return value: $?" echo "Script's PID: $$" echo "Number of arguments: $#" echo "Scripts arguments: $@" -echo "Scripts arguments separeted in different variables: $1 $2..." +echo "Scripts arguments separated in different variables: $1 $2..." # 读取输入: echo "What's your name?" -read NAME # 这里不需要声明新变量 -echo Hello, $NAME! +read Name # 这里不需要声明新变量 +echo Hello, $Name! # 通常的 if 结构看起来像这样: # 'man test' 可查看更多的信息 -if [ $NAME -ne $USER ] +if [ $Name -ne $USER ] then - echo "Your name is you username" + echo "Your name isn't your username" else - echo "Your name isn't you username" + echo "Your name is your username" fi # 根据上一个指令执行结果决定是否执行下一个指令 -echo "Always executed" || echo "Only executed if first command fail" +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 )) @@ -88,18 +120,54 @@ ls -l # 列出文件和目录的详细信息 # 用下面的指令列出当前目录下所有的 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 + # 重定向可以到输出,输入和错误输出。 -python2 hello.py < "input.in" -python2 hello.py > "output.out" -python2 hello.py 2> "error.err" +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 +case "$Variable" in # 列出需要匹配的字符串 0) echo "There is a zero.";; 1) echo "There is a one.";; @@ -107,11 +175,37 @@ case "$VARIABLE" in esac # 循环遍历给定的参数序列: -# 变量$VARIABLE 的值会被打印 3 次。 -# 注意 ` ` 和 $( ) 等价。seq 返回长度为 3 的数组。 -for VARIABLE in `seq 3` +# 变量$Variable 的值会被打印 3 次。 +for Variable in {1..3} +do + echo "$Variable" +done + +# 或传统的 “for循环” : +for ((a=1; a <= 3; a++)) do - echo "$VARIABLE" + 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 # 你也可以使用函数 @@ -132,17 +226,52 @@ bar () } # 调用函数 -foo "My name is" $NAME +foo "My name is" $Name # 有很多有用的指令需要学习: -tail -n 10 file.txt # 打印 file.txt 的最后 10 行 -head -n 10 file.txt +tail -n 10 file.txt # 打印 file.txt 的前 10 行 -sort file.txt +head -n 10 file.txt # 将 file.txt 按行排序 -uniq -d file.txt +sort file.txt # 报告或忽略重复的行,用选项 -d 打印重复的行 -cut -d ',' -f 1 file.txt +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 . + +# 用 man 指令阅读相关的 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 ``` diff --git a/zh-cn/brainfuck-cn.html.markdown b/zh-cn/bf-cn.html.markdown index a6f3fa09..2d2a114a 100644 --- a/zh-cn/brainfuck-cn.html.markdown +++ b/zh-cn/bf-cn.html.markdown @@ -1,11 +1,13 @@ --- -language: brainfuck -lang: zh-cn +language: bf +filename: brainfuck-cn.bf contributors: - ["Prajit Ramachandran", "http://prajitr.github.io/"] - ["Mathias Bynens", "http://mathiasbynens.be/"] translators: - ["lyuehh", "https://github.com/lyuehh"] +lang: zh-cn + --- Brainfuck 是一个极小的只有8个指令的图灵完全的编程语言。 diff --git a/zh-cn/c++-cn.html.markdown b/zh-cn/c++-cn.html.markdown new file mode 100644 index 00000000..87951bc3 --- /dev/null +++ b/zh-cn/c++-cn.html.markdown @@ -0,0 +1,572 @@ +---
+language: c++
+filename: learncpp-cn.cpp
+contributors:
+ - ["Steven Basart", "http://github.com/xksteven"]
+ - ["Matt Kline", "https://github.com/mrkline"]
+translators:
+ - ["Arnie97", "https://github.com/Arnie97"]
+lang: zh-cn
+---
+
+C++是一种系统编程语言。用它的发明者,
+[Bjarne Stroustrup的话](http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote)来说,C++的设计目标是:
+
+- 成为“更好的C语言”
+- 支持数据的抽象与封装
+- 支持面向对象编程
+- 支持泛型编程
+
+C++提供了对硬件的紧密控制(正如C语言一样),
+能够编译为机器语言,由处理器直接执行。
+与此同时,它也提供了泛型、异常和类等高层功能。
+虽然C++的语法可能比某些出现较晚的语言更复杂,它仍然得到了人们的青睞——
+功能与速度的平衡使C++成为了目前应用最广泛的系统编程语言之一。
+
+```c++
+////////////////
+// 与C语言的比较
+////////////////
+
+// C++_几乎_是C语言的一个超集,它与C语言的基本语法有许多相同之处,
+// 例如变量和函数的声明,原生数据类型等等。
+
+// 和C语言一样,在C++中,你的程序会从main()开始执行,
+// 该函数的返回值应当为int型,这个返回值会作为程序的退出状态值。
+// 不过,大多数的编译器(gcc,clang等)也接受 void main() 的函数原型。
+// (参见 http://en.wikipedia.org/wiki/Exit_status 来获取更多信息)
+int main(int argc, char** argv)
+{
+ // 和C语言一样,命令行参数通过argc和argv传递。
+ // argc代表命令行参数的数量,
+ // 而argv是一个包含“C语言风格字符串”(char *)的数组,
+ // 其中每个字符串代表一个命令行参数的内容,
+ // 首个命令行参数是调用该程序时所使用的名称。
+ // 如果你不关心命令行参数的值,argc和argv可以被忽略。
+ // 此时,你可以用int main()作为函数原型。
+
+ // 退出状态值为0时,表示程序执行成功
+ return 0;
+}
+
+// 然而,C++和C语言也有一些区别:
+
+// 在C++中,字符字面量的大小是一个字节。
+sizeof('c') == 1
+
+// 在C语言中,字符字面量的大小与int相同。
+sizeof('c') == sizeof(10)
+
+
+// C++的函数原型与函数定义是严格匹配的
+void func(); // 这个函数不能接受任何参数
+
+// 而在C语言中
+void func(); // 这个函数能接受任意数量的参数
+
+// 在C++中,用nullptr代替C语言中的NULL
+int* ip = nullptr;
+
+// C++也可以使用C语言的标准头文件,
+// 但是需要加上前缀“c”并去掉末尾的“.h”。
+#include <cstdio>
+
+int main()
+{
+ printf("Hello, world!\n");
+ return 0;
+}
+
+///////////
+// 函数重载
+///////////
+
+// C++支持函数重载,你可以定义一组名称相同而参数不同的函数。
+
+void print(char const* myString)
+{
+ printf("String %s\n", myString);
+}
+
+void print(int myInt)
+{
+ printf("My int is %d", myInt);
+}
+
+int main()
+{
+ print("Hello"); // 解析为 void print(const char*)
+ print(15); // 解析为 void print(int)
+}
+
+///////////////////
+// 函数参数的默认值
+///////////////////
+
+// 你可以为函数的参数指定默认值,
+// 它们将会在调用者没有提供相应参数时被使用。
+
+void doSomethingWithInts(int a = 1, int b = 4)
+{
+ // 对两个参数进行一些操作
+}
+
+int main()
+{
+ doSomethingWithInts(); // a = 1, b = 4
+ doSomethingWithInts(20); // a = 20, b = 4
+ doSomethingWithInts(20, 5); // a = 20, b = 5
+}
+
+// 默认参数必须放在所有的常规参数之后。
+
+void invalidDeclaration(int a = 1, int b) // 这是错误的!
+{
+}
+
+
+///////////
+// 命名空间
+///////////
+
+// 命名空间为变量、函数和其他声明提供了分离的的作用域。
+// 命名空间可以嵌套使用。
+
+namespace First {
+ namespace Nested {
+ void foo()
+ {
+ printf("This is First::Nested::foo\n");
+ }
+ } // 结束嵌套的命名空间Nested
+} // 结束命名空间First
+
+namespace Second {
+ void foo()
+ {
+ printf("This is Second::foo\n")
+ }
+}
+
+void foo()
+{
+ printf("This is global foo\n");
+}
+
+int main()
+{
+ // 如果没有特别指定,就从“Second”中取得所需的内容。
+ using namespace Second;
+
+ foo(); // 显示“This is Second::foo”
+ First::Nested::foo(); // 显示“This is First::Nested::foo”
+ ::foo(); // 显示“This is global foo”
+}
+
+////////////
+// 输入/输出
+////////////
+
+// C++使用“流”来输入输出。<<是流的插入运算符,>>是流提取运算符。
+// cin、cout、和cerr分别代表
+// stdin(标准输入)、stdout(标准输出)和stderr(标准错误)。
+
+#include <iostream> // 引入包含输入/输出流的头文件
+
+using namespace std; // 输入输出流在std命名空间(也就是标准库)中。
+
+int main()
+{
+ int myInt;
+
+ // 在标准输出(终端/显示器)中显示
+ cout << "Enter your favorite number:\n";
+ // 从标准输入(键盘)获得一个值
+ cin >> myInt;
+
+ // cout也提供了格式化功能
+ cout << "Your favorite number is " << myInt << "\n";
+ // 显示“Your favorite number is <myInt>”
+
+ cerr << "Used for error messages";
+}
+
+/////////
+// 字符串
+/////////
+
+// C++中的字符串是对象,它们有很多成员函数
+#include <string>
+
+using namespace std; // 字符串也在std命名空间(标准库)中。
+
+string myString = "Hello";
+string myOtherString = " World";
+
+// + 可以用于连接字符串。
+cout << myString + myOtherString; // "Hello World"
+
+cout << myString + " You"; // "Hello You"
+
+// C++中的字符串是可变的,具有“值语义”。
+myString.append(" Dog");
+cout << myString; // "Hello Dog"
+
+
+/////////////
+// 引用
+/////////////
+
+// 除了支持C语言中的指针类型以外,C++还提供了_引用_。
+// 引用是一种特殊的指针类型,一旦被定义就不能重新赋值,并且不能被设置为空值。
+// 使用引用时的语法与原变量相同:
+// 也就是说,对引用类型进行解引用时,不需要使用*;
+// 赋值时也不需要用&来取地址。
+
+using namespace std;
+
+string foo = "I am foo";
+string bar = "I am bar";
+
+
+string& fooRef = foo; // 建立了一个对foo的引用。
+fooRef += ". Hi!"; // 通过引用来修改foo的值
+cout << fooRef; // "I am foo. Hi!"
+
+// 这句话的并不会改变fooRef的指向,其效果与“foo = bar”相同。
+// 也就是说,在执行这条语句之后,foo == "I am bar"。
+fooRef = bar;
+
+const string& barRef = bar; // 建立指向bar的常量引用。
+// 和C语言中一样,(指针和引用)声明为常量时,对应的值不能被修改。
+barRef += ". Hi!"; // 这是错误的,不能修改一个常量引用的值。
+
+///////////////////
+// 类与面向对象编程
+///////////////////
+
+// 有关类的第一个示例
+#include <iostream>
+
+// 声明一个类。
+// 类通常在头文件(.h或.hpp)中声明。
+class Dog {
+ // 成员变量和成员函数默认情况下是私有(private)的。
+ std::string name;
+ int weight;
+
+// 在这个标签之后,所有声明都是公有(public)的,
+// 直到重新指定“private:”(私有继承)或“protected:”(保护继承)为止
+public:
+
+ // 默认的构造器
+ Dog();
+
+ // 这里是成员函数声明的一个例子。
+ // 可以注意到,我们在此处使用了std::string,而不是using namespace std
+ // 语句using namespace绝不应当出现在头文件当中。
+ void setName(const std::string& dogsName);
+
+ void setWeight(int dogsWeight);
+
+ // 如果一个函数不对对象的状态进行修改,
+ // 应当在声明中加上const。
+ // 这样,你就可以对一个以常量方式引用的对象执行该操作。
+ // 同时可以注意到,当父类的成员函数需要被子类重写时,
+ // 父类中的函数必须被显式声明为_虚函数(virtual)_。
+ // 考虑到性能方面的因素,函数默认情况下不会被声明为虚函数。
+ virtual void print() const;
+
+ // 函数也可以在class body内部定义。
+ // 这样定义的函数会自动成为内联函数。
+ void bark() const { std::cout << name << " barks!\n" }
+
+ // 除了构造器以外,C++还提供了析构器。
+ // 当一个对象被删除或者脱离其定义域时,它的析构函数会被调用。
+ // 这使得RAII这样的强大范式(参见下文)成为可能。
+ // 为了衍生出子类来,基类的析构函数必须定义为虚函数。
+ virtual ~Dog();
+
+}; // 在类的定义之后,要加一个分号
+
+// 类的成员函数通常在.cpp文件中实现。
+void Dog::Dog()
+{
+ std::cout << "A dog has been constructed\n";
+}
+
+// 对象(例如字符串)应当以引用的形式传递,
+// 对于不需要修改的对象,最好使用常量引用。
+void Dog::setName(const std::string& dogsName)
+{
+ name = dogsName;
+}
+
+void Dog::setWeight(int dogsWeight)
+{
+ weight = dogsWeight;
+}
+
+// 虚函数的virtual关键字只需要在声明时使用,不需要在定义时重复
+void Dog::print() const
+{
+ std::cout << "Dog is " << name << " and weighs " << weight << "kg\n";
+}
+
+void Dog::~Dog()
+{
+ std::cout << "Goodbye " << name << "\n";
+}
+
+int main() {
+ Dog myDog; // 此时显示“A dog has been constructed”
+ myDog.setName("Barkley");
+ myDog.setWeight(10);
+ myDog.print(); // 显示“Dog is Barkley and weighs 10 kg”
+ return 0;
+} // 显示“Goodbye Barkley”
+
+// 继承:
+
+// 这个类继承了Dog类中的公有(public)和保护(protected)对象
+class OwnedDog : public Dog {
+
+ void setOwner(const std::string& dogsOwner)
+
+ // 重写OwnedDogs类的print方法。
+ // 如果你不熟悉子类多态的话,可以参考这个页面中的概述:
+ // http://zh.wikipedia.org/wiki/%E5%AD%90%E7%B1%BB%E5%9E%8B
+
+ // override关键字是可选的,它确保你所重写的是基类中的方法。
+ void print() const override;
+
+private:
+ std::string owner;
+};
+
+// 与此同时,在对应的.cpp文件里:
+
+void OwnedDog::setOwner(const std::string& dogsOwner)
+{
+ owner = dogsOwner;
+}
+
+void OwnedDog::print() const
+{
+ Dog::print(); // 调用基类Dog中的print方法
+ // "Dog is <name> and weights <weight>"
+
+ std::cout << "Dog is owned by " << owner << "\n";
+ // "Dog is owned by <owner>"
+}
+
+/////////////////////
+// 初始化与运算符重载
+/////////////////////
+
+// 在C++中,通过定义一些特殊名称的函数,
+// 你可以重载+、-、*、/等运算符的行为。
+// 当运算符被使用时,这些特殊函数会被调用,从而实现运算符重载。
+
+#include <iostream>
+using namespace std;
+
+class Point {
+public:
+ // 可以以这样的方式为成员变量设置默认值。
+ double x = 0;
+ double y = 0;
+
+ // 定义一个默认的构造器。
+ // 除了将Point初始化为(0, 0)以外,这个函数什么都不做。
+ Point() { };
+
+ // 下面使用的语法称为初始化列表,
+ // 这是初始化类中成员变量的正确方式。
+ Point (double a, double b) :
+ x(a),
+ y(b)
+ { /* 除了初始化成员变量外,什么都不做 */ }
+
+ // 重载 + 运算符
+ Point operator+(const Point& rhs) const;
+
+ // 重载 += 运算符
+ Point& operator+=(const Point& rhs);
+
+ // 增加 - 和 -= 运算符也是有意义的,但这里不再赘述。
+};
+
+Point Point::operator+(const Point& rhs) const
+{
+ // 创建一个新的点,
+ // 其横纵坐标分别为这个点与另一点在对应方向上的坐标之和。
+ return Point(x + rhs.x, y + rhs.y);
+}
+
+Point& Point::operator+=(const Point& rhs)
+{
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+}
+
+int main () {
+ Point up (0,1);
+ Point right (1,0);
+ // 这里使用了Point类型的运算符“+”
+ // 调用up(Point类型)的“+”方法,并以right作为函数的参数
+ Point result = up + right;
+ // 显示“Result is upright (1,1)”
+ cout << "Result is upright (" << result.x << ',' << result.y << ")\n";
+ return 0;
+}
+
+///////////
+// 异常处理
+///////////
+
+// 标准库中提供了一些基本的异常类型
+// (参见http://en.cppreference.com/w/cpp/error/exception)
+// 但是,其他任何类型也可以作为一个异常被拋出
+#include <exception>
+
+// 在_try_代码块中拋出的异常可以被随后的_catch_捕获。
+try {
+ // 不要用 _new_关键字在堆上为异常分配空间。
+ throw std::exception("A problem occurred");
+}
+// 如果拋出的异常是一个对象,可以用常量引用来捕获它
+catch (const std::exception& ex)
+{
+ std::cout << ex.what();
+// 捕获尚未被_catch_处理的所有错误
+} catch (...)
+{
+ std::cout << "Unknown exception caught";
+ throw; // 重新拋出异常
+}
+
+///////
+// RAII
+///////
+
+// RAII指的是“资源获取就是初始化”(Resource Allocation Is Initialization),
+// 它被视作C++中最强大的编程范式之一。
+// 简单说来,它指的是,用构造函数来获取一个对象的资源,
+// 相应的,借助析构函数来释放对象的资源。
+
+// 为了理解这一范式的用处,让我们考虑某个函数使用文件句柄时的情况:
+void doSomethingWithAFile(const char* filename)
+{
+ // 首先,让我们假设一切都会顺利进行。
+
+ FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
+
+ doSomethingWithTheFile(fh);
+ doSomethingElseWithIt(fh);
+
+ fclose(fh); // 关闭文件句柄
+}
+
+// 不幸的是,随着错误处理机制的引入,事情会变得复杂。
+// 假设fopen函数有可能执行失败,
+// 而doSomethingWithTheFile和doSomethingElseWithIt会在失败时返回错误代码。
+// (虽然异常是C++中处理错误的推荐方式,
+// 但是某些程序员,尤其是有C语言背景的,并不认可异常捕获机制的作用)。
+// 现在,我们必须检查每个函数调用是否成功执行,并在问题发生的时候关闭文件句柄。
+bool doSomethingWithAFile(const char* filename)
+{
+ FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
+ if (fh == nullptr) // 当执行失败是,返回的指针是nullptr
+ return false; // 向调用者汇报错误
+
+ // 假设每个函数会在执行失败时返回false
+ if (!doSomethingWithTheFile(fh)) {
+ fclose(fh); // 关闭文件句柄,避免造成内存泄漏。
+ return false; // 反馈错误
+ }
+ if (!doSomethingElseWithIt(fh)) {
+ fclose(fh); // 关闭文件句柄
+ return false; // 反馈错误
+ }
+
+ fclose(fh); // 关闭文件句柄
+ return true; // 指示函数已成功执行
+}
+
+// C语言的程序员通常会借助goto语句简化上面的代码:
+bool doSomethingWithAFile(const char* filename)
+{
+ FILE* fh = fopen(filename, "r");
+ if (fh == nullptr)
+ return false;
+
+ if (!doSomethingWithTheFile(fh))
+ goto failure;
+
+ if (!doSomethingElseWithIt(fh))
+ goto failure;
+
+ fclose(fh); // 关闭文件
+ return true; // 执行成功
+
+failure:
+ fclose(fh);
+ return false; // 反馈错误
+}
+
+// 如果用异常捕获机制来指示错误的话,
+// 代码会变得清晰一些,但是仍然有优化的余地。
+void doSomethingWithAFile(const char* filename)
+{
+ FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
+ if (fh == nullptr)
+ throw std::exception("Could not open the file.");
+
+ try {
+ doSomethingWithTheFile(fh);
+ doSomethingElseWithIt(fh);
+ }
+ catch (...) {
+ fclose(fh); // 保证出错的时候文件被正确关闭
+ throw; // 之后,重新抛出这个异常
+ }
+
+ fclose(fh); // 关闭文件
+ // 所有工作顺利完成
+}
+
+// 相比之下,使用C++中的文件流类(fstream)时,
+// fstream会利用自己的析构器来关闭文件句柄。
+// 只要离开了某一对象的定义域,它的析构函数就会被自动调用。
+void doSomethingWithAFile(const std::string& filename)
+{
+ // ifstream是输入文件流(input file stream)的简称
+ std::ifstream fh(filename); // 打开一个文件
+
+ // 对文件进行一些操作
+ doSomethingWithTheFile(fh);
+ doSomethingElseWithIt(fh);
+
+} // 文件已经被析构器自动关闭
+
+// 与上面几种方式相比,这种方式有着_明显_的优势:
+// 1. 无论发生了什么情况,资源(此例当中是文件句柄)都会被正确关闭。
+// 只要你正确使用了析构器,就_不会_因为忘记关闭句柄,造成资源的泄漏。
+// 2. 可以注意到,通过这种方式写出来的代码十分简洁。
+// 析构器会在后台关闭文件句柄,不再需要你来操心这些琐事。
+// 3. 这种方式的代码具有异常安全性。
+// 无论在函数中的何处拋出异常,都不会阻碍对文件资源的释放。
+
+// 地道的C++代码应当把RAII的使用扩展到各种类型的资源上,包括:
+// - 用unique_ptr和shared_ptr管理的内存
+// - 各种数据容器,例如标准库中的链表、向量(容量自动扩展的数组)、散列表等;
+// 当它们脱离作用域时,析构器会自动释放其中储存的内容。
+// - 用lock_guard和unique_lock实现的互斥
+```
+扩展阅读:
+
+<http://cppreference.com/w/cpp> 提供了最新的语法参考。
+
+可以在 <http://cplusplus.com> 找到一些补充资料。
diff --git a/zh-cn/common-lisp-cn.html.markdown b/zh-cn/common-lisp-cn.html.markdown index b82829a9..b8979ed0 100644 --- a/zh-cn/common-lisp-cn.html.markdown +++ b/zh-cn/common-lisp-cn.html.markdown @@ -14,6 +14,8 @@ ANSI Common Lisp 是一个广泛通用于各个工业领域的、支持多种范 免费的经典的入门书籍[《实用 Common Lisp 编程》](http://www.gigamonkeys.com/book/) +许多人都抱怨上面这本书的翻译。[《ANSI Common Lisp》](http://acl.readthedocs.org/en/latest/)也许对中文读者更友好一些。 + 另外还有一本热门的近期出版的 [Land of Lisp](http://landoflisp.com/). @@ -287,7 +289,7 @@ nil ; 逻辑假,或者空列表 ;; (通过键)检索对应的值 (gethash 'a *m*) ; => 1, t -;; 注意此处有一细节:Common Lisp往往返回多个值。`gethash`返回的两个值是t,代表找到了这个元素;返回nil表示没有找到这个元素。 +;; 注意此处有一细节:Common Lisp往往返回多个值。`gethash`返回的第二个值是t,代表找到了这个元素;返回nil表示没有找到这个元素。 ;;(译者注:返回的第一个值表示给定的键所对应的值或者nil;) ;;(第二个是一个布尔值,表示在哈希表中是否存在这个给定的键) ;; 例如,如果可以找到给定的键所对应的值,则返回一个t,否则返回nil diff --git a/zh-cn/csharp-cn.html.markdown b/zh-cn/csharp-cn.html.markdown index a3cda5b3..971c1be9 100644 --- a/zh-cn/csharp-cn.html.markdown +++ b/zh-cn/csharp-cn.html.markdown @@ -232,7 +232,8 @@ on a new line! ""Wow!"", the masses cried"; // 三元表达式 // 简单的 if/else 语句可以写成: // <条件> ? <真> : <假> - string isTrue = (true) ? "True" : "False"; + int toCompare = 17; + string isTrue = toCompare == 17 ? "True" : "False"; // While 循环 int fooWhile = 0; diff --git a/zh-cn/dynamic-programming-cn.html.markdown b/zh-cn/dynamic-programming-cn.html.markdown new file mode 100644 index 00000000..b75c5404 --- /dev/null +++ b/zh-cn/dynamic-programming-cn.html.markdown @@ -0,0 +1,57 @@ +--- +category: Algorithms & Data Structures +name: Dynamic Programming +contributors: + - ["Akashdeep Goel", "http://github.com/akashdeepgoel"] +filename: dynamic-programming-cn.html.markdown +lang: zh-cn +translators: + - ["EtaoinWu", "https://github.com/EtaoinWu"] +--- + +# 动态规划 + +## 简介 + +动态规划是一种实用的技巧,它可以用来解决一系列特定问题。它的思路很简单,如果你对某个给定的输入解决了一个问题,那么你可以保存已有信息,以避免重复计算,节约计算时间。 + +记住,只有那些没有办法记住历史的才被迫做更多的苦力。(Fibonacci就是一个显然的例子) + +## 解决问题的方式 + +1. *自顶向下* : 利用分支策略分解问题。如果你已经解决过当前子问题了,那么就返回已有信息。如果当前子问题没有计算过,那么就对它进行计算。这样的方法很易于思考、很直观。这被称作“记忆化”。 + +2. *自底向上* : 首先分析问题,将问题分解为不同规模的问题,并决定它们的顺序,按顺序计算,直到解决给定规模的问题。这样的流程可以保证在解决较大的问题之前解决(它所依赖的)较小的问题。这种流程被称作“动态规划”。 + +## 动态规划的例子 + +最长上升子序列问题。给定`S= {a[1] , a[2] , a[3], a[4], ............., a[n-1], a[n] }`,求出一个子序列,使得对于所有在这个子序列中所有满足`j<i`的`j`与`i`,满足`aj<ai`。首先我们要讨论以原序列的第`i`个元素结尾的最长上升子序列`dp[i]`。那么答案是整个dp序列的最大值。考虑`dp[i]`,它的最后一个元素为`a[i]`。枚举它的倒数第二个元素`a[j]`,则`a[j]<a[i]`成立。则`dp[i]`就是所有这样的`dp[j]`的最大值加上1(最后一个元素)。这个算法具有*O(n^2)*的时间复杂度。 + +此算法的伪代码: + +```python +for i=0 to n-1 + dp[i]=0 + for j=0 to i-1 + if (a[i] > a[j] and dp[i]<dp[j]) + LS[i] = LS[j] + dp[i]=dp[i]+1 +for i=0 to n-1 + if (largest < dp[i]) + largest = dp[i] +``` + +这个算法的复杂度可以通过将数组换为其他数据结构来优化,来获得*O(n * log n)*的时间复杂度。 + +同样的思路可以求出有向无环图上的最大路径。 + +### 一些著名的动态规划问题及其实现 + +- Floyd Warshall 算法 - [教程与C实现源码](http://www.thelearningpoint.net/computer-science/algorithms-all-to-all-shortest-paths-in-graphs---floyd-warshall-algorithm-with-c-program-source-code) +- 整数背包问题 - [教程与C实现源码](http://www.thelearningpoint.net/computer-science/algorithms-dynamic-programming---the-integer-knapsack-problem) +- 最长公共子序列问题 - [教程与C实现源码](http://www.thelearningpoint.net/computer-science/algorithms-dynamic-programming---longest-common-subsequence) + +## 在线资源 + +* [codechef](https://www.codechef.com/wiki/tutorial-dynamic-programming) +* [洛谷](https://www.luogu.org/problem/lists?name=&orderitem=pid&tag=3) diff --git a/zh-cn/elisp-cn.html.markdown b/zh-cn/elisp-cn.html.markdown index 06f38d77..3f6ccbcf 100644 --- a/zh-cn/elisp-cn.html.markdown +++ b/zh-cn/elisp-cn.html.markdown @@ -293,7 +293,7 @@ lang: zh-cn (hello-to-bonjour)
-;; 给这些名字上个色:
+;; 给这些名字加粗:
(defun boldify-names ()
(switch-to-buffer-other-window "*test*")
@@ -340,5 +340,6 @@ lang: zh-cn ;; - Kevin Montuori
;; - Arne Babenhauserheide
;; - Alan Schmitt
+;; - spacegoing
```
diff --git a/zh-cn/go-cn.html.markdown b/zh-cn/go-cn.html.markdown index 9f6a8c15..37b4b137 100644 --- a/zh-cn/go-cn.html.markdown +++ b/zh-cn/go-cn.html.markdown @@ -6,7 +6,8 @@ contributors: - ["Sonia Keys", "https://github.com/soniakeys"] - ["pantaovay", "https://github.com/pantaovay"] - ["lidashuang", "https://github.com/lidashuang"] - + - ["Tim Zhang", "https://github.com/ttimasdf"] + --- 发明Go语言是出于更好地完成工作的需要。Go不是计算机科学的最新发展潮流,但它却提供了解决现实问题的最新最快的方法。 @@ -27,7 +28,10 @@ package main // Import语句声明了当前文件引用的包。 import ( "fmt" // Go语言标准库中的包 + "io/ioutil" // 包含一些输入输出函数 + m "math" // 数学标准库,在此文件中别名为m "net/http" // 一个web服务器包 + "os" // 系统底层函数,如文件读写 "strconv" // 字符串转换 ) @@ -36,7 +40,7 @@ import ( func main() { // 往标准输出打印一行。 // 用包名fmt限制打印函数。 - fmt.Println("Hello world!") + fmt.Println("天坑欢迎你!") // 调用当前包的另一个函数。 beyondHello() @@ -54,7 +58,11 @@ func beyondHello() { learnTypes() // 少于y分钟,学的更多! } -// 多变量和多返回值的函数 +/* <- 快看快看我是跨行注释_(:з」∠)_ +Go语言的函数可以有多个参数和 *多个* 返回值。 +在这个函数中, `x`、`y` 是参数, +`sum`、`prod` 是返回值的标识符(可以理解为名字)且类型为int +*/ func learnMultiple(x, y int) (sum, prod int) { return x + y, x * y // 返回两个值 } @@ -62,11 +70,11 @@ func learnMultiple(x, y int) (sum, prod int) { // 内置变量类型和关键词 func learnTypes() { // 短声明给你所想。 - s := "Learn Go!" // String类型 + str := "少说话多读书!" // String类型 + + s2 := `这是一个 +可以换行的字符串` // 同样是String类型 - s2 := `A "raw" string literal -can include line breaks.` // 同样是String类型 - // 非ascii字符。Go使用UTF-8编码。 g := 'Σ' // rune类型,int32的别名,使用UTF-8编码 @@ -80,16 +88,29 @@ can include line breaks.` // 同样是String类型 // 字符转换 n := byte('\n') // byte是uint8的别名 - // 数组类型编译的时候大小固定。 + // 数组(Array)类型的大小在编译时即确定 var a4 [4] int // 有4个int变量的数组,初始为0 a3 := [...]int{3, 1, 5} // 有3个int变量的数组,同时进行了初始化 - // Slice 可以动态的增删。Array和Slice各有千秋,但是使用slice的地方更多些。 - s3 := []int{4, 5, 9} // 和a3相比,这里没有省略号 - s4 := make([]int, 4) // 分配一个有4个int型变量的slice,全部被初始化为0 - - var d2 [][]float64 // 声明而已,什么都没有分配 - bs := []byte("a slice") // 类型转换的语法 + // Array和slice各有所长,但是slice可以动态的增删,所以更多时候还是使用slice。 + s3 := []int{4, 5, 9} // 回去看看 a3 ,是不是这里没有省略号? + s4 := make([]int, 4) // 分配4个int大小的内存并初始化为0 + var d2 [][]float64 // 这里只是声明,并未分配内存空间 + bs := []byte("a slice") // 进行类型转换 + + // 切片(Slice)的大小是动态的,它的长度可以按需增长 + // 用内置函数 append() 向切片末尾添加元素 + // 要增添到的目标是 append 函数第一个参数, + // 多数时候数组在原内存处顺次增长,如 + s := []int{1, 2, 3} // 这是个长度3的slice + s = append(s, 4, 5, 6) // 再加仨元素,长度变为6了 + fmt.Println(s) // 更新后的数组是 [1 2 3 4 5 6] + + // 除了向append()提供一组原子元素(写死在代码里的)以外,我们 + // 还可以用如下方法传递一个slice常量或变量,并在后面加上省略号, + // 用以表示我们将引用一个slice、解包其中的元素并将其添加到s数组末尾。 + s = append(s, []int{7, 8, 9}...) // 第二个参数是一个slice常量 + fmt.Println(s) // 更新后的数组是 [1 2 3 4 5 6 7 8 9] p, q := learnMemory() // 声明p,q为int型变量的指针 fmt.Println(*p, *q) // * 取值 @@ -100,11 +121,27 @@ can include line breaks.` // 同样是String类型 // 在Go语言中未使用的变量在编译的时候会报错,而不是warning。 // 下划线 _ 可以使你“使用”一个变量,但是丢弃它的值。 - _,_,_,_,_,_,_,_,_ = s2, g, f, u, pi, n, a3, s4, bs + _, _, _, _, _, _, _, _, _, _ = str, s2, g, f, u, pi, n, a3, s4, bs + // 通常的用法是,在调用拥有多个返回值的函数时, + // 用下划线抛弃其中的一个参数。下面的例子就是一个脏套路, + // 调用os.Create并用下划线变量扔掉它的错误代码。 + // 因为我们觉得这个文件一定会成功创建。 + file, _ := os.Create("output.txt") + fmt.Fprint(file, "这句代码还示范了如何写入文件呢") + file.Close() + // 输出变量 fmt.Println(s, c, a4, s3, d2, m) - learnFlowControl() // 回到流程控制 + learnFlowControl() // 回到流程控制 +} + +// 和其他编程语言不同的是,go支持有名称的变量返回值。 +// 声明返回值时带上一个名字允许我们在函数内的不同位置 +// 只用写return一个词就能将函数内指定名称的变量返回 +func learnNamedReturns(x, y int) (z int) { + z = x * y + return // z is implicit here, because we named it earlier. } // Go全面支持垃圾回收。Go有指针,但是不支持指针运算。 @@ -126,10 +163,10 @@ func expensiveComputation() int { func learnFlowControl() { // If需要花括号,括号就免了 if true { - fmt.Println("told ya") + fmt.Println("这句话肯定被执行") } // 用go fmt 命令可以帮你格式化代码,所以不用怕被人吐槽代码风格了, - // 也不用容忍被人的代码风格。 + // 也不用容忍别人的代码风格。 if false { // pout } else { @@ -146,15 +183,28 @@ func learnFlowControl() { } // 和if一样,for也不用括号 for x := 0; x < 3; x++ { // ++ 自增 - fmt.Println("iteration", x) + fmt.Println("遍历", x) } // x在这里还是1。为什么? // for 是go里唯一的循环关键字,不过它有很多变种 for { // 死循环 - break // 骗你的 + break // 骗你的 continue // 不会运行的 } + + // 用range可以枚举 array、slice、string、map、channel等不同类型 + // 对于channel,range返回一个值, + // array、slice、string、map等其他类型返回一对儿 + for key, value := range map[string]int{"one": 1, "two": 2, "three": 3} { + // 打印map中的每一个键值对 + fmt.Printf("索引:%s, 值为:%d\n", key, value) + } + // 如果你只想要值,那就用前面讲的下划线扔掉没用的 + for _, name := range []string{"Bob", "Bill", "Joe"} { + fmt.Printf("你是。。 %s\n", name) + } + // 和for一样,if中的:=先给y赋值,然后再和x作比较。 if y := expensiveComputation(); y > x { x = y @@ -163,17 +213,55 @@ func learnFlowControl() { xBig := func() bool { return x > 100 // x是上面声明的变量引用 } - fmt.Println("xBig:", xBig()) // true (上面把y赋给x了) + fmt.Println("xBig:", xBig()) // true (上面把y赋给x了) x /= 1e5 // x变成10 fmt.Println("xBig:", xBig()) // 现在是false + // 除此之外,函数体可以在其他函数中定义并调用, + // 满足下列条件时,也可以作为参数传递给其他函数: + // a) 定义的函数被立即调用 + // b) 函数返回值符合调用者对类型的要求 + fmt.Println("两数相加乘二: ", + func(a, b int) int { + return (a + b) * 2 + }(10, 2)) // Called with args 10 and 2 + // => Add + double two numbers: 24 + // 当你需要goto的时候,你会爱死它的! goto love love: + learnFunctionFactory() // 返回函数的函数多棒啊 + learnDefer() // 对defer关键字的简单介绍 learnInterfaces() // 好东西来了! } +func learnFunctionFactory() { + // 空行分割的两个写法是相同的,不过第二个写法比较实用 + fmt.Println(sentenceFactory("原谅")("当然选择", "她!")) + + d := sentenceFactory("原谅") + fmt.Println(d("当然选择", "她!")) + fmt.Println(d("你怎么可以", "她?")) +} + +// Decorator在一些语言中很常见,在go语言中, +// 接受参数作为其定义的一部分的函数是修饰符的替代品 +func sentenceFactory(mystring string) func(before, after string) string { + return func(before, after string) string { + return fmt.Sprintf("%s %s %s", before, mystring, after) // new string + } +} + +func learnDefer() (ok bool) { + // defer表达式在函数返回的前一刻执行 + defer fmt.Println("defer表达式执行顺序为后进先出(LIFO)") + defer fmt.Println("\n这句话比上句话先输出,因为") + // 关于defer的用法,例如用defer关闭一个文件, + // 就可以让关闭操作与打开操作的代码更近一些 + return true +} + // 定义Stringer为一个接口类型,有一个方法String type Stringer interface { String() string @@ -194,9 +282,9 @@ func (p pair) String() string { // p被叫做“接收器” func learnInterfaces() { // 花括号用来定义结构体变量,:=在这里将一个结构体变量赋值给p。 p := pair{3, 4} - fmt.Println(p.String()) // 调用pair类型p的String方法 - var i Stringer // 声明i为Stringer接口类型 - i = p // 有效!因为p实现了Stringer接口(类似java中的塑型) + fmt.Println(p.String()) // 调用pair类型p的String方法 + var i Stringer // 声明i为Stringer接口类型 + i = p // 有效!因为p实现了Stringer接口(类似java中的塑型) // 调用i的String方法,输出和上面一样 fmt.Println(i.String()) @@ -205,14 +293,28 @@ func learnInterfaces() { fmt.Println(p) // 输出和上面一样,自动调用String函数。 fmt.Println(i) // 输出和上面一样。 + learnVariadicParams("great", "learning", "here!") +} + +// 有变长参数列表的函数 +func learnVariadicParams(myStrings ...interface{}) { + // 枚举变长参数列表的每个参数值 + // 下划线在这里用来抛弃枚举时返回的数组索引值 + for _, param := range myStrings { + fmt.Println("param:", param) + } + + // 将可变参数列表作为其他函数的参数列表 + fmt.Println("params:", fmt.Sprintln(myStrings...)) + learnErrorHandling() } func learnErrorHandling() { - // ", ok"用来判断有没有正常工作 + // ", ok"用来判断有没有正常工作 m := map[int]string{3: "three", 4: "four"} if x, ok := m[1]; !ok { // ok 为false,因为m中没有1 - fmt.Println("no one there") + fmt.Println("别找了真没有") } else { fmt.Print(x) // 如果x在map中的话,x就是那个值喽。 } @@ -239,23 +341,23 @@ func learnConcurrency() { go inc(0, c) // go is a statement that starts a new goroutine. go inc(10, c) go inc(-805, c) - // 从channel中独处结果并打印。 + // 从channel中读取结果并打印。 // 打印出什么东西是不可预知的。 fmt.Println(<-c, <-c, <-c) // channel在右边的时候,<-是读操作。 cs := make(chan string) // 操作string的channel cc := make(chan chan string) // 操作channel的channel - go func() { c <- 84 }() // 开始一个goroutine来发送一个新的数字 + go func() { c <- 84 }() // 开始一个goroutine来发送一个新的数字 go func() { cs <- "wordy" }() // 发送给cs // Select类似于switch,但是每个case包括一个channel操作。 // 它随机选择一个准备好通讯的case。 select { case i := <-c: // 从channel接收的值可以赋给其他变量 - fmt.Println("it's a", i) + fmt.Println("这是……", i) case <-cs: // 或者直接丢弃 - fmt.Println("it's a string") - case <-cc: // 空的,还没作好通讯的准备 - fmt.Println("didn't happen.") + fmt.Println("这是个字符串!") + case <-cc: // 空的,还没作好通讯的准备 + fmt.Println("别瞎想") } // 上面c或者cs的值被取到,其中一个goroutine结束,另外一个一直阻塞。 @@ -265,22 +367,43 @@ func learnConcurrency() { // http包中的一个简单的函数就可以开启web服务器。 func learnWebProgramming() { // ListenAndServe第一个参数指定了监听端口,第二个参数是一个接口,特定是http.Handler。 - err := http.ListenAndServe(":8080", pair{}) - fmt.Println(err) // 不要无视错误。 + go func() { + err := http.ListenAndServe(":8080", pair{}) + fmt.Println(err) // 不要无视错误。 + }() + + requestServer() } // 使pair实现http.Handler接口的ServeHTTP方法。 func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 使用http.ResponseWriter返回数据 - w.Write([]byte("You learned Go in Y minutes!")) + w.Write([]byte("Y分钟golang速成!")) +} + +func requestServer() { + resp, err := http.Get("http://localhost:8080") + fmt.Println(err) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + fmt.Printf("\n服务器消息: `%s`", string(body)) } ``` ## 更进一步 -Go的根源在[Go官方网站](http://golang.org/)。 -在那里你可以学习入门教程,通过浏览器交互式地学习,而且可以读到很多东西。 +关于Go的一切你都可以在[Go官方网站](http://golang.org/)找到。 +在那里你可以获得教程参考,在线试用,和更多的资料。 +在简单的尝试过后,在[官方文档](https://golang.org/doc/)那里你会得到你所需要的所有资料、关于编写代码的规范、库和命令行工具的文档与Go的版本历史。 + +强烈推荐阅读语言定义部分,很简单而且很简洁!(赶时髦!) + +你还可以前往[Go在线体验中心](https://play.golang.org/p/tnWMjr16Mm)进,在浏览器里修改并运行这些代码,一定要试一试哦!你可以将[https://play.golang.org](https://play.golang.org)当作一个[REPL](https://en.wikipedia.org/wiki/Read-eval-print_loop),在那里体验语言特性或运行自己的代码,连环境都不用配! + +学习Go还要阅读Go[标准库的源代码](http://golang.org/src/),全部文档化了,可读性非常好,可以学到go,go style和go idioms。在[文档](http://golang.org/pkg/)中点击函数名,源代码就出来了! + +[Go by example](https://gobyexample.com/)也是一个学习的好地方。 + -强烈推荐阅读语言定义部分,很简单而且很简洁!(as language definitions go these days.) -学习Go还要阅读Go标准库的源代码,全部文档化了,可读性非常好,可以学到go,go style和go idioms。在文档中点击函数名,源代码就出来了! +Go Mobile添加了对移动平台的支持(Android and iOS)。你可以完全用go语言来创造一个app或编写一个可以从Java或Obj-C调用的函数库,敬请参考[Go Mobile page](https://github.com/golang/go/wiki/Mobile)。 diff --git a/zh-cn/groovy-cn.html.markdown b/zh-cn/groovy-cn.html.markdown new file mode 100644 index 00000000..0e7a020c --- /dev/null +++ b/zh-cn/groovy-cn.html.markdown @@ -0,0 +1,422 @@ +--- +language: Groovy +filename: learngroovy-cn.groovy +contributors: + - ["Roberto Pérez Alcolea", "http://github.com/rpalcolea"] +translators: + - ["Todd Gao", "http://github.com/7c00"] +lang: zh-cn +--- + +Groovy - Java平台的动态语言。[了解更多。](http://www.groovy-lang.org/) + +```groovy + +/* + 安装: + + 1) 安装 GVM - http://gvmtool.net/ + 2) 安装 Groovy: gvm install groovy + 3) 启动 groovy 控制台,键入: groovyConsole + +*/ + +// 双斜线开始的是单行注释 +/* +像这样的是多行注释 +*/ + +// Hello World +println "Hello world!" + +/* + 变量: + + 可以给变量赋值,以便稍后使用 +*/ + +def x = 1 +println x + +x = new java.util.Date() +println x + +x = -3.1499392 +println x + +x = false +println x + +x = "Groovy!" +println x + +/* + 集合和映射 +*/ + +//创建一个空的列表 +def technologies = [] + +/*** 往列表中增加一个元素 ***/ + +// 和Java一样 +technologies.add("Grails") + +// 左移添加,返回该列表 +technologies << "Groovy" + +// 增加多个元素 +technologies.addAll(["Gradle","Griffon"]) + +/*** 从列表中删除元素 ***/ + +// 和Java一样 +technologies.remove("Griffon") + +// 减号也行 +technologies = technologies - 'Grails' + +/*** 遍历列表 ***/ + +// 遍历列表中的元素 +technologies.each { println "Technology: $it"} +technologies.eachWithIndex { it, i -> println "$i: $it"} + +/*** 检查列表内容 ***/ + +//判断列表是否包含某元素,返回boolean +contained = technologies.contains( 'Groovy' ) + +// 或 +contained = 'Groovy' in technologies + +// 检查多个元素 +technologies.containsAll(['Groovy','Grails']) + +/*** 列表排序 ***/ + +// 排序列表(修改原列表) +technologies.sort() + +// 要想不修改原列表,可以这样: +sortedTechnologies = technologies.sort( false ) + +/*** 列表操作 ***/ + +//替换列表元素 +Collections.replaceAll(technologies, 'Gradle', 'gradle') + +//打乱列表 +Collections.shuffle(technologies, new Random()) + +//清空列表 +technologies.clear() + +//创建空的映射 +def devMap = [:] + +//增加值 +devMap = ['name':'Roberto', 'framework':'Grails', 'language':'Groovy'] +devMap.put('lastName','Perez') + +//遍历映射元素 +devMap.each { println "$it.key: $it.value" } +devMap.eachWithIndex { it, i -> println "$i: $it"} + +//判断映射是否包含某键 +assert devMap.containsKey('name') + +//判断映射是否包含某值 +assert devMap.containsValue('Roberto') + +//取得映射所有的键 +println devMap.keySet() + +//取得映射所有的值 +println devMap.values() + +/* + Groovy Beans + + GroovyBeans 是 JavaBeans,但使用了更简单的语法 + + Groovy 被编译为字节码时,遵循下列规则。 + + * 如果一个名字声明时带有访问修饰符(public, private, 或者 protected), + 则会生成一个字段(field)。 + + * 名字声明时没有访问修饰符,则会生成一个带有public getter和setter的 + private字段,即属性(property)。 + + * 如果一个属性声明为final,则会创建一个final的private字段,但不会生成setter。 + + * 可以声明一个属性的同时定义自己的getter和setter。 + + * 可以声明具有相同名字的属性和字段,该属性会使用该字段。 + + * 如果要定义private或protected属性,必须提供声明为private或protected的getter + 和setter。 + + * 如果使用显式或隐式的 this(例如 this.foo, 或者 foo)访问类的在编译时定义的属性, + Groovy会直接访问对应字段,而不是使用getter或者setter + + * 如果使用显式或隐式的 foo 访问一个不存在的属性,Groovy会通过元类(meta class) + 访问它,这可能导致运行时错误。 + +*/ + +class Foo { + // 只读属性 + final String name = "Roberto" + + // 只读属性,有public getter和protected setter + String language + protected void setLanguage(String language) { this.language = language } + + // 动态类型属性 + def lastName +} + +/* + 逻辑分支和循环 +*/ + +//Groovy支持常见的if - else语法 +def x = 3 + +if(x==1) { + println "One" +} else if(x==2) { + println "Two" +} else { + println "X greater than Two" +} + +//Groovy也支持三元运算符 +def y = 10 +def x = (y > 1) ? "worked" : "failed" +assert x == "worked" + +//for循环 +//使用区间(range)遍历 +def x = 0 +for (i in 0 .. 30) { + x += i +} + +//遍历列表 +x = 0 +for( i in [5,3,2,1] ) { + x += i +} + +//遍历数组 +array = (0..20).toArray() +x = 0 +for (i in array) { + x += i +} + +//遍历映射 +def map = ['name':'Roberto', 'framework':'Grails', 'language':'Groovy'] +x = "" +for ( e in map ) { + x += e.value + x += " " +} +assert x.equals("Roberto Grails Groovy ") + +/* + 运算符 + + 在Groovy中以下常用运算符支持重载: + http://www.groovy-lang.org/operators.html#Operator-Overloading + + 实用的groovy运算符 +*/ +//展开(spread)运算符:对聚合对象的所有元素施加操作 +def technologies = ['Groovy','Grails','Gradle'] +technologies*.toUpperCase() // 相当于 technologies.collect { it?.toUpperCase() } + +//安全导航(safe navigation)运算符:用来避免NullPointerException +def user = User.get(1) +def username = user?.username + + +/* + 闭包 + Groovy闭包好比代码块或者方法指针,它是一段代码定义,可以以后执行。 + + 更多信息见:http://www.groovy-lang.org/closures.html +*/ +//例子: +def clos = { println "Hello World!" } + +println "Executing the Closure:" +clos() + +//传参数给闭包 +def sum = { a, b -> println a+b } +sum(2,4) + +//闭包可以引用参数列表以外的变量 +def x = 5 +def multiplyBy = { num -> num * x } +println multiplyBy(10) + +// 只有一个参数的闭包可以省略参数的定义 +def clos = { print it } +clos( "hi" ) + +/* + Groovy可以记忆闭包结果 [1][2][3] +*/ +def cl = {a, b -> + sleep(3000) // 模拟费时操作 + a + b +} + +mem = cl.memoize() + +def callClosure(a, b) { + def start = System.currentTimeMillis() + mem(a, b) + println "Inputs(a = $a, b = $b) - took ${System.currentTimeMillis() - start} msecs." +} + +callClosure(1, 2) +callClosure(1, 2) +callClosure(2, 3) +callClosure(2, 3) +callClosure(3, 4) +callClosure(3, 4) +callClosure(1, 2) +callClosure(2, 3) +callClosure(3, 4) + +/* + Expando + + Expando类是一种动态bean类,可以给它的实例添加属性和添加闭包作为方法 + + http://mrhaki.blogspot.mx/2009/10/groovy-goodness-expando-as-dynamic-bean.html +*/ + def user = new Expando(name:"Roberto") + assert 'Roberto' == user.name + + user.lastName = 'Pérez' + assert 'Pérez' == user.lastName + + user.showInfo = { out -> + out << "Name: $name" + out << ", Last name: $lastName" + } + + def sw = new StringWriter() + println user.showInfo(sw) + + +/* + 元编程(MOP) +*/ + +//使用ExpandoMetaClass增加行为 +String.metaClass.testAdd = { + println "we added this" +} + +String x = "test" +x?.testAdd() + +//拦截方法调用 +class Test implements GroovyInterceptable { + def sum(Integer x, Integer y) { x + y } + + def invokeMethod(String name, args) { + System.out.println "Invoke method $name with args: $args" + } +} + +def test = new Test() +test?.sum(2,3) +test?.multiply(2,3) + +//Groovy支持propertyMissing,来处理属性解析尝试 +class Foo { + def propertyMissing(String name) { name } +} +def f = new Foo() + +assertEquals "boo", f.boo + +/* + 类型检查和静态编译 + Groovy天生是并将永远是一门动态语言,但也支持类型检查和静态编译 + + 更多: http://www.infoq.com/articles/new-groovy-20 +*/ +//类型检查 +import groovy.transform.TypeChecked + +void testMethod() {} + +@TypeChecked +void test() { + testMeethod() + + def name = "Roberto" + + println naameee + +} + +//另一例子 +import groovy.transform.TypeChecked + +@TypeChecked +Integer test() { + Integer num = "1" + + Integer[] numbers = [1,2,3,4] + + Date date = numbers[1] + + return "Test" + +} + +//静态编译例子 +import groovy.transform.CompileStatic + +@CompileStatic +int sum(int x, int y) { + x + y +} + +assert sum(2,5) == 7 + + +``` + +## 进阶资源 + +[Groovy文档](http://www.groovy-lang.org/documentation.html) + +[Groovy web console](http://groovyconsole.appspot.com/) + +加入[Groovy用户组](http://www.groovy-lang.org/usergroups.html) + +## 图书 + +* [Groovy Goodness] (https://leanpub.com/groovy-goodness-notebook) + +* [Groovy in Action] (http://manning.com/koenig2/) + +* [Programming Groovy 2: Dynamic Productivity for the Java Developer] (http://shop.oreilly.com/product/9781937785307.do) + +[1] http://roshandawrani.wordpress.com/2010/10/18/groovy-new-feature-closures-can-now-memorize-their-results/ +[2] http://www.solutionsiq.com/resources/agileiq-blog/bid/72880/Programming-with-Groovy-Trampoline-and-Memoize +[3] http://mrhaki.blogspot.mx/2011/05/groovy-goodness-cache-closure-results.html + + + diff --git a/zh-cn/haskell-cn.html.markdown b/zh-cn/haskell-cn.html.markdown index cb7ccdee..c854169e 100644 --- a/zh-cn/haskell-cn.html.markdown +++ b/zh-cn/haskell-cn.html.markdown @@ -5,24 +5,25 @@ contributors: - ["Adit Bhargava", "http://adit.io"] translators: - ["Peiyong Lin", ""] + - ["chad luo", "http://yuki.rocks"] lang: zh-cn --- -Haskell 被设计成一种实用的纯函数式编程语言。它因为 monads 及其类型系统而出名,但是我回归到它本身因为。Haskell 使得编程对于我而言是一种真正的快乐。 +Haskell 是一门实用的函数式编程语言,因其 Monads 与类型系统而闻名。而我使用它则是因为它异常优雅。用 Haskell 编程令我感到非常快乐。 ```haskell --- 单行注释以两个破折号开头 +-- 单行注释以两个减号开头 {- 多行注释像这样 - 被一个闭合的块包围 + 被一个闭合的块包围 -} ---------------------------------------------------- -- 1. 简单的数据类型和操作符 ---------------------------------------------------- --- 你有数字 +-- 数字 3 -- 3 --- 数学计算就像你所期待的那样 +-- 数学计算 1 + 1 -- 2 8 - 1 -- 7 10 * 2 -- 20 @@ -34,7 +35,7 @@ Haskell 被设计成一种实用的纯函数式编程语言。它因为 monads -- 整除 35 `div` 4 -- 8 --- 布尔值也简单 +-- 布尔值 True False @@ -45,184 +46,187 @@ not False -- True 1 /= 1 -- False 1 < 10 -- True --- 在上述的例子中,`not` 是一个接受一个值的函数。 --- Haskell 不需要括号来调用函数。。。所有的参数 --- 都只是在函数名之后列出来。因此,通常的函数调用模式是: --- func arg1 arg2 arg3... --- 查看关于函数的章节以获得如何写你自己的函数的相关信息。 +-- 在上面的例子中,`not` 是一个接受一个参数的函数。 +-- Haskell 不需要括号来调用函数,所有的参数都只是在函数名之后列出来 +-- 因此,通常的函数调用模式是: +-- func arg1 arg2 arg3... +-- 你可以查看函数部分了解如何自行编写。 -- 字符串和字符 -"This is a string." +"This is a string." -- 字符串 'a' -- 字符 '对于字符串你不能使用单引号。' -- 错误! --- 连结字符串 +-- 连接字符串 "Hello " ++ "world!" -- "Hello world!" -- 一个字符串是一系列字符 +['H', 'e', 'l', 'l', 'o'] -- "Hello" "This is a string" !! 0 -- 'T' ---------------------------------------------------- --- 列表和元组 +-- 2. 列表和元组 ---------------------------------------------------- --- 一个列表中的每一个元素都必须是相同的类型 --- 下面两个列表一样 +-- 一个列表中的每一个元素都必须是相同的类型。 +-- 下面两个列表等价 [1, 2, 3, 4, 5] [1..5] --- 在 Haskell 你可以拥有含有无限元素的列表 -[1..] -- 一个含有所有自然数的列表 +-- 区间也可以这样 +['A'..'F'] -- "ABCDEF" --- 因为 Haskell 有“懒惰计算”,所以无限元素的列表可以正常运作。这意味着 --- Haskell 可以只在它需要的时候计算。所以你可以请求 --- 列表中的第1000个元素,Haskell 会返回给你 +-- 你可以在区间中指定步进 +[0,2..10] -- [0, 2, 4, 6, 8, 10] +[5..1] -- 这样不行,因为 Haskell 默认递增 +[5,4..1] -- [5, 4, 3, 2, 1] -[1..] !! 999 -- 1000 +-- 列表下标 +[0..] !! 5 -- 5 --- Haskell 计算了列表中 1 - 1000 个元素。。。但是 --- 这个无限元素的列表中剩下的元素还不存在! Haskell 不会 --- 真正地计算它们知道它需要。 +-- 在 Haskell 你可以使用无限列表 +[1..] -- 一个含有所有自然数的列表 -<FS>- 连接两个列表 +-- 无限列表的原理是,Haskell 有“惰性求值”。 +-- 这意味着 Haskell 只在需要时才会计算。 +-- 所以当你获取列表的第 1000 项元素时,Haskell 会返回给你: +[1..] !! 999 -- 1000 +-- Haskell 计算了列表中第 1 至 1000 项元素,但这个无限列表中剩下的元素还不存在。 +-- Haskell 只有在需要时才会计算它们。 + +-- 连接两个列表 [1..5] ++ [6..10] -- 往列表头增加元素 0:[1..5] -- [0, 1, 2, 3, 4, 5] --- 列表中的下标 -[0..] !! 5 -- 5 - --- 更多列表操作 +-- 其它列表操作 head [1..5] -- 1 tail [1..5] -- [2, 3, 4, 5] init [1..5] -- [1, 2, 3, 4] last [1..5] -- 5 --- 列表推导 +-- 列表推导 (list comprehension) [x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10] -- 附带条件 [x*2 | x <-[1..5], x*2 > 4] -- [6, 8, 10] --- 元组中的每一个元素可以是不同类型的,但是一个元组 --- 的长度是固定的 +-- 元组中的每一个元素可以是不同类型,但是一个元组的长度是固定的 -- 一个元组 ("haskell", 1) --- 获取元组中的元素 +-- 获取元组中的元素(例如,一个含有 2 个元素的元祖) fst ("haskell", 1) -- "haskell" snd ("haskell", 1) -- 1 ---------------------------------------------------- -- 3. 函数 ---------------------------------------------------- + -- 一个接受两个变量的简单函数 add a b = a + b --- 注意,如果你使用 ghci (Hakell 解释器) --- 你将需要使用 `let`,也就是 +-- 注意,如果你使用 ghci (Hakell 解释器),你需要使用 `let`,也就是 -- let add a b = a + b --- 使用函数 +-- 调用函数 add 1 2 -- 3 --- 你也可以把函数放置在两个参数之间 --- 附带倒引号: +-- 你也可以使用反引号中置函数名: 1 `add` 2 -- 3 --- 你也可以定义不带字符的函数!这使得 --- 你定义自己的操作符!这里有一个操作符 --- 来做整除 +-- 你也可以定义不带字母的函数名,这样你可以定义自己的操作符。 +-- 这里有一个做整除的操作符 (//) a b = a `div` b 35 // 4 -- 8 --- 守卫:一个简单的方法在函数里做分支 +-- Guard:一个在函数中做条件判断的简单方法 fib x | x < 2 = x | otherwise = fib (x - 1) + fib (x - 2) --- 模式匹配是类型的。这里有三种不同的 fib --- 定义。Haskell 将自动调用第一个 --- 匹配值的模式的函数。 +-- 模式匹配与 Guard 类似。 +-- 这里给出了三个不同的 fib 定义。 +-- Haskell 会自动调用第一个符合参数模式的声明 fib 1 = 1 fib 2 = 2 fib x = fib (x - 1) + fib (x - 2) --- 元组的模式匹配: +-- 元组的模式匹配 foo (x, y) = (x + 1, y + 2) --- 列表的模式匹配。这里 `x` 是列表中第一个元素, --- 并且 `xs` 是列表剩余的部分。我们可以写 --- 自己的 map 函数: +-- 列表的模式匹配 +-- 这里 `x` 是列表中第一个元素,`xs` 是列表剩余的部分。 +-- 我们可以实现自己的 map 函数: myMap func [] = [] myMap func (x:xs) = func x:(myMap func xs) --- 编写出来的匿名函数带有一个反斜杠,后面跟着 --- 所有的参数。 +-- 匿名函数带有一个反斜杠,后面跟着所有的参数 myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7] --- 使用 fold (在一些语言称为`inject`)随着一个匿名的 --- 函数。foldl1 意味着左折叠(fold left), 并且使用列表中第一个值 --- 作为累加器的初始化值。 +-- 在 fold(在一些语言称 为`inject`)中使用匿名函数 +-- foldl1 意味着左折叠 (fold left), 并且使用列表中第一个值作为累加器的初始值。 foldl1 (\acc x -> acc + x) [1..5] -- 15 ---------------------------------------------------- --- 4. 更多的函数 +-- 4. 其它函数 ---------------------------------------------------- --- 柯里化(currying):如果你不传递函数中所有的参数, --- 它就变成“柯里化的”。这意味着,它返回一个接受剩余参数的函数。 - +-- 部分调用 +-- 如果你调用函数时没有给出所有参数,它就被“部分调用”。 +-- 它将返回一个接受余下参数的函数。 add a b = a + b foo = add 10 -- foo 现在是一个接受一个数并对其加 10 的函数 foo 5 -- 15 --- 另外一种方式去做同样的事 +-- 另一种等价写法 foo = (+10) foo 5 -- 15 --- 函数组合 --- (.) 函数把其它函数链接到一起 --- 举个列子,这里 foo 是一个接受一个值的函数。它对接受的值加 10, --- 并对结果乘以 5,之后返回最后的值。 +-- 函列表合 +-- (.) 函数把其它函数链接到一起。 +-- 例如,这里 foo 是一个接受一个值的函数。 +-- 它对接受的值加 10,并对结果乘以 5,之后返回最后的值。 foo = (*5) . (+10) -- (5 + 10) * 5 = 75 foo 5 -- 75 --- 修复优先级 --- Haskell 有另外一个函数称为 `$`。它改变优先级 --- 使得其左侧的每一个操作先计算然后应用到 --- 右侧的每一个操作。你可以使用 `.` 和 `$` 来除去很多 --- 括号: +-- 修正优先级 +-- Haskell 有另外一个函数 `$` 可以改变优先级。 +-- `$` 使得 Haskell 先计算其右边的部分,然后调用左边的部分。 +-- 你可以使用 `$` 来移除多余的括号。 --- before -(even (fib 7)) -- true +-- 修改前 +(even (fib 7)) -- False --- after -even . fib $ 7 -- true +-- 修改后 +even . fib $ 7 -- False + +-- 等价地 +even $ fib 7 -- False ---------------------------------------------------- --- 5. 类型签名 +-- 5. 类型声明 ---------------------------------------------------- --- Haskell 有一个非常强壮的类型系统,一切都有一个类型签名。 +-- Haskell 有一个非常强大的类型系统,一切都有一个类型声明。 -- 一些基本的类型: 5 :: Integer "hello" :: String True :: Bool --- 函数也有类型。 --- `not` 接受一个布尔型返回一个布尔型: +-- 函数也有类型 +-- `not` 接受一个布尔型返回一个布尔型 -- not :: Bool -> Bool --- 这是接受两个参数的函数: +-- 这是接受两个参数的函数 -- add :: Integer -> Integer -> Integer --- 当你定义一个值,在其上写明它的类型是一个好实践: +-- 当你定义一个值,声明其类型是一个好做法 double :: Integer -> Integer double x = x * 2 @@ -230,159 +234,148 @@ double x = x * 2 -- 6. 控制流和 If 语句 ---------------------------------------------------- --- if 语句 +-- if 语句: haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome" --- if 语句也可以有多行,缩进是很重要的 +-- if 语句也可以有多行,注意缩进: haskell = if 1 == 1 then "awesome" else "awful" --- case 语句:这里是你可以怎样去解析命令行参数 +-- case 语句 +-- 解析命令行参数: case args of "help" -> printHelp "start" -> startProgram _ -> putStrLn "bad args" --- Haskell 没有循环因为它使用递归取代之。 --- map 应用一个函数到一个数组中的每一个元素 - +-- Haskell 没有循环,它使用递归 +-- map 对一个列表中的每一个元素调用一个函数 map (*2) [1..5] -- [2, 4, 6, 8, 10] -- 你可以使用 map 来编写 for 函数 for array func = map func array --- 然后使用它 +-- 调用 for [0..5] $ \i -> show i --- 我们也可以像这样写: +-- 我们也可以像这样写 for [0..5] show -- 你可以使用 foldl 或者 foldr 来分解列表 -- foldl <fn> <initial value> <list> foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43 --- 这和下面是一样的 +-- 等价于 (2 * (2 * (2 * 4 + 1) + 2) + 3) --- foldl 是左手边的,foldr 是右手边的- +-- foldl 从左开始,foldr 从右 foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16 --- 这和下面是一样的 +-- 现在它等价于 (2 * 3 + (2 * 2 + (2 * 1 + 4))) ---------------------------------------------------- -- 7. 数据类型 ---------------------------------------------------- --- 这里展示在 Haskell 中你怎样编写自己的数据类型 - +-- 在 Haskell 中声明你自己的数据类型: data Color = Red | Blue | Green -- 现在你可以在函数中使用它: - - say :: Color -> String say Red = "You are Red!" say Blue = "You are Blue!" say Green = "You are Green!" -- 你的数据类型也可以有参数: - data Maybe a = Nothing | Just a --- 类型 Maybe 的所有 -Just "hello" -- of type `Maybe String` -Just 1 -- of type `Maybe Int` -Nothing -- of type `Maybe a` for any `a` +-- 这些都是 Maybe 类型: +Just "hello" -- `Maybe String` 类型 +Just 1 -- `Maybe Int` 类型 +Nothing -- 对任意 `a` 为 `Maybe a` 类型 ---------------------------------------------------- -- 8. Haskell IO ---------------------------------------------------- --- 虽然在没有解释 monads 的情况下 IO不能被完全地解释, --- 着手解释到位并不难。 - --- 当一个 Haskell 程序被执行,函数 `main` 就被调用。 --- 它必须返回一个类型 `IO ()` 的值。举个列子: +-- 虽然不解释 Monads 就无法完全解释 IO,但大致了解并不难。 +-- 当执行一个 Haskell 程序时,函数 `main` 就被调用。 +-- 它必须返回一个类型 `IO ()` 的值。例如: main :: IO () main = putStrLn $ "Hello, sky! " ++ (say Blue) --- putStrLn has type String -> IO () +-- putStrLn 的类型是 String -> IO () --- 如果你能实现你的程序依照函数从 String 到 String,那样编写 IO 是最简单的。 +-- 如果你的程序输入 String 返回 String,那样编写 IO 是最简单的。 -- 函数 -- interact :: (String -> String) -> IO () --- 输入一些文本,在其上运行一个函数,并打印出输出 +-- 输入一些文本,对其调用一个函数,并打印输出。 countLines :: String -> String countLines = show . length . lines main' = interact countLines --- 你可以考虑一个 `IO()` 类型的值,当做一系列计算机所完成的动作的代表, --- 就像一个以命令式语言编写的计算机程序。我们可以使用 `do` 符号来把动作链接到一起。 --- 举个列子: - +-- 你可以认为一个 `IO ()` 类型的值是表示计算机做的一系列操作,类似命令式语言。 +-- 我们可以使用 `do` 声明来把动作连接到一起。 +-- 举个列子 sayHello :: IO () sayHello = do putStrLn "What is your name?" - name <- getLine -- this gets a line and gives it the name "input" + name <- getLine -- 这里接受一行输入并绑定至 "name" putStrLn $ "Hello, " ++ name -- 练习:编写只读取一行输入的 `interact` -- 然而,`sayHello` 中的代码将不会被执行。唯一被执行的动作是 `main` 的值。 --- 为了运行 `sayHello`,注释上面 `main` 的定义,并代替它: +-- 为了运行 `sayHello`,注释上面 `main` 的定义,替换为: -- main = sayHello --- 让我们来更好地理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是: +-- 让我们来更进一步理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是: -- getLine :: IO String --- 你可以考虑一个 `IO a` 类型的值,代表一个当被执行的时候 --- 将产生一个 `a` 类型的值的计算机程序(除了它所做的任何事之外)。我们可以保存和重用这个值通过 `<-`。 --- 我们也可以写自己的 `IO String` 类型的动作: - +-- 你可以认为一个 `IO a` 类型的值代表了一个运行时会生成一个 `a` 类型值的程序。 +-- (可能伴随其它行为) +-- 我们可以通过 `<-` 保存和重用这个值。 +-- 我们也可以实现自己的 `IO String` 类型函数: action :: IO String action = do putStrLn "This is a line. Duh" input1 <- getLine input2 <- getLine - -- The type of the `do` statement is that of its last line. - -- `return` is not a keyword, but merely a function + -- `do` 语句的类型是它的最后一行 + -- `return` 不是关键字,只是一个普通函数 return (input1 ++ "\n" ++ input2) -- return :: String -> IO String --- 我们可以使用这个动作就像我们使用 `getLine`: - +-- 我们可以像调用 `getLine` 一样调用它 main'' = do putStrLn "I will echo two lines!" result <- action putStrLn result putStrLn "This was all, folks!" --- `IO` 类型是一个 "monad" 的例子。Haskell 使用一个 monad 来做 IO的方式允许它是一门纯函数式语言。 --- 任何与外界交互的函数(也就是 IO) 都在它的类型签名处做一个 `IO` 标志 --- 着让我们推出 什么样的函数是“纯洁的”(不与外界交互,不修改状态) 和 什么样的函数不是 “纯洁的” - --- 这是一个强有力的特征,因为并发地运行纯函数是简单的;因此,Haskell 中并发是非常简单的。 - +-- `IO` 类型是一个 "Monad" 的例子。 +-- Haskell 通过使用 Monad 使得其本身为纯函数式语言。 +-- 任何与外界交互的函数(即 IO)都在它的类型声明中标记为 `IO`。 +-- 这告诉我们什么样的函数是“纯洁的”(不与外界交互,不修改状态) , +-- 什么样的函数不是 “纯洁的”。 +-- 这个功能非常强大,因为纯函数并发非常容易,由此在 Haskell 中做并发非常容易。 ---------------------------------------------------- --- 9. The Haskell REPL +-- 9. Haskell REPL ---------------------------------------------------- --- 键入 `ghci` 开始 repl。 +-- 键入 `ghci` 开始 REPL。 -- 现在你可以键入 Haskell 代码。 --- 任何新值都需要通过 `let` 来创建: - +-- 任何新值都需要通过 `let` 来创建 let foo = 5 --- 你可以查看任何值的类型,通过命令 `:t`: - +-- 你可以通过命令 `:t` 查看任何值的类型 >:t foo foo :: Integer -- 你也可以运行任何 `IO ()`类型的动作 - > sayHello What is your name? Friend! @@ -390,7 +383,7 @@ Hello, Friend! ``` -还有很多关于 Haskell,包括类型类和 monads。这些是使得编码 Haskell 是如此有趣的主意。我用一个最后的 Haskell 例子来结束:一个 Haskell 的快排实现: +Haskell 还有许多内容,包括类型类 (typeclasses) 与 Monads。这些都是令 Haskell 编程非常有趣的好东西。我们最后给出 Haskell 的一个例子,一个快速排序的实现: ```haskell qsort [] = [] @@ -399,9 +392,9 @@ qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater greater = filter (>= p) xs ``` -安装 Haskell 是简单的。你可以从[这里](http://www.haskell.org/platform/)获得它。 +安装 Haskell 很简单。你可以[从这里获得](http://www.haskell.org/platform/)。 你可以从优秀的 [Learn you a Haskell](http://learnyouahaskell.com/) 或者 [Real World Haskell](http://book.realworldhaskell.org/) -找到优雅不少的入门介绍。 +找到更平缓的入门介绍。 diff --git a/zh-cn/html-cn.html.markdown b/zh-cn/html-cn.html.markdown new file mode 100644 index 00000000..6f60d5ce --- /dev/null +++ b/zh-cn/html-cn.html.markdown @@ -0,0 +1,121 @@ +--- +language: html +filename: learnhtml-cn.html +contributors: + - ["Christophe THOMAS", "https://github.com/WinChris"] +translators: + - ["zxyqwe", "https://github.com/zxyqwe"] +lang: zh-cn +--- + +HTML是超文本标记语言的缩写。 +这门语言可以让我们为万维网创造页面。 +这是一门标记语言,它允许我们用代码来指示网页上文字和数据应该如何显示。 +实际上html文件是简单的文本文件。 +什么是标记?标记是通过使用开始和结束标签包围数据的方法,来组织管理页面上的数据。 +这些标记对它们环绕的文本有重要的意义。 +和其它计算机语言意义,HTML有很多版本。这里我们将讨论HTML5。 + +**注意:** 你可以在类似[codepen](http://codepen.io/pen/)的网站上的教程中,尝试不同的标签和元素带来的效果,理解它们如何起效,并且逐渐熟悉这门语言。 +本文主要关注HTML的语法和一些有用的小窍门。 + + +```html +<!-- 注释要像本行一样被包围起来! --> + +<!-- #################### 标签 #################### --> + +<!-- 下面是一个我们将要分析的HTML文件的例子。 --> + +<!doctype html> + <html> + <head> + <title>我的网站</title> + </head> + <body> + <h1>Hello, world!</h1> + <a href = "http://codepen.io/anon/pen/xwjLbZ">来看看这里有什么</a> + <p>这是一个段落。</p> + <p>这是另外一个段落。</p> + <ul> + <li>这是一个非计数列表的一项(项目符合列表)</li> + <li>这是另一项</li> + <li>这是列表中的最后一项</li> + </ul> + </body> + </html> + +<!-- 一个HTML文件通常开始于向浏览器表明本页面是HTML。 --> +<!doctype html> + +<!-- 在这之后,由<html>开始标签作为起始。 --> +<html> + +<!-- 在文件的最后会由</html>标签结束。 --> +</html> + +<!-- 在最终的标签后面应该没有任何东西。 --> + +<!-- 在其中(在开始标签<html>和结束标签</html>中间)我们可以看到: --> + +<!-- 由标签<head>定义的头部 (头部必须被</head>标签关闭)。 --> +<!-- 头部包含一些不显示的描述和额外信息;这些是元数据。 --> + +<head> + <title>我的网站</title><!-- <title>标签告诉浏览器在浏览器窗口的标题区和标签栏应该显示什么标题。 --> +</head> + +<!-- 在<head>区域之后,我们可以看到<body>标签 --> +<!-- 在这点之前的内容都不会显示在浏览器的窗口中。 --> +<!-- 我们必须在正文区填上需要显示的内容。 --> + +<body> + <h1>Hello, world!</h1> <!-- h1标签创建了一个标题 --> + <!-- <h1>标签可以有一些副标题,从最重要的(h2)到最细微的(h6)。 --> + <a href = "http://codepen.io/anon/pen/xwjLbZ">来看看这里有什么</a> <!-- 一个指向href=""属性中URL的超链接 --> + <p>这是一个段落。</p> <!-- <p>标签让我们在html页面中显示文字 --> + <p>这是另外一个段落。</p> + <ul> <!-- <ul>标签创建了一个项目符合列表。 --> + <!-- 如果需要一个编号列表,我们可以使用<ol>标签。这样会在在第一项前显示1.,第二项前显示2.,以此类推。 --> + <li>这是一个非计数列表的一项(项目符合列表)</li> + <li>这是另一项</li> + <li>这是列表中的最后一项</li> + </ul> +</body> + +<!-- 好了,创建一个HTML文件就是这么简单。 --> + +<!-- 当然我们还可以加入很多额外的HTML标签类型。 --> + +<!-- 插入图片。 --> +<img src="http://i.imgur.com/XWG0O.gif"/> <!-- 图片源是由src=""属性指明的 --> +<!-- 图片源可以是一个URL或者你电脑上一个文件的路径。 --> + +<!-- 创建表格也没问题。 --> + +<table> <!-- 我们开始一个<table>元素 --> + <tr> <!-- <tr>让我们创建一行 --> + <th>第一个表头</th> <!-- <th>让我们给表格列一个标题 --> + <th>第二个表头</th> + </tr> + <tr> + <td>第一行第一列</td> <!-- <td>让我们创建一个单元格 --> + <td>第一行第二列</td> + </tr> + <tr> + <td>第二行第一列</td> + <td>第二行第二列</td> + </tr> +</table> + +``` + +## 使用 + +HTML文件使用`.html`后缀。 + +## 扩展阅读 + +* [维基百科](https://en.wikipedia.org/wiki/HTML) +* [HTML tutorial](https://developer.mozilla.org/en-US/docs/Web/HTML) +* [W3School](http://www.w3schools.com/html/html_intro.asp) diff --git a/zh-cn/java-cn.html.markdown b/zh-cn/java-cn.html.markdown index f7d319e6..27003f3e 100644 --- a/zh-cn/java-cn.html.markdown +++ b/zh-cn/java-cn.html.markdown @@ -108,7 +108,7 @@ public class LearnJava { boolean [] booleanArray = new boolean[100]; // 声明并初始化数组也可以这样: - int [] y = {9000, 1000, 1337}; + int [] intArray = {9000, 1000, 1337}; // 随机访问数组中的元素 System.out.println("intArray @ 0: " + intArray[0]); @@ -124,7 +124,7 @@ public class LearnJava { // HashMaps /////////////////////////////////////// - // 操作符 + // 操作符 /////////////////////////////////////// System.out.println("\n->Operators"); @@ -149,7 +149,7 @@ public class LearnJava { // 位运算操作符 /* - ~ 补 + ~ 取反,求反码 << 带符号左移 >> 带符号右移 >>> 无符号右移 @@ -161,10 +161,13 @@ public class LearnJava { // 自增 int i = 0; System.out.println("\n->Inc/Dec-rementation"); - System.out.println(i++); //i = 1 后自增 - System.out.println(++i); //i = 2 前自增 - System.out.println(i--); //i = 1 后自减 - System.out.println(--i); //i = 0 前自减 + // ++ 和 -- 操作符使变量加或减1。放在变量前面或者后面的区别是整个表达 + // 式的返回值。操作符在前面时,先加减,后取值。操作符在后面时,先取值 + // 后加减。 + System.out.println(i++); // 后自增 i = 1, 输出0 + System.out.println(++i); // 前自增 i = 2, 输出2 + System.out.println(i--); // 后自减 i = 1, 输出2 + System.out.println(--i); // 前自减 i = 0, 输出0 /////////////////////////////////////// // 控制结构 @@ -192,7 +195,7 @@ public class LearnJava { } System.out.println("fooWhile Value: " + fooWhile); - // Do While循环 + // Do While循环 int fooDoWhile = 0; do { @@ -299,14 +302,14 @@ class Bicycle { // 构造函数是初始化一个对象的方式 // 以下是一个默认构造函数 - public Bi450635425cycle() { + public Bicycle() { gear = 1; cadence = 50; speed = 5; name = "Bontrager"; } - // 一下是一个含有参数的构造函数 + // 以下是一个含有参数的构造函数 public Bicycle(int startCadence, int startSpeed, int startGear, String name) { this.gear = startGear; this.cadence = startCadence; @@ -325,7 +328,7 @@ class Bicycle { return cadence; } - // void返450635425回值函数没有返回值 + // void返回值函数没有返回值 public void setCadence(int newValue) { cadence = newValue; } @@ -402,4 +405,4 @@ class PennyFarthing extends Bicycle { * [泛型](http://docs.oracle.com/javase/tutorial/java/generics/index.html) -* [Java代码规范](http://www.oracle.com/technetwork/java/codeconv-138413.html) +* [Java代码规范](http://www.oracle.com/technetwork/java/codeconvtoc-136057.html) diff --git a/zh-cn/javascript-cn.html.markdown b/zh-cn/javascript-cn.html.markdown index 7dee9cc4..bdef0099 100644 --- a/zh-cn/javascript-cn.html.markdown +++ b/zh-cn/javascript-cn.html.markdown @@ -5,17 +5,19 @@ name: javascript filename: javascript-zh.js contributors: - ["Adam Brenecki", "http://adam.brenecki.id.au"] + - ["Ariel Krakowski", "http://www.learneroo.com"] translators: - ["Chenbo Li", "http://binarythink.net"] + - ["Guodong Qu", "https://github.com/jasonqu"] lang: zh-cn --- Javascript于1995年由网景公司的Brendan Eich发明。 最初发明的目的是作为一个简单的网站脚本语言,来作为 -复杂网站应用java的补充。但由于javascript和网站结合度很高 -所以javascript逐渐变得比java在前端更为流行了。 +复杂网站应用java的补充。但由于它与网页结合度很高并且由浏览器内置支持, +所以javascript变得比java在前端更为流行了。 -JavaScript 不仅仅只可以用于浏览器, 也可用于 Node.js 等后台环境。 +不过 JavaScript 可不仅仅只用于浏览器: Node.js,一个基于Google Chrome V8引擎的独立运行时环境,也越来越流行。 很欢迎来自您的反馈,您可以通过下列方式联系到我: [@adambrenecki](https://twitter.com/adambrenecki), 或者 @@ -29,145 +31,167 @@ JavaScript 不仅仅只可以用于浏览器, 也可用于 Node.js 等后台环 // 语句可以以分号结束 doStuff(); -// ... 但是分号也可以省略,每当遇到一个新行时,分号会自动插入 +// ... 但是分号也可以省略,每当遇到一个新行时,分号会自动插入(除了一些特殊情况)。 doStuff() -// 我们在这里会去掉分号,但是否添加最后的分号取决于你个人的习惯 -// 及你所在团队的编程风格 +// 因为这些特殊情况会导致意外的结果,所以我们在这里保留分号。 /////////////////////////////////// // 1. 数字、字符串与操作符 -// Javascript 只有一种数字类型 (即 64位 IEEE 754 双精度浮点). -3 // = 3 -1.5 // = 1.5 +// Javascript 只有一种数字类型(即 64位 IEEE 754 双精度浮点 double)。 +// double 有 52 位表示尾数,足以精确存储大到 9✕10¹⁵ 的整数。 +3; // = 3 +1.5; // = 1.5 -// 所有基本的算数运算 -1 + 1 // = 2 -8 - 1 // = 7 -10 * 2 // = 20 -35 / 5 // = 7 +// 所有基本的算数运算都如你预期。 +1 + 1; // = 2 +0.1 + 0.2; // = 0.30000000000000004 +8 - 1; // = 7 +10 * 2; // = 20 +35 / 5; // = 7 -// 包括无法整除的除法 -5 / 2 // = 2.5 +// 包括无法整除的除法。 +5 / 2; // = 2.5 -// 位运算也和其他语言一样。当你对浮点数进行位运算时, -// 浮点数会转换为至多 32 位的无符号整数 -1 << 2 // = 4 +// 位运算也和其他语言一样;当你对浮点数进行位运算时, +// 浮点数会转换为*至多* 32 位的无符号整数。 +1 << 2; // = 4 -// 括号可以决定优先级 -(1 + 3) * 2 // = 8 +// 括号可以决定优先级。 +(1 + 3) * 2; // = 8 // 有三种非数字的数字类型 -Infinity // 1/0 的结果 --Infinity // -1/0 的结果 -NaN // 0/0 的结果 +Infinity; // 1/0 的结果 +-Infinity; // -1/0 的结果 +NaN; // 0/0 的结果 -// 也有布尔值 -true -false +// 也有布尔值。 +true; +false; -// 可以通过单引号或双引号来构造字符串 -'abc' -"Hello, world" +// 可以通过单引号或双引号来构造字符串。 +'abc'; +"Hello, world"; // 用!来取非 -!true // = false -!false // = true +!true; // = false +!false; // = true -// 相等 == -1 == 1 // = true -2 == 1 // = false +// 相等 === +1 === 1; // = true +2 === 1; // = false // 不等 != -1 != 1 // = false -2 != 1 // = true +1 !== 1; // = false +2 !== 1; // = true // 更多的比较操作符 -1 < 10 // = true -1 > 10 // = false -2 <= 2 // = true -2 >= 2 // = true +1 < 10; // = true +1 > 10; // = false +2 <= 2; // = true +2 >= 2; // = true // 字符串用+连接 -"Hello " + "world!" // = "Hello world!" +"Hello " + "world!"; // = "Hello world!" // 字符串也可以用 < 、> 来比较 -"a" < "b" // = true +"a" < "b"; // = true -// 比较时会进行类型转换... -"5" == 5 // = true +// 使用“==”比较时会进行类型转换... +"5" == 5; // = true +null == undefined; // = true // ...除非你是用 === -"5" === 5 // = false +"5" === 5; // = false +null === undefined; // = false -// 你可以用charAt来得到字符串中的字符 -"This is a string".charAt(0) +// ...但会导致奇怪的行为 +13 + !0; // 14 +"13" + !0; // '13true' -// 还有两个特殊的值:null和undefined -null // 用来表示刻意设置成的空值 -undefined // 用来表示还没有设置的值 +// 你可以用`charAt`来得到字符串中的字符 +"This is a string".charAt(0); // = 'T' -// null, undefined, NaN, 0 和 "" 都是假的(false),其他的都视作逻辑真 -// 注意 0 是逻辑假而 "0"是逻辑真, 尽管 0 == "0". +// ...或使用 `substring` 来获取更大的部分。 +"Hello world".substring(0, 5); // = "Hello" + +// `length` 是一个属性,所以不要使用 (). +"Hello".length; // = 5 + +// 还有两个特殊的值:`null`和`undefined` +null; // 用来表示刻意设置的空值 +undefined; // 用来表示还没有设置的值(尽管`undefined`自身实际是一个值) + +// false, null, undefined, NaN, 0 和 "" 都是假的;其他的都视作逻辑真 +// 注意 0 是逻辑假而 "0"是逻辑真,尽管 0 == "0"。 /////////////////////////////////// // 2. 变量、数组和对象 -// 变量需要用 var 这个关键字声明. Javascript是动态类型语言 -// 所以你在声明时无需指定类型。 赋值需要用 = -var someVar = 5 +// 变量需要用`var`关键字声明。Javascript是动态类型语言, +// 所以你无需指定类型。 赋值需要用 `=` +var someVar = 5; -// 如果你在声明时没有加var关键字,你也不会得到错误 -someOtherVar = 10 +// 如果你在声明时没有加var关键字,你也不会得到错误... +someOtherVar = 10; -// ...但是此时这个变量就会拥有全局的作用域,而非当前作用域 +// ...但是此时这个变量就会在全局作用域被创建,而非你定义的当前作用域 -// 没有被赋值的变量都会返回undefined这个值 -var someThirdVar // = undefined +// 没有被赋值的变量都会被设置为undefined +var someThirdVar; // = undefined -// 对变量进行数学运算有一些简写法 -someVar += 5 // 等价于 someVar = someVar + 5; someVar 现在是 10 -someVar *= 10 // 现在 someVar 是 100 +// 对变量进行数学运算有一些简写法: +someVar += 5; // 等价于 someVar = someVar + 5; someVar 现在是 10 +someVar *= 10; // 现在 someVar 是 100 // 自增和自减也有简写 -someVar++ // someVar 是 101 -someVar-- // 回到 100 +someVar++; // someVar 是 101 +someVar--; // 回到 100 // 数组是任意类型组成的有序列表 -var myArray = ["Hello", 45, true] +var myArray = ["Hello", 45, true]; + +// 数组的元素可以用方括号下标来访问。 +// 数组的索引从0开始。 +myArray[1]; // = 45 -// 数组的元素可以用方括号下标来访问 -// 数组的索引从0开始 -myArray[1] // = 45 +// 数组是可变的,并拥有变量 length。 +myArray.push("World"); +myArray.length; // = 4 -// javascript中的对象相当于其他语言中的字典或映射:是键-值的集合 -{key1: "Hello", key2: "World"} +// 在指定下标添加/修改 +myArray[3] = "Hello"; -// 键是字符串,但是引号也并非是必须的,如果键本身是合法的js标识符 -// 而值则可以是任意类型的值 -var myObj = {myKey: "myValue", "my other key": 4} +// javascript中的对象相当于其他语言中的“字典”或“映射”:是键-值对的无序集合。 +var myObj = {key1: "Hello", key2: "World"}; -// 对象的访问可以通过下标 -myObj["my other key"] // = 4 +// 键是字符串,但如果键本身是合法的js标识符,则引号并非是必须的。 +// 值可以是任意类型。 +var myObj = {myKey: "myValue", "my other key": 4}; + +// 对象属性的访问可以通过下标 +myObj["my other key"]; // = 4 // ... 或者也可以用 . ,如果属性是合法的标识符 -myObj.myKey // = "myValue" +myObj.myKey; // = "myValue" -// 对象是可变的,键和值也可以被更改或增加 -myObj.myThirdKey = true +// 对象是可变的;值也可以被更改或增加新的键 +myObj.myThirdKey = true; -// 如果你想要访问一个还没有被定义的属性,那么会返回undefined -myObj.myFourthKey // = undefined +// 如果你想要获取一个还没有被定义的值,那么会返回undefined +myObj.myFourthKey; // = undefined /////////////////////////////////// // 3. 逻辑与控制结构 -// if语句和其他语言中一样 -var count = 1 +// 本节介绍的语法与Java的语法几乎完全相同 + +// `if`语句和其他语言中一样。 +var count = 1; if (count == 3){ // count 是 3 时执行 -} else if (count == 4) { +} else if (count == 4){ // count 是 4 时执行 } else { // 其他情况下执行 @@ -179,219 +203,270 @@ while (true) { } // Do-while 和 While 循环很像 ,但前者会至少执行一次 -var input +var input; do { - input = getInput() + input = getInput(); } while (!isValid(input)) -// for循环和C、Java中的一样 -// 初始化; 继续执行的条件; 遍历后执行. +// `for`循环和C、Java中的一样: +// 初始化; 继续执行的条件; 迭代。 for (var i = 0; i < 5; i++){ // 遍历5次 } // && 是逻辑与, || 是逻辑或 if (house.size == "big" && house.colour == "blue"){ - house.contains = "bear" + house.contains = "bear"; } if (colour == "red" || colour == "blue"){ // colour是red或者blue时执行 } -// && 和 || 是“短路”语句,在初始化值时会变得有用 -var name = otherName || "default" +// && 和 || 是“短路”语句,它在设定初始化值时特别有用 +var name = otherName || "default"; + +// `switch`语句使用`===`检查相等性。 +// 在每一个case结束时使用 'break' +// 否则其后的case语句也将被执行。 +grade = 'B'; +switch (grade) { + case 'A': + console.log("Great job"); + break; + case 'B': + console.log("OK job"); + break; + case 'C': + console.log("You can do better"); + break; + default: + console.log("Oy vey"); + break; +} /////////////////////////////////// // 4. 函数、作用域、闭包 -// JavaScript 函数由function关键字定义 +// JavaScript 函数由`function`关键字定义 function myFunction(thing){ - return thing.toUpperCase() + return thing.toUpperCase(); } -myFunction("foo") // = "FOO" - -// 函数也可以是匿名的: -function(thing){ - return thing.toLowerCase() +myFunction("foo"); // = "FOO" + +// 注意被返回的值必须开始于`return`关键字的那一行, +// 否则由于自动的分号补齐,你将返回`undefined`。 +// 在使用Allman风格的时候要注意. +function myFunction() +{ + return // <- 分号自动插在这里 + { + thisIsAn: 'object literal' + } } -// (我们无法调用此函数,因为我们不知道这个函数的名字) +myFunction(); // = undefined -// javascript中的函数也是对象,所以函数也能够赋给一个变量,并且被传递 -// 比如一个事件处理函数: +// javascript中函数是一等对象,所以函数也能够赋给一个变量, +// 并且被作为参数传递 —— 比如一个事件处理函数: function myFunction(){ - // this code will be called in 5 seconds' time + // 这段代码将在5秒钟后被调用 } -setTimeout(myFunction, 5000) - -// 你甚至可以直接把一个函数写到另一个函数的参数中 +setTimeout(myFunction, 5000); +// 注意:setTimeout不是js语言的一部分,而是由浏览器和Node.js提供的。 -setTimeout(function myFunction(){ - // 5秒之后会执行这里的代码 -}, 5000) +// 函数对象甚至不需要声明名称 —— 你可以直接把一个函数定义写到另一个函数的参数中 +setTimeout(function(){ + // 这段代码将在5秒钟后被调用 +}, 5000); -// JavaScript 仅有函数作用于,而其他的语句则没有作用域 +// JavaScript 有函数作用域;函数有其自己的作用域而其他的代码块则没有。 if (true){ - var i = 5 + var i = 5; } -i // = 5 - 并非我们在其他语言中所得到的undefined - -// 这就导致了人们经常用一种叫做“即使执行匿名函数”的模式 -// 这样可以避免一些临时变量扩散到外边去 -function(){ - var temporary = 5 - // 我们可以访问一个全局对象来访问全局作用域 - // 在浏览器中是 'window' 这个对象。 - // 在Node.js中这个对象的名字可能会不同。 - window.permanent = 10 - // 或者,我们也可以把var去掉就行了 - permanent2 = 15 -}() -temporary // 抛出引用异常 -permanent // = 10 -permanent2 // = 15 - -// javascript最强大的功能之一就是闭包 -// 如果一个函数在另一个函数中定义,那么这个函数就拥有外部函数的所有访问权 +i; // = 5 - 并非我们在其他语言中所期望得到的undefined + +// 这就导致了人们经常使用的“立即执行匿名函数”的模式, +// 这样可以避免一些临时变量扩散到全局作用域去。 +(function(){ + var temporary = 5; + // 我们可以访问修改全局对象("global object")来访问全局作用域, + // 在web浏览器中是`window`这个对象。 + // 在其他环境如Node.js中这个对象的名字可能会不同。 + window.permanent = 10; +})(); +temporary; // 抛出引用异常ReferenceError +permanent; // = 10 + +// javascript最强大的功能之一就是闭包。 +// 如果一个函数在另一个函数中定义,那么这个内部函数就拥有外部函数的所有变量的访问权, +// 即使在外部函数结束之后。 function sayHelloInFiveSeconds(name){ - var prompt = "Hello, " + name + "!" + var prompt = "Hello, " + name + "!"; + // 内部函数默认是放在局部作用域的, + // 就像是用`var`声明的。 function inner(){ - alert(prompt) + alert(prompt); } - setTimeout(inner, 5000) - // setTimeout 是异步的,所以这个函数会马上终止不会等待。 - // 然而,在5秒结束后,inner函数仍然会弹出prompt信息。 + setTimeout(inner, 5000); + // setTimeout是异步的,所以 sayHelloInFiveSeconds 函数会立即退出, + // 而 setTimeout 会在后面调用inner + // 然而,由于inner是由sayHelloInFiveSeconds“闭合包含”的, + // 所以inner在其最终被调用时仍然能够访问`prompt`变量。 } -sayHelloInFiveSeconds("Adam") // 会在5秒后弹出 "Hello, Adam!" +sayHelloInFiveSeconds("Adam"); // 会在5秒后弹出 "Hello, Adam!" + /////////////////////////////////// // 5. 对象、构造函数与原型 -// 对象包含方法 +// 对象可以包含方法。 var myObj = { myFunc: function(){ - return "Hello world!" + return "Hello world!"; } -} -myObj.myFunc() // = "Hello world!" +}; +myObj.myFunc(); // = "Hello world!" -// 当对象中的函数被调用时,这个函数就可以通过this关键字访问这个对象 +// 当对象中的函数被调用时,这个函数可以通过`this`关键字访问其依附的这个对象。 myObj = { myString: "Hello world!", myFunc: function(){ - return this.myString + return this.myString; } -} -myObj.myFunc() // = "Hello world!" +}; +myObj.myFunc(); // = "Hello world!" -// 但这个函数访问的其实是其运行时环境,而非定义时环境 -// 所以如果函数所在的环境不在当前对象的环境中运行时,就运行不成功了 -var myFunc = myObj.myFunc -myFunc() // = undefined +// 但这个函数访问的其实是其运行时环境,而非定义时环境,即取决于函数是如何调用的。 +// 所以如果函数被调用时不在这个对象的上下文中,就不会运行成功了。 +var myFunc = myObj.myFunc; +myFunc(); // = undefined -// 相应的,一个函数也可以被指定为一个对象的方法,并且用过this可以访问 -// 这个对象的成员,即使在定义时并没有绑定任何值 +// 相应的,一个函数也可以被指定为一个对象的方法,并且可以通过`this`访问 +// 这个对象的成员,即使在函数被定义时并没有依附在对象上。 var myOtherFunc = function(){ - return this.myString.toUpperCase() + return this.myString.toUpperCase(); +} +myObj.myOtherFunc = myOtherFunc; +myObj.myOtherFunc(); // = "HELLO WORLD!" + +// 当我们通过`call`或者`apply`调用函数的时候,也可以为其指定一个执行上下文。 +var anotherFunc = function(s){ + return this.myString + s; } -myObj.myOtherFunc = myOtherFunc -myObj.myOtherFunc() // = "HELLO WORLD!" +anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!" + +// `apply`函数几乎完全一样,只是要求一个array来传递参数列表。 +anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!" -// 当你通过new关键字调用一个函数时,就会生成一个对象 -// 而对象的成员需要通过this来定义。 -// 这样的函数就叫做构造函数 +// 当一个函数接受一系列参数,而你想传入一个array时特别有用。 +Math.min(42, 6, 27); // = 6 +Math.min([42, 6, 27]); // = NaN (uh-oh!) +Math.min.apply(Math, [42, 6, 27]); // = 6 +// 但是`call`和`apply`只是临时的。如果我们希望函数附着在对象上,可以使用`bind`。 +var boundFunc = anotherFunc.bind(myObj); +boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!" + +// `bind` 也可以用来部分应用一个函数(柯里化)。 +var product = function(a, b){ return a * b; } +var doubler = product.bind(this, 2); +doubler(8); // = 16 + +// 当你通过`new`关键字调用一个函数时,就会创建一个对象, +// 而且可以通过this关键字访问该函数。 +// 设计为这样调用的函数就叫做构造函数。 var MyConstructor = function(){ - this.myNumber = 5 + this.myNumber = 5; } -myNewObj = new MyConstructor() // = {myNumber: 5} -myNewObj.myNumber // = 5 +myNewObj = new MyConstructor(); // = {myNumber: 5} +myNewObj.myNumber; // = 5 -// 每一个js对象都有一个原型,当你要访问一个没有定义过的成员时, -// 解释器就回去找这个对象的原型 +// 每一个js对象都有一个‘原型’。当你要访问一个实际对象中没有定义的一个属性时, +// 解释器就回去找这个对象的原型。 -// 有一些JS实现会让你通过一个对象的__proto__方法访问这个原型。 -// 这虽然对理解这个对象很有用,但是这并不是标准的一部分 -// 我们之后会通过标准方式来访问原型。 +// 一些JS实现会让你通过`__proto__`属性访问一个对象的原型。 +// 这虽然对理解原型很有用,但是它并不是标准的一部分; +// 我们后面会介绍使用原型的标准方式。 var myObj = { - myString: "Hello world!", -} + myString: "Hello world!" +}; var myPrototype = { meaningOfLife: 42, myFunc: function(){ return this.myString.toLowerCase() } -} -myObj.__proto__ = myPrototype -myObj.meaningOfLife // = 42 +}; + +myObj.__proto__ = myPrototype; +myObj.meaningOfLife; // = 42 -// This works for functions, too. +// 函数也可以工作。 myObj.myFunc() // = "hello world!" -// 当然,如果你要访问的成员在原型当中也没有定义的话,解释器就会去找原型的原型。 +// 当然,如果你要访问的成员在原型当中也没有定义的话,解释器就会去找原型的原型,以此类推。 myPrototype.__proto__ = { myBoolean: true -} -myObj.myBoolean // = true - -// 这其中并没有对象的拷贝。每个对象的原型实际上是持有原型对象的引用 -// 这说明当我们改变对象的原型时,会影响到其他以这个原型为原型的对象 -myPrototype.meaningOfLife = 43 -myObj.meaningOfLife // = 43 - -// 我们知道 __proto__ 并非标准规定,实际上也没有办法更改已经指定好的原型。 -// 但是,我们有两种方式可以为新的对象指定原型。 - -// 第一种方式是 Object.create,这个方法是在最近才被添加到Js中的 -// 也因此并不是所有的JS实现都有这个放啊 -var myObj = Object.create(myPrototype) -myObj.meaningOfLife // = 43 - -// 第二种方式可以在任意版本中使用,不过需要通过构造函数。 -// 构造函数有一个属性prototype。但是这 *不是* 构造函数本身的函数 -// 而是通过构造函数和new关键字生成新对象时自动生成的。 -myConstructor.prototype = { +}; +myObj.myBoolean; // = true + +// 这其中并没有对象的拷贝;每个对象实际上是持有原型对象的引用。 +// 这意味着当我们改变对象的原型时,会影响到其他以这个原型为原型的对象。 +myPrototype.meaningOfLife = 43; +myObj.meaningOfLife; // = 43 + +// 我们知道 `__proto__` 并非标准规定,实际上也没有标准办法来修改一个已存在对象的原型。 +// 然而,我们有两种方式为指定原型创建一个新的对象。 + +// 第一种方式是 Object.create,这个方法是在最近才被添加到Js中的, +// 因此并不是所有的JS实现都有这个方法 +var myObj = Object.create(myPrototype); +myObj.meaningOfLife; // = 43 + +// 第二种方式可以在任意版本中使用,不过必须通过构造函数。 +// 构造函数有一个属性prototype。但是它 *不是* 构造函数本身的原型;相反, +// 是通过构造函数和new关键字创建的新对象的原型。 +MyConstructor.prototype = { + myNumber: 5, getMyNumber: function(){ - return this.myNumber + return this.myNumber; } -} -var myNewObj2 = new myConstructor() -myNewObj2.getMyNumber() // = 5 +}; +var myNewObj2 = new MyConstructor(); +myNewObj2.getMyNumber(); // = 5 +myNewObj2.myNumber = 6 +myNewObj2.getMyNumber(); // = 6 // 字符串和数字等内置类型也有通过构造函数来创建的包装类型 -var myNumber = 12 -var myNumberObj = new Number(12) -myNumber == myNumberObj // = true +var myNumber = 12; +var myNumberObj = new Number(12); +myNumber == myNumberObj; // = true // 但是它们并非严格等价 -typeof myNumber // = 'number' -typeof myNumberObj // = 'object' -myNumber === myNumberObj // = false +typeof myNumber; // = 'number' +typeof myNumberObj; // = 'object' +myNumber === myNumberObj; // = false if (0){ // 这段代码不会执行,因为0代表假 } -if (Number(0)){ - // 这段代码会执行,因为Number(0)代表真 -} -// 但是,包装类型和内置类型共享一个原型 -// 这样你就可以给内置类型也增加一些功能 +// 不过,包装类型和内置类型共享一个原型, +// 所以你实际可以给内置类型也增加一些功能,例如对string: String.prototype.firstCharacter = function(){ - return this.charAt(0) + return this.charAt(0); } -"abc".firstCharacter() // = "a" +"abc".firstCharacter(); // = "a" -// 这个技巧可以用来用老版本的javascript子集来是实现新版本js的功能 +// 这个技巧经常用在“代码填充”中,来为老版本的javascript子集增加新版本js的特性, // 这样就可以在老的浏览器中使用新功能了。 -// 比如,我们知道Object.create并没有在所有的版本中都实现 -// 但是我们仍然可以通过这个技巧来使用 +// 比如,我们知道Object.create并没有在所有的版本中都实现, +// 但是我们仍然可以通过“代码填充”来实现兼容: if (Object.create === undefined){ // 如果存在则不覆盖 Object.create = function(proto){ // 用正确的原型来创建一个临时构造函数 - var Constructor = function(){} - Constructor.prototype = proto + var Constructor = function(){}; + Constructor.prototype = proto; // 之后用它来创建一个新的对象 - return new Constructor() + return new Constructor(); } } ``` @@ -399,19 +474,23 @@ if (Object.create === undefined){ // 如果存在则不覆盖 ## 更多阅读 [Mozilla 开发者 -网络](https://developer.mozilla.org/en-US/docs/Web/JavaScript) 提供了很好的 -Javascript文档,并且由于是wiki,所以你也可以自行编辑来分享你的知识。 +网络](https://developer.mozilla.org/en-US/docs/Web/JavaScript) 提供了优秀的介绍 +Javascript如何在浏览器中使用的文档。而且它是wiki,所以你也可以自行编辑来分享你的知识。 MDN的 [A re-introduction to JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript) -覆盖了这里提到的绝大多数话题,大多数只是Javascript这个语言本身。 +覆盖了这里提到的绝大多数话题的细节。该导引的大多数内容被限定在只是Javascript这个语言本身; 如果你想了解Javascript是如何在网页中被应用的,那么可以查看 [Document Object Model](https://developer.mozilla.org/en-US/docs/Using_the_W3C_DOM_Level_1_Core) +[Learn Javascript by Example and with Challenges](http://www.learneroo.com/modules/64/nodes/350) 是本参考的另一个版本,并包含了挑战习题。 + [Javascript Garden](http://bonsaiden.github.io/JavaScript-Garden/) 是一个深入 -讲解所有Javascript反直觉部分的一本书 +讲解所有Javascript反直觉部分的导引。 + +[JavaScript: The Definitive Guide](http://www.amazon.com/gp/product/0596805527/) 是一个经典的指导参考书。 除了这篇文章的直接贡献者之外,这篇文章也参考了这个网站上 Louie Dinh 的 Python 教程,以及 Mozilla开发者网络上的[JS -Tutorial](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript) +Tutorial](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)。 diff --git a/zh-cn/jquery-cn.html.markdown b/zh-cn/jquery-cn.html.markdown new file mode 100644 index 00000000..4b23274e --- /dev/null +++ b/zh-cn/jquery-cn.html.markdown @@ -0,0 +1,131 @@ +--- +category: tool +tool: jquery +contributors: + - ["Sawyer Charles", "https://github.com/xssc"] +translators: + - ["zxyqwe", "https://github.com/zxyqwe"] +lang: zh-cn +filename: jquery-cn.js +--- + +jQuery是JavaScript的一个函数库,它可以帮你“写更少,做更多”。它集成了很多常见的JavaScript任务并且很容易调用。jQuery被世界各地的很多的大公司和开发者使用。它包括了AJAX,事件处理,文档操作以及很多其它功能,并且更加简单和快速。 + +正因为jQuery是JavaScript的一个函数库,所以你需要[首先学习JavaScript](https://learnxinyminutes.com/docs/javascript/) + +```js + + +/////////////////////////////////// +// 1. 选择器 + +// jQuery中的选择器被用来选择一个元素 +var page = $(window); // 选择整个视窗 + +// 选择器可以作为CSS选择器使用 +var paragraph = $('p'); // 选择所有段落元素 +var table1 = $('#table1'); // 选择id为table1的元素 +var squares = $('.square'); // 选择所有类是square的元素 +var square_p = $('p.square') // 选择具有square类的所有段落 + + +/////////////////////////////////// +// 2. 事件和效果 +// jQuery非常善于处理当事件触发的时候应该做什么 +// 一个非常常见的事件就是文档的就绪事件 +// 你可以用ready方法,在所有元素完成加载的时候执行 +$(document).ready(function(){ + // 只有文档加载完成以后代码才会执行 +}); +// 你也可以用定义了的函数 +function onAction() { + // 本函数在事件触发的时候被执行 +} +$('#btn').click(onAction); // 当点击的时候调用onAction函数 + +// 其它常见的事件: +$('#btn').dblclick(onAction); // 双击 +$('#btn').hover(onAction); // 划过 +$('#btn').focus(onAction); // 聚焦 +$('#btn').blur(onAction); // 失焦 +$('#btn').submit(onAction); // 提交 +$('#btn').select(onAction); // 当元素被选中 +$('#btn').keydown(onAction); // 当一个按键被按下 +$('#btn').keyup(onAction); // 当一个按键被抬起 +$('#btn').keypress(onAction); // 当一个按键被按住 +$('#btn').mousemove(onAction); // 当鼠标在移动 +$('#btn').mouseenter(onAction); // 鼠标移入元素 +$('#btn').mouseleave(onAction); // 鼠标离开元素 + + +// 如果不提供任何参数的话,那么这些方法可以触发事件 +// 而不是定义处理事件的方法 +$('#btn').dblclick(); // 触发元素上的双击 + +// 你可以只用选择器一次而处理多个事件 +$('#btn').on( + {dblclick: myFunction1} // 双击的时候触发 + {blur: myFunction1} // 失焦的时候触发 +); + +// 你可以用一些效果函数来移动或隐藏元素 +$('.table').hide(); // 隐藏元素 + +// 注意:在这些方法中调用函数会仍然隐藏元素 +$('.table').hide(function(){ + // 元素先隐藏然后函数被执行 +}); + +// 你可以在变量中储存选择器 +var tables = $('.table'); + +// 一些基本的文档操作方法有: +tables.hide(); // 隐藏元素 +tables.show(); // 显示元素 +tables.toggle(); // 对被选元素进行隐藏和显示的切换 +tables.fadeOut(); // 淡出 +tables.fadeIn(); // 淡入 +tables.fadeToggle(); // 对被选元素进行淡入和淡出显示的切换 +tables.fadeTo(0.5); // 把被选元素逐渐改变至给定的不透明度(0和1之间) +tables.slideUp(); // 通过调整高度来滑动隐藏被选元素 +tables.slideDown(); // 对被选元素进行滑动隐藏和滑动显示的切换 +tables.slideToggle(); // 对被选元素进行滑动隐藏和滑动显示的切换 + +// 上面所有的方法接受速度参数(毫秒)和一个回调函数 +tables.hide(1000, myFunction); // 持续一秒的隐藏动画然后执行函数 + +// fadeTo要求提供透明度参数作为第二个参数 +tables.fadeTo(2000, 0.1, myFunction); // 通过2秒钟将透明度变为0.1然后执行函数 + +// 你可以用animate方法实现一些略微高级的效果 +tables.animate({margin-top:"+=50", height: "100px"}, 500, myFunction); +// animate方法接受一个包含CSS和值的对象作为目标, +// 其次是可选的速度参数, +// 以及最后的回调函数 + +/////////////////////////////////// +// 3. 操作 + +// 这些类似效果函数但是可以做更多 +$('div').addClass('taming-slim-20'); // 给所有div添加类taming-slim-20 + +// 常见操作方法 +$('p').append('Hello world'); // 添加到元素末尾 +$('p').attr('class'); // 获取属性 +$('p').attr('class', 'content'); // 设置属性 +$('p').hasClass('taming-slim-20'); // 如果有类则为真 +$('p').height(); // 获取和设置元素的高度 + + +// 对于很多的操作函数来说,获取元素的信息 +// 仅仅是第一个符合元素的 +$('p').height(); // 仅仅获取第一个p标签的高度 + +// 你可以用each来迭代所有元素 +var heights = []; +$('p').each(function() { + heights.push($(this).height()); // 把所有p标签的高度加入数组 +}); + + +``` diff --git a/zh-cn/json-cn.html.markdown b/zh-cn/json-cn.html.markdown index 3a8db2cf..73d3eb57 100644 --- a/zh-cn/json-cn.html.markdown +++ b/zh-cn/json-cn.html.markdown @@ -8,12 +8,21 @@ filename: learnjson-cn.json lang: zh-cn --- -因为JSON是一个极其简单的数据交换形式,这个最有可能将会是曾经最简单 -的Learn X in Y Minutes。 +因为JSON是一个极其简单的数据交换格式,本教程最有可能成为有史以来最简单的 +Learn X in Y Minutes。 -最纯正形式的JSON没有实际的注解,但是大多数解析器将会 -接受C-风格(//, /\* \*/)的注解。为了这个目的,但是, -一切都将会是100%有效的JSON。幸亏,它是不言自明的。 +纯正的JSON实际上没有注释,但是大多数解析器都 +接受C-风格(//, /\* \*/)的注释。为了兼容性,最好不要在其中写这样形式的注释。 + +因此,本教程的一切都会是100%有效的JSON。幸亏,它的表达能力很丰富。 + +支持的数据类型: + +- 字符串: "hello", "\"A quote.\"", "\u0abe", "Newline.\n" +- 数字: 23, 0.11, 12e10, 3.141e-10, 1.23e+4 +- 对象: { "key": "value" } +- 数组: ["Values"] +- 其他: true, false, null ```json { diff --git a/zh-cn/kotlin-cn.html.markdown b/zh-cn/kotlin-cn.html.markdown new file mode 100644 index 00000000..5d655029 --- /dev/null +++ b/zh-cn/kotlin-cn.html.markdown @@ -0,0 +1,346 @@ +--- +language: kotlin +contributors: + - ["S Webber", "https://github.com/s-webber"] +translators: + - ["Jimin Lu", "https://github.com/lujimin"] +filename: LearnKotlin-cn.kt +lang: zh-cn +--- + +Kotlin是一门适用于JVM、Android和浏览器的静态类型编程语言。它100%兼容Java。 +[了解更多。](https://kotlinlang.org/) + +```java +// 单行注释从 // 开始 +/* +多行注释看起来像这样。 +*/ + +// "package" 关键字的工作方式与Java相同。 +package com.learnxinyminutes.kotlin + +/* +Kotlin程序的入口点是一个"main"函数 +该函数传递一个包含任何命令行参数的数组。 +*/ +fun main(args: Array<String>) { + /* + 使用"var"或"val"来声明一个值。 + "val"声明的值不能被重新赋值,而"var"声明的值可以。 + */ + val fooVal = 10 // 以后我们不能再次给fooVal赋值 + var fooVar = 10 + fooVar = 20 // fooVar可以被再次赋值 + + /* + 在大多数情况下,Kotlin可以确定变量的类型是什么, + 所以我们不必要每次都去明确指定它。 + 我们可以像这样明确地声明一个变量的类型: + */ + val foo: Int = 7 + + /* + 可以采取和Java类似的方法来表示一个字符串。 + 用反斜杠来转义字符。 + */ + val fooString = "My String Is Here!"; + val barString = "Printing on a new line?\nNo Problem!"; + val bazString = "Do you want to add a tab?\tNo Problem!"; + println(fooString); + println(barString); + println(bazString); + + /* + 原始字符串用三重引号(""")来定义。 + 原始字符串可以包含换行符以及其他任何字符。 + */ + val fooRawString = """ +fun helloWorld(val name : String) { + println("Hello, world!") +} +""" + println(fooRawString) + + /* + 字符串可以包含模板表达式。 + 模板表达式从一个美元符号($)开始。 + */ + val fooTemplateString = "$fooString has ${fooString.length} characters" + println(fooTemplateString) + + /* + 当某个变量的值可以为 null 的时候,我们必须被明确指定它是可为空的。 + 在变量声明处的类型后面加上?来标识它是可为空的。 + 我们可以用?.操作符来访问可为空的变量。 + 我们可以用?:操作符来指定一个在变量为空时使用的替代值。 + */ + var fooNullable: String? = "abc" + println(fooNullable?.length) // => 3 + println(fooNullable?.length ?: -1) // => 3 + fooNullable = null + println(fooNullable?.length) // => null + println(fooNullable?.length ?: -1) // => -1 + + /* + 使用"fun"关键字来声明一个函数。 + 函数的参数在函数名后面的括号内指定。 + 函数的参数可以设定一个默认值。 + 如果需要的话,函数的返回值类型可以在参数后面指定。 + */ + fun hello(name: String = "world") : String { + return "Hello, $name!" + } + println(hello("foo")) // => Hello, foo! + println(hello(name = "bar")) // => Hello, bar! + println(hello()) // => Hello, world! + + /* + 用"vararg"关键字来修饰一个函数的参数来允许可变参数传递给该函数 + */ + fun varargExample(vararg names: Int) { + println("Argument has ${names.size} elements") + } + varargExample() // => Argument has 0 elements + varargExample(1) // => Argument has 1 elements + varargExample(1, 2, 3) // => Argument has 3 elements + + /* + 当函数只包含一个单独的表达式时,大括号可以被省略。 + 函数体可以被指定在一个=符号后面。 + */ + fun odd(x: Int): Boolean = x % 2 == 1 + println(odd(6)) // => false + println(odd(7)) // => true + + // 如果返回值类型可以被推断,那么我们不需要指定它。 + fun even(x: Int) = x % 2 == 0 + println(even(6)) // => true + println(even(7)) // => false + + // 函数可以用函数作为参数并且可以返回函数。 + fun not(f: (Int) -> Boolean) : (Int) -> Boolean { + return {n -> !f.invoke(n)} + } + // 命名函数可以用::运算符被指定为参数。 + val notOdd = not(::odd) + val notEven = not(::even) + // 匿名函数可以被指定为参数。 + val notZero = not {n -> n == 0} + /* + 如果一个匿名函数只有一个参数 + 那么它的声明可以被省略(连同->)。 + 这个参数的名字是"it"。 + */ + val notPositive = not {it > 0} + for (i in 0..4) { + println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}") + } + + // "class"关键字用来声明类。 + class ExampleClass(val x: Int) { + fun memberFunction(y: Int) : Int { + return x + y + } + + infix fun infixMemberFunction(y: Int) : Int { + return x * y + } + } + /* + 我们调用构造方法来创建一个新的实例。 + 注意,Kotlin没有"new"关键字。 + */ + val fooExampleClass = ExampleClass(7) + // 可以使用一个点号来调用成员函数。 + println(fooExampleClass.memberFunction(4)) // => 11 + /* + 如果使用"infix"关键字来标记一个函数 + 那么可以使用中缀表示法来调用该函数。 + */ + println(fooExampleClass infixMemberFunction 4) // => 28 + + /* + 数据类是创建只包含数据的类的一个简洁的方法。 + "hashCode"、"equals"和"toString"方法将被自动生成。 + */ + data class DataClassExample (val x: Int, val y: Int, val z: Int) + val fooData = DataClassExample(1, 2, 4) + println(fooData) // => DataClassExample(x=1, y=2, z=4) + + // 数据类有一个"copy"函数 + val fooCopy = fooData.copy(y = 100) + println(fooCopy) // => DataClassExample(x=1, y=100, z=4) + + // 对象可以被解构成为多个变量 + val (a, b, c) = fooCopy + println("$a $b $c") // => 1 100 4 + + // "with"函数类似于JavaScript中的"with"用法。 + data class MutableDataClassExample (var x: Int, var y: Int, var z: Int) + val fooMutableData = MutableDataClassExample(7, 4, 9) + with (fooMutableData) { + x -= 2 + y += 2 + z-- + } + println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8) + + /* + 我们可以使用"listOf"函数来创建一个list。 + 这个list是不可变的 - 元素不可以被添加或删除。 + */ + val fooList = listOf("a", "b", "c") + println(fooList.size) // => 3 + println(fooList.first()) // => a + println(fooList.last()) // => c + // 可以通过索引来访问list中的元素。 + println(fooList[1]) // => b + + // 可以使用"mutableListOf"函数来创建一个可变的list。 + val fooMutableList = mutableListOf("a", "b", "c") + fooMutableList.add("d") + println(fooMutableList.last()) // => d + println(fooMutableList.size) // => 4 + + // 我们可以使用"setOf"函数来创建一个set。 + val fooSet = setOf("a", "b", "c") + println(fooSet.contains("a")) // => true + println(fooSet.contains("z")) // => false + + // 我们可以使用"mapOf"函数来创建一个map。 + val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9) + // 可以通过键来访问map中的值。 + println(fooMap["a"]) // => 8 + + /* + 序列表示惰性求值集合。 + 我们可以使用"generateSequence"函数来创建一个序列。 + */ + val fooSequence = generateSequence(1, {it + 1}) + val x = fooSequence.take(10).toList() + println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + // 一个用序列来生成斐波那契数列的例子。 + fun fibonacciSequence() : Sequence<Long> { + var a = 0L + var b = 1L + + fun next() : Long { + val result = a + b + a = b + b = result + return a + } + + return generateSequence(::next) + } + val y = fibonacciSequence().take(10).toList() + println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + + // Kotlin为集合提供高阶函数。 + val z = (1..9).map {it * 3} + .filter {it < 20} + .groupBy {it % 2 == 0} + .mapKeys {if (it.key) "even" else "odd"} + println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]} + + // 任何提供迭代器的都可以使用"for"循环。 + for (c in "hello") { + println(c) + } + + // "while"循环的用法和其他语言一样。 + var ctr = 0 + while (ctr < 5) { + println(ctr) + ctr++ + } + do { + println(ctr) + ctr++ + } while (ctr < 10) + + // "when"可以用来替代"if-else if"链。 + val i = 10 + when { + i < 7 -> println("first block") + fooString.startsWith("hello") -> println("second block") + else -> println("else block") + } + + // "when"可以带参数。 + when (i) { + 0, 21 -> println("0 or 21") + in 1..20 -> println("in the range 1 to 20") + else -> println("none of the above") + } + + // "when"可以作为一个函数,提供返回值。 + var result = when (i) { + 0, 21 -> "0 or 21" + in 1..20 -> "in the range 1 to 20" + else -> "none of the above" + } + println(result) + + /* + 我们可以通过使用"is"操作符来检查一个对象是否是某个类型的。 + 如果对象通过了类型检查那么它可以作为该类型使用而不需要强制转换它。 + */ + fun smartCastExample(x: Any) : Boolean { + if (x is Boolean) { + // x自动转换为Boolean + return x + } else if (x is Int) { + // x自动转换为Int + return x > 0 + } else if (x is String) { + // x自动转换为String + return x.isNotEmpty() + } else { + return false + } + } + println(smartCastExample("Hello, world!")) // => true + println(smartCastExample("")) // => false + println(smartCastExample(5)) // => true + println(smartCastExample(0)) // => false + println(smartCastExample(true)) // => true + + /* + 扩展是用来给一个类添加新的功能的。 + 它类似于C#的扩展方法。 + */ + fun String.remove(c: Char): String { + return this.filter {it != c} + } + println("Hello, world!".remove('l')) // => Heo, word! + + println(EnumExample.A) // => A + println(ObjectExample.hello()) // => hello +} + +// 枚举类和Java的枚举类型类似。 +enum class EnumExample { + A, B, C +} + +/* +"object"关键字用来创建单例对象。 +我们不能把它赋给一个变量,但我们可以通过它的名字引用它。 +这类似于Scala的单例对象。 +*/ +object ObjectExample { + fun hello() : String { + return "hello" + } +} + +``` + +### 进一步阅读 + +* [Kotlin教程](https://kotlinlang.org/docs/tutorials/) +* [在您的浏览器中使用Kotlin](http://try.kotlinlang.org/) +* [Kotlin资源列表](http://kotlin.link/) diff --git a/zh-cn/latex-cn.html.markdown b/zh-cn/latex-cn.html.markdown new file mode 100644 index 00000000..83491acd --- /dev/null +++ b/zh-cn/latex-cn.html.markdown @@ -0,0 +1,277 @@ +--- +language: latex +contributors: + - ["Chaitanya Krishna Ande", "http://icymist.github.io"] + - ["Colton Kohnke", "http://github.com/voltnor"] + - ["Sricharan Chiruvolu", "http://sricharan.xyz"] + - ["Ramanan Balakrishnan", "https://github.com/ramananbalakrishnan"] + - ["Svetlana Golubeva", "https://attillax.github.io/"] +translators: + - ["Dp Leo", "https://github.com/minoriwww"] +filename: learn-latex-cn.tex +lang: zh-cn +--- + +```tex +% 所有的注释行以 % 开头 +% 没有多行注释语法 + +% LaTeX 不是一个“所见即所得” 的文字处理软件 +% 这与 MS Word,和 OpenOffice Writer 不同 + +% 每一个LaTeX命令由反斜线 (\) 开始 + +% LaTeX 文档以对编译对象文档的定义开始 +% 这些文档包括书籍,报告,演示等 +% 文档的选项出现在中括号里 +% 下例中,我们设定文章字体为12pt +\documentclass[12pt]{article} + +% 之后我们定义该文档所用的库 +% 如果想要引入图片,彩色字,或是其他语言的源码在您的文档中 +% 您需要增强 LaTeX 的功能。这将通过添加库来实现 +% 下例中将要为展示数据引入 float 和 caption 库 +% 为超链接引入 hyperref 库 +\usepackage{caption} +\usepackage{float} +\usepackage{hyperref} + +% 我们还可以定义其他文档属性! +\author{Chaitanya Krishna Ande, Colton Kohnke, Sricharan Chiruvolu \& \\ +Svetlana Golubeva} +\date{\today} +\title{Learn \LaTeX \hspace{1pt} in Y Minutes!} + +% 现在我们开始正文 +% 这一行之前都是“序章” +\begin{document} +% 如果想设定作者,时间,标题字段我们可使用 LaTeX 来建立标题页 +\maketitle + +% 分章节时,可以建立目录 +% 我们需要编译文档两次来保证他们顺序正确 +% 使用目录来分开文档是很好的做法 +% 这里我们使用 \newpage 操作符 +\newpage +\tableofcontents + +\newpage + +% 许多研究论文有摘要部分。这可以使用预定义的指令来实现 +% 它应被放在逻辑上正确的位置,即顶部标题等的下面和文章主体的上面 +% 该指令可以再报告和文章中使用 +\begin{abstract} + \LaTeX \hspace{1pt} documentation written as \LaTeX! How novel and totally not + my idea! +\end{abstract} + +% 章节指令非常直观 +% 所有章节标题会自动地添加到目录中 +\section{Introduction} +Hello, my name is Colton and together we're going to explore \LaTeX! + +\section{Another section} +This is the text for another section. I think it needs a subsection. + +\subsection{This is a subsection} % 子章节同样非常直观 +I think we need another one + +\subsubsection{Pythagoras} +Much better now. +\label{subsec:pythagoras} + +% 使用型号我们可以借助 LaTeX 内置的编号功能 +% 这一技巧也在其他指令中有效 +\section*{This is an unnumbered section} +然而并不是所有章节都要被标序号 + +\section{Some Text notes} +%\section{Spacing} % 需要增加有关空白间隔的信息 +\LaTeX \hspace{1pt} is generally pretty good about placing text where it should +go. If +a line \\ needs \\ to \\ break \\ you add \textbackslash\textbackslash +\hspace{1pt} to the source code. \\ + +\section{Lists} +Lists are one of the easiest things to create in \LaTeX! I need to go shopping +tomorrow, so let's make a grocery list. +\begin{enumerate} % 此处创建了一个“枚举”环境 + % \item 使枚举增加一个单位 + \item Salad. + \item 27 watermelon. + \item A single jackrabbit. + % 我们甚至可以通过使用 [] 覆盖美剧的数量 + \item[how many?] Medium sized squirt guns. + + Not a list item, but still part of the enumerate. + +\end{enumerate} % 所有环境都有终止符 + +\section{Math} + +使用 \LaTeX \hspace{1pt} 的一个最主要的方面是学术论文和技术文章 +通常在数学和科学的领域 +因此我们需要在文章中插入特殊符号! \\ + +数学符号极多,远超出你能在键盘上找到的那些; +集合关系符,箭头,操作符,希腊字符等等 \\ + +集合与关系在数学文章中很重要 +如声明所有 x 属于 X $\forall$ x $\in$ X. \\ +% 注意我们需要在这些符号之前和之后增加 $ 符号 +% 因为在编写时我们处于 text-mode,然而数学符号只在 math-mode 中存在 +% text mode 进入 math-mode 使用 $ 操作符 +% 反之亦然,变量同时会在 math-mode 中被渲染。 +% 我们也可以使用 \[\] 来进入 math mode + +\[a^2 + b^2 = c^2 \] + +My favorite Greek letter is $\xi$. I also like $\beta$, $\gamma$ and $\sigma$. +I haven't found a Greek letter yet that \LaTeX \hspace{1pt} doesn't know +about! \\ + +常用函数操作符同样很重要: +trigonometric functions ($\sin$, $\cos$, $\tan$), +logarithms 和 exponentials ($\log$, $\exp$), +limits ($\lim$), etc. +在 LaTeX 指令中预定义 +让我们写一个等式看看发生了什么: +$\cos(2\theta) = \cos^{2}(\theta) - \sin^{2}(\theta)$ \\ + +分数可以写成以下形式: + +% 10 / 7 +$$ ^{10}/_{7} $$ + +% 相对比较复杂的分数可以写成 +% \frac{numerator}{denominator} +$$ \frac{n!}{k!(n - k)!} $$ \\ + +我们同样可以插入公式(equations)在环境 ``equation environment'' 下。 + +% 展示数学相关时,使用方程式环境 +\begin{equation} % 进入 math-mode + c^2 = a^2 + b^2. + \label{eq:pythagoras} % 为了下一步引用 +\end{equation} % 所有 \begin 语句必须有end语句对应 + +引用我们的新等式! +Eqn.~\ref{eq:pythagoras} is also known as the Pythagoras Theorem which is also +the subject of Sec.~\ref{subsec:pythagoras}. A lot of things can be labeled: +figures, equations, sections, etc. + +求和(Summations)与整合(Integrals)写作 sum 和 int : + +% 一些编译器会提醒在等式环境中的空行 + +\begin{equation} + \sum_{i=0}^{5} f_{i} +\end{equation} +\begin{equation} + \int_{0}^{\infty} \mathrm{e}^{-x} \mathrm{d}x +\end{equation} + +\section{Figures} + +让我们插入图片。图片的放置非常微妙。 +我在每次使用时都会查找可用选项。 + +\begin{figure}[H] % H 是放置选项的符号 + \centering % 图片在本页居中 + % 宽度放缩为页面的0.8倍 + %\includegraphics[width=0.8\linewidth]{right-triangle.png} + % 需要使用想象力决定是否语句超出编译预期 + \caption{Right triangle with sides $a$, $b$, $c$} + \label{fig:right-triangle} +\end{figure} + +\subsection{Table} +插入表格与插入图片方式相同 + +\begin{table}[H] + \caption{Caption for the Table.} + % 下方的 {} 描述了表格中每一行的绘制方式 + % 同样,我在每次使用时都会查找可用选项。 + \begin{tabular}{c|cc} + Number & Last Name & First Name \\ % 每一列被 & 分开 + \hline % 水平线 + 1 & Biggus & Dickus \\ + 2 & Monty & Python + \end{tabular} +\end{table} + +\section{Getting \LaTeX \hspace{1pt} to not compile something (i.e. Source Code)} +现在增加一些源代码在 \LaTeX \hspace{1pt} 文档中, +我们之后需要 \LaTeX \hspace{1pt} 不翻译这些内容而仅仅是把他们打印出来 +这里使用 verbatim environment。 + +% 也有其他库存在 (如. minty, lstlisting, 等) +% 但是 verbatim 是最基础和简单的一个 +\begin{verbatim} + print("Hello World!") + a%b; % 在这一环境下我们可以使用 % + random = 4; #decided by fair random dice roll +\end{verbatim} + +\section{Compiling} + +现在你大概想了解如何编译这个美妙的文档 +然后得到饱受称赞的 \LaTeX \hspace{1pt} pdf文档 +(这个文档确实被编译了)。 \\ +得到最终文档,使用 \LaTeX \hspace{1pt} 组合步骤: + \begin{enumerate} + \item Write the document in plain text (the ``source code''). + \item Compile source code to produce a pdf. + The compilation step looks like this (in Linux): \\ + \begin{verbatim} + > pdflatex learn-latex.tex + \end{verbatim} + \end{enumerate} + +许多 \LaTeX \hspace{1pt}编译器把步骤1和2在同一个软件中进行了整合 +所以你可以只看步骤1完全不看步骤2 +步骤2同样在以下情境中使用情景 \footnote{以防万一,当你使用引用时 + (如 Eqn.~\ref{eq:pythagoras}),你将需要多次运行步骤2 +来生成一个媒介文件 *.aux 。}. +% 同时这也是在文档中增加脚标的方式 + +在步骤1中,用普通文本写入格式化信息 +步骤2的编译阶段则注意在步骤1 中定义的格式信息。 + +\section{Hyperlinks} +同样可以在文档中加入超链接 +使用如下命令在序言中引入库: +\begin{verbatim} + \usepackage{hyperref} +\end{verbatim} + +有两种主要的超链接方式 \\ +\url{https://learnxinyminutes.com/docs/latex/}, 或 +\href{https://learnxinyminutes.com/docs/latex/}{shadowed by text} +% 你不可以增加特殊空格和符号,因为这将会造成编译错误 + +这个库同样在输出PDF文档时制造略缩的列表,或在目录中激活链接 + + +\section{End} + +这就是全部内容了! + +% 通常,你会希望文章中有个引用部分 +% 最简单的建立方式是使用书目提要章节 +\begin{thebibliography}{1} + % 与其他列表相同, \bibitem 命令被用来列出条目 + % 每个记录可以直接被文章主体引用 + \bibitem{latexwiki} The amazing \LaTeX \hspace{1pt} wikibook: {\em +https://en.wikibooks.org/wiki/LaTeX} + \bibitem{latextutorial} An actual tutorial: {\em http://www.latex-tutorial.com} +\end{thebibliography} + +% 结束文档 +\end{document} +``` + +## LaTeX 进阶 + +* The amazing LaTeX wikibook: [https://en.wikibooks.org/wiki/LaTeX](https://en.wikibooks.org/wiki/LaTeX) +* An actual tutorial: [http://www.latex-tutorial.com/](http://www.latex-tutorial.com/) diff --git a/zh-cn/less-cn.html.markdown b/zh-cn/less-cn.html.markdown new file mode 100644 index 00000000..365a0287 --- /dev/null +++ b/zh-cn/less-cn.html.markdown @@ -0,0 +1,387 @@ +--- +language: less +filename: learnless-cn.less +contributors: + - ["Saravanan Ganesh", "http://srrvnn.me"] +translators: + - ["Jiang Haiyun", "http://www.atjiang.com"] +lang: zh-cn +--- + + +Less是一种CSS预处理器,它增加了诸如变量、嵌套、mixin等功能。 +Less(以及其它预处理器,如[Sass](http://sass-lang.com/))能帮助开发人员编写易维护,DRY (Don't Repeat Yourself) 的代码。 + +```css + + +//单行注释在编译成CSS后会被删除。 + +/* 多行注释将保留. */ + + + +/* 变量 +==============================*/ + + +/* 你可以将一个CSS值(如一个颜色值)保存到变量中。 + 使用'@'符号来创建一个变量。*/ + +@primary-color: #a3a4ff; +@secondary-color: #51527f; +@body-font: 'Roboto', sans-serif; + +/* 你可以在你的样式文件中使用这些变量。 + 现在假如你想修改颜色,你只需修改一次即可。*/ + +body { + background-color: @primary-color; + color: @secondary-color; + font-family: @body-font; +} + +/* 以上将编译成: */ + +body { + background-color: #a3a4ff; + color: #51527F; + font-family: 'Roboto', sans-serif; +} + + +/* 相比于在你的样式文件中逐个修改,这种方式维护性更好。 */ + + + +/* Mixins +==============================*/ + + +/* 如果你要为多个元素编写同样的代码, + 你可能想实现轻松地重用。*/ + +.center { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} + +/* 你只需简单地将选择子作为样式添加进来就能使用mixin了 */ + +div { + .center; + background-color: @primary-color; +} + +/* 它将编译成: */ + +.center { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #a3a4ff; +} + +/* 通过在选择子后添加括号,可以使这些mixin代码不被编译 */ + +.center() { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} + +div { + .center; + background-color: @primary-color; +} + +/* 将编译成: */ +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #a3a4ff; +} + + + +/* 嵌套 +==============================*/ + + +/* Less允许你在选择子中嵌套选择子 */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: #f00; + } +} + +/* '&'将被替换成父选择子。*/ +/* 你也可以嵌套伪类。 */ +/* 注意过度嵌套将会导致代码难以维护。 + 最佳实践推荐在嵌套时不超过3层。 + 例如: */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: red; + + &:hover { + background-color: blue; + } + + a { + color: white; + } + } +} + +/* 编译成: */ + +ul { + list-style-type: none; + margin-top: 2em; +} + +ul li { + background-color: red; +} + +ul li:hover { + background-color: blue; +} + +ul li a { + color: white; +} + + + +/* 函数 +==============================*/ + + +/* Less提供的函数可以用来完成多种任务。 + 考虑以下情况: */ + +/* 函数可以通过其名称及传入其所需的参数来调用。 */ + +body { + width: round(10.25px); +} + +.header { + background-color: lighten(#000, 0.5); +} + +.footer { + background-color: fadeout(#000, 0.25) +} + +/* 编译成: */ + +body { + width: 10px; +} + +.header { + background-color: #010101; +} + +.footer { + background-color: rgba(0, 0, 0, 0.75); +} + +/* 你也可以定义自己的函数。函数非常类似于mixin。 + 当你在函数和mixin之间抉择时, + 记住mixin最适合用来创建CSS而函数更适合于 + 处理那些可能在你的Less代码中使用的逻辑。 + '数学运算符'部分的例子是转成可重用函数的最佳选择。*/ + +/* 该函数计算两数的平均值: */ + +.average(@x, @y) { + @average-result: ((@x + @y) / 2); +} + +div { + .average(16px, 50px); // "调用"mixin + padding: @average-result; // 使用它的"返回"值 +} + +/* 编译成: */ + +div { + padding: 33px; +} + + + +/* 扩展 (继承) +==============================*/ + + +/* 扩展是在选择子间共享属性的一种方法。 */ + +.display { + height: 50px; +} + +.display-success { + &:extend(.display); + border-color: #22df56; +} + +/* 编译成: */ +.display, +.display-success { + height: 50px; +} +.display-success { + border-color: #22df56; +} + +/* 扩展一条CSS语句优于创建一个mixin, + 这是由其组合所有共享相同基样式的类的方式决定的。 + 如果使用mixin完成,其属性将会在调用了该mixin的每条语句中重复。 + 虽然它不至会影响你的工作流,但它会在由Less编译器 + 生成的的文件中添加不必要的代码。*/ + + + +/* 片段与导入 +==============================*/ + + +/* Less允许你创建片段文件。它有助于你的Less代码保持模块化。 + 片段文件习惯上以'_'开头,例如 _reset.css,并被导入到 + 一个将会被编译成CSS的主less文件中。*/ + +/* 考虑以下的CSS,我们将把它们放入一个叫_reset.css的文件中 */ + +html, +body, +ul, +ol { + margin: 0; + padding: 0; +} + +/* Less提供的@import能用来将片段导入到文件中。 + 它与传统的CSS @import语句不同,无需通过 + HTTP请求获取导入文件。Less提取导入文件 + 并将它们与编译后的代码结合起来。 */ + +@import 'reset'; + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + +/* 编译成: */ + +html, body, ul, ol { + margin: 0; + padding: 0; +} + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + + + +/* 数学运算符 +==============================*/ + + +/* Less提供以下的运算符: +, -, *, /, 和 %。 + 相比于使用你事先手工计算好了的数值,它们 + 对于直接在你的Less文件中计算数值很有用。 + 以下是设置一个两列设计的例子。*/ + +@content-area: 960px; +@main-content: 600px; +@sidebar-content: 300px; + +@main-size: @main-content / @content-area * 100%; +@sidebar-size: @sidebar-content / @content-area * 100%; +@gutter: 100% - (@main-size + @sidebar-size); + +body { + width: 100%; +} + +.main-content { + width: @main-size; +} + +.sidebar { + width: @sidebar-size; +} + +.gutter { + width: @gutter; +} + +/* 编译成: */ + +body { + width: 100%; +} + +.main-content { + width: 62.5%; +} + +.sidebar { + width: 31.25%; +} + +.gutter { + width: 6.25%; +} + + +``` + +## 实践Less + +如果你想在你的浏览器中尝试LESS,参阅: +* [Codepen](http://codepen.io/) +* [LESS2CSS](http://lesscss.org/less-preview/) + +## 兼容性 + +Less可以用于任何项目中,只要你有程序能将它编译成CSS即可。你还需要验证你所使用的CSS是否与你的目标浏览器兼容。 + +[QuirksMode CSS](http://www.quirksmode.org/css/)和[CanIUse](http://caniuse.com) 对于检查兼容性来说都是不错的资源。 + +## 延伸阅读资料 +* [Official Documentation](http://lesscss.org/features/) +* [Less CSS - Beginner's Guide](http://www.hongkiat.com/blog/less-basic/) diff --git a/zh-cn/livescript-cn.html.markdown b/zh-cn/livescript-cn.html.markdown index fea00bc1..cc7daab1 100644 --- a/zh-cn/livescript-cn.html.markdown +++ b/zh-cn/livescript-cn.html.markdown @@ -1,6 +1,6 @@ --- language: LiveScript -filename: learnLivescript.ls +filename: learnLivescript-cn.ls contributors: - ["Christina Whyte", "http://github.com/kurisuwhyte/"] translators: diff --git a/zh-cn/lua-cn.html.markdown b/zh-cn/lua-cn.html.markdown index 098d0ab5..6736dc2a 100644 --- a/zh-cn/lua-cn.html.markdown +++ b/zh-cn/lua-cn.html.markdown @@ -91,10 +91,10 @@ until num == 0 -- 2. 函数。 ---------------------------------------------------- -function fib(n) - if n < 2 then return 1 end - return fib(n - 2) + fib(n - 1) -end +function fib(n) + if n < 2 then return n end + return fib(n - 2) + fib(n - 1) +end -- 支持闭包及匿名函数: function adder(x) @@ -129,9 +129,11 @@ 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可以自引用。 +local function g(x) return math.sin(x) end +local g; g = function (x) return math.sin(x) end +-- 以上均因'local g',使得g可以自引用。 +local g = function(x) return math.sin(x) end +-- 等价于 local function g(x)..., 但函数体中g不可自引用 -- 顺便提下,三角函数以弧度为单位。 @@ -210,7 +212,7 @@ f2 = {a = 2, b = 3} metafraction = {} function metafraction.__add(f1, f2) - sum = {} + local sum = {} sum.b = f1.b * f2.b sum.a = f1.a * f2.b + f2.a * f1.b return sum @@ -273,7 +275,7 @@ eatenBy = myFavs.animal -- 可以工作!感谢元表 Dog = {} -- 1. function Dog:new() -- 2. - newObj = {sound = 'woof'} -- 3. + local newObj = {sound = 'woof'} -- 3. self.__index = self -- 4. return setmetatable(newObj, self) -- 5. end @@ -307,7 +309,7 @@ mrDog:makeSound() -- 'I say woof' -- 8. LoudDog = Dog:new() -- 1. function LoudDog:makeSound() - s = self.sound .. ' ' -- 2. + local s = self.sound .. ' ' -- 2. print(s .. s .. s) end @@ -328,7 +330,7 @@ seymour:makeSound() -- 'woof woof woof' -- 4. -- 如果有必要,子类也可以有new(),与基类相似: function LoudDog:new() - newObj = {} + local newObj = {} -- 初始化newObj self.__index = self return setmetatable(newObj, self) @@ -340,7 +342,9 @@ end --[[ 我把这部分给注释了,这样脚本剩下的部分可以运行 +``` +```lua -- 假设文件mod.lua的内容类似这样: local M = {} @@ -411,4 +415,9 @@ lua-users.org上的[Lua简明参考](http://lua-users.org/files/wiki_insecure/us * <a href="http://lua-users.org/wiki/IoLibraryTutorial">io library</a> * <a href="http://lua-users.org/wiki/OsLibraryTutorial">os library</a> +顺便说一下,整个文件是可运行的Lua; +保存为 learn-cn.lua 用命令 `lua learn-cn.lua` 启动吧! + +本文首次撰写于 [tylerneylon.com](http://tylerneylon.com) 同时也有 [github gist](https://gist.github.com/tylerneylon/5853042) 版. + 使用Lua,欢乐常在! diff --git a/zh-cn/markdown-cn.html.markdown b/zh-cn/markdown-cn.html.markdown index 1c577efb..87ed46ad 100644 --- a/zh-cn/markdown-cn.html.markdown +++ b/zh-cn/markdown-cn.html.markdown @@ -53,7 +53,7 @@ __此文本也是__ **_或者这样。_** *__这个也是!__* -<!-- 在 Github 采用的 Markdown 中 --> +<!-- 在 GitHub 采用的 Markdown 中 --> ~~此文本为删除线效果。~~ @@ -69,7 +69,7 @@ __此文本也是__ <!-- 如果你插入一个 HTML中的<br />标签,你可以在段末加入两个以上的空格, 然后另起一段。--> -此段落结尾有两个空格(选中以显示)。 +此段落结尾有两个空格(选中以显示)。 上文有一个 <br /> ! @@ -127,7 +127,7 @@ __此文本也是__ <!-- 代码段落 --> <!-- 代码段落(HTML中 <code>标签)可以由缩进四格(spaces) -或者一个标签页(tab)实现--> +或者一个制表符(tab)实现--> This is code So is this @@ -142,7 +142,7 @@ __此文本也是__ John 甚至不知道 `go_to()` 方程是干嘛的! -<!-- 在Github的 Markdown中,对于代码你可以使用特殊的语法 --> +<!-- 在GitHub的 Markdown中,对于代码你可以使用特殊的语法 --> \`\`\`ruby <!-- 插入时记得移除反斜线, 仅留```ruby ! --> def foobar @@ -150,7 +150,7 @@ def foobar end \`\`\` <!-- 这里也是,移除反斜线,仅留 ``` --> -<!-- 以上代码不需要缩进,而且 Github 会根据```后表明的语言来进行语法高亮 --> +<!-- 以上代码不需要缩进,而且 GitHub 会根据```后表明的语言来进行语法高亮 --> <!-- 水平线 (<hr />) --> <!-- 水平线可由三个或以上的星号或者减号创建,可带可不带空格。 --> @@ -220,7 +220,7 @@ end 斜体化, 所以我就: \*这段置文字于星号之间\*。 <!-- 表格 --> -<!-- 表格只被 Github 的 Markdown 支持,并且有一点笨重,但如果你真的要用的话: --> +<!-- 表格只被 GitHub 的 Markdown 支持,并且有一点笨重,但如果你真的要用的话: --> | 第一列 | 第二列 | 第三列 | | :---------- | :------: | ----------: | diff --git a/zh-cn/matlab-cn.html.markdown b/zh-cn/matlab-cn.html.markdown index 77ba765a..2fbccfc4 100644 --- a/zh-cn/matlab-cn.html.markdown +++ b/zh-cn/matlab-cn.html.markdown @@ -1,11 +1,13 @@ ---
language: Matlab
+filename: matlab-cn.m
contributors:
- ["mendozao", "http://github.com/mendozao"]
- ["jamesscottbrown", "http://jamesscottbrown.com"]
translators:
- ["sunxb10", "https://github.com/sunxb10"]
lang: zh-cn
+
---
MATLAB 是 MATrix LABoratory (矩阵实验室)的缩写,它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
diff --git a/zh-cn/python3-cn.html.markdown b/zh-cn/python3-cn.html.markdown index c223297c..76455a46 100644 --- a/zh-cn/python3-cn.html.markdown +++ b/zh-cn/python3-cn.html.markdown @@ -535,7 +535,7 @@ Human.grunt() # => "*grunt*" # 用import导入模块 import math -print(math.sqrt(16)) # => 4 +print(math.sqrt(16)) # => 4.0 # 也可以从模块中导入个别值 from math import ceil, floor diff --git a/zh-cn/r-cn.html.markdown b/zh-cn/r-cn.html.markdown index 0c46bc22..d576db29 100644 --- a/zh-cn/r-cn.html.markdown +++ b/zh-cn/r-cn.html.markdown @@ -285,7 +285,7 @@ while (a > 4) { } # 记住,在 R 语言中 for / while 循环都很慢 -# 建议使用 apply()(我们一会介绍)来错做一串数据(比如一列或者一行数据) +# 建议使用 apply()(我们一会介绍)来操作一串数据(比如一列或者一行数据) # IF/ELSE @@ -303,7 +303,7 @@ if (4 > 3) { # 定义如下 jiggle <- function(x) { - x + rnorm(x, sd=.1) #add in a bit of (controlled) noise + x = x + rnorm(1, sd=.1) # 添加一点(正态)波动 return(x) } diff --git a/zh-cn/red-cn.html.markdown b/zh-cn/red-cn.html.markdown new file mode 100644 index 00000000..85812990 --- /dev/null +++ b/zh-cn/red-cn.html.markdown @@ -0,0 +1,208 @@ +--- +name: Red +category: language +language: Red +filename: LearnRed-zh.red +contributors: + - ["Arnold van Hofwegen", "https://github.com/iArnold"] +translators: + - ["Limo Saplf", "https://github.com/saplf"] +lang: zh-cn +--- + +Red 的编写是出于工作需要,该语言的作者想要使用 REBOL,但它有许多缺陷。 +当时 REBOL 还没有开源,由于它是一门解释型语言,这就意味着它比编译型语言效率低。 + +Red 使用 C 语言级别的 Red/System,是一门涉及所有编程领域的语言。 +Red 基于 REBOL 编写,它继承了 REBOL 的灵活性,同时也包含了许多 C 语言能做的底层实现。 + +Red 将会成为世界上第一门全栈式编程语言,这意味着它可以完成几乎所有的编程任务,从底层到抽象,无需其他工具的参与。 +而且,Red 支持交叉编译,任意两个平台之间,不需要任何 GCC 之类的工具链的支持。 +所有的工作,仅仅需要一个不到 1 MB 的二进制可执行文件。 + +准备好你的 Red 第一课了吗? + +```red +所有 header 之前的文字都是注释,只要你不使用 "red" 关键字,其中的 "r" 大写。 +这是词法分析器的一个缺陷,所以大多数时候,你都应该直接以 header 开始程序或者脚本的编写。 + +red 脚本的 header 由关键字,首字母大写的 "red" 开始,后跟一个空格,再跟一对方括号 []。 +方括号里可以写上一些关于这段脚本或者程序的相关信息: +作者,文件名,版本号,license,程序功能的简介,它依赖的其他文件。 +red/System 的 header 和 red header 类似,仅仅是说明 "red/System" 而非 "red"。 + + +Red [] + +; 这是一条行注释 + +print "Hello Red World" ; 另一条注释 + +comment { + 这是多行注释。 + 你刚刚看到的就是 Red 版的 Hello World。 +} + +; 程序的入口就是第一句可执行的代码 +; 不需要把它放在 'main' 函数里 + +; 变量名以一个字母开始,可以包含数字, +; 只包含 A ~ F 字符和数字的变量名不能以 'h' 结尾, +; 因为这是 Red 和 Red/System 中十六进制数字的表达方式。 + +; 给变量赋值使用冒号 ":" +my-name: "Red" +reason-for-using-the-colon: {使用冒号作为赋值符号 + 是为了能够让 "=" 能够用来作为比较符号,这本来就是 "=" + 存在的意义!还记得上学时学的,y = x + 1 、 x = 1, + 以及推导出的 y = 2 吗? +} +is-this-name-valid?: true + +; 用 print 打印输出,prin 打印不带换行的输出 + +prin "我的名字是 " print my-name +; 我的名字是 Red + +print ["我的名字是 " my-name lf] +; 我的名字是 Red + +; 注意到了吗:语句没有以分号结尾 ;-) + +; +; 数据类型 +; +; 如果你了解 Rebol,你可能就会注意到它有许多数据类型。 +; Red 并没有囊括它所有的类型,但由于 Red 想要尽可能的 +; 接近 Rebol,所以它也会有很多数据类型。 +; 类型以叹号结尾,但要注意,变量名也是可以以叹号结尾的。 +; 一些内置类型有 integer! string! block! + +; 使用变量前需要声明吗? +; Red 能够分辨什么时候使用什么变量,变量声明并非必要的。 +; 一般认为,声明变量是较好的编码实践,但 Red 并不会强制这点。 +; 你可以声明一个变量然后指定它的类型,而一个变量的类型就 +; 指定了它的字节大小。 + +; integer! 类型的变量通常是 4 字节,32位 +my-integer: 0 +; Red 的整型包含符号,暂时不支持无符号类型,但以后会支持的。 + +; 怎样判断一个变量的类型? +type? my-integer +; integer! + +; 一个变量的初始化可以使用另一个同样刚刚初始化的变量: +i2: 1 + i1: 1 + +; 算数运算符 +i1 + i2 ; 3 +i2 - i1 ; 1 +i2 * i1 ; 2 +i1 / i2 ; 0 (0.5,但截取为 0) + +; 比较运算符都差不多,但和其他语言不一样的是相等的比较, +; Red 使用单个的 '='。 +; Red 有一个类似 boolean 的类型,它的值是 true 和 false, +; 但也可以使用 on/off 或者 yes/on + +3 = 2 ; false +3 != 2 ; true +3 > 2 ; true +3 < 2 ; false +2 <= 2 ; true +2 >= 2 ; true + +; +; 控制流 +; +; if +; 如果给定的条件为 true 则执行一段代码块。 +; if 没有返回值,所以不能用作表达式。 +if a < 0 [print "a 是负值"] + +; either +; 如果给定的条件为 true 则执行一段代码块,否则就 +; 执行另一段可选的代码块。如果两个代码块中最后一个表达式 +; 的类型相同, either 就可以用作表达式。 +either a > 0 [ + msg: "正值" +][ + either a = 0 [ + msg: "零" + ][ + msg: "负值" + ] +] +print ["a 是 " msg lf] + +; 还可以有另一种写法 +; (因为两条路径的返回值相同,所以可以这么写): + +msg: either a > 0 [ + "正值" +][ + either a = 0 [ + "零" + ][ + "负值" + ] +] +print ["a 是 " msg lf] + +; util +; 循环执行一段代码块,直到满足给定的条件为止。 +; util 没有返回值,所以它不能用在表示式中。 +c: 5 +util [ + prin "o" + c: c - 1 + c = 0 ; 终止循环的条件 +] +; 输出:ooooo +; 需要注意的是,即使条件从一开始就不满足, +; 这个循环也至少会执行一次。 + +; while +; 当满足给定的条件,就执行一段代码。 +; while 没有返回值,不能用在表达式中。 +c: 5 +while [c > 0][ + prin "o" + c: c - 1 +] +; 输出:ooooo + +; +; 函数 +; +; 函数示例 +twice: function [a [integer!] /one return: [integer!]][ + c: 2 + a: a * c + either one [a + 1][a] +] +b: 3 +print twice b ; 输出 6 + +; 使用 #include 和 %文件名 来导入外部文件 +#include %includefile.red +; 现在就可以使用 includefile.red 中的函数了。 + +``` + +## 更进一步 + +Red 相关的源码信息在 [Red 语言主页](http://www.red-lang.org)。 + +源代码的 [github 库](https://github.com/red/red)。 + +Red/System 特性在 [这里](http://static.red-lang.org/red-system-specs-light.html)。 + +想要了解更多关于 Rebol 和 Red 的信息,加入 [Gitter 聊天室](https://gitter.im/red/red)。如果你无法加入,也可以给我们发[邮件](mailto:red-langNO_SPAM@googlegroups.com)。 + +也可以在 [Stack Overflow](stackoverflow.com/questions/tagged/red) 上查阅、提交问题。 + +也许你现在就要试一试 Red ?可以在线尝试 [try Rebol and Red site](http://tryrebol.esperconsultancy.nl)。 + +你也可以通过学习一些 [Rebol](http://www.rebol.com/docs.html) 来学习 Red。 diff --git a/zh-cn/ruby-cn.html.markdown b/zh-cn/ruby-cn.html.markdown index 99250b43..657a913d 100644 --- a/zh-cn/ruby-cn.html.markdown +++ b/zh-cn/ruby-cn.html.markdown @@ -7,8 +7,10 @@ contributors: - ["Joel Walden", "http://joelwalden.net"] - ["Luke Holder", "http://twitter.com/lukeholder"] - ["lidashuang", "https://github.com/lidashuang"] + - ["ftwbzhao", "https://github.com/ftwbzhao"] translators: - ["Lin Xiangyu", "https://github.com/oa414"] + - ["Jiang Haiyun", "https://github.com/haiiiiiyun"] --- ```ruby @@ -34,6 +36,13 @@ translators: 8 - 1 #=> 7 10 * 2 #=> 20 35 / 5 #=> 7 +2**5 #=> 32 +5 % 3 #=> 2 + +# 位运算符 +3 & 5 #=> 1 +3 | 5 #=> 7 +3 ^ 5 #=> 6 # 算术符号只是语法糖而已 # 实际上是调用对象的方法 @@ -41,7 +50,7 @@ translators: 10.* 5 #=> 50 # 特殊的值也是对象 -nil # 空 +nil # 相当于其它语言中的 null true # 真 false # 假 @@ -53,13 +62,11 @@ false.class #=> FalseClass 1 == 1 #=> true 2 == 1 #=> false -# 不等运算符 +# 不相等运算符 1 != 1 #=> false 2 != 1 #=> true -!true #=> false -!false #=> true -# 除了false自己,nil是唯一的值为false的对象 +# 除了false自己,nil是唯一的另一个值为false的对象 !nil #=> true !false #=> true @@ -71,6 +78,26 @@ false.class #=> FalseClass 2 <= 2 #=> true 2 >= 2 #=> true + +# 组合比较运算符 +1 <=> 10 #=> -1 +10 <=> 1 #=> 1 +1 <=> 1 #=> 0 + +# 逻辑运算符 +true && false #=> false +true || false #=> true +!true #=> false + +# 也有优先级更低的逻辑运算符 +# 它们用于控制流结构中,用来串接语句,直到返回true或false。 + +# `do_something_else` 只当 `do_something` 返回true时才会被调用 +do_something() and do_something_else() +# `log_error` 只当 `do_something` 返回false时才会被调用 +do_something() or log_error() + + # 字符串是对象 'I am a string'.class #=> String @@ -80,9 +107,28 @@ placeholder = "use string interpolation" "I can #{placeholder} when using double quoted strings" #=> "I can use string interpolation when using double quoted strings" +# 尽可能优先使用单引号的字符串 +# 双引号的字符串会进行一些额外的内部处理 + +# 合并字符串,但不能和数字合并 +'hello ' + 'world' #=> "hello world" +'hello ' + 3 #=> TypeError: can't convert Fixnum into String +'hello ' + 3.to_s #=> "hello 3" + +# 合并字符串及其运算符 +'hello ' * 3 #=> "hello hello hello " + +# 字符串追加 +'hello' << ' world' #=> "hello world" -# 输出值 +# 打印输出,并在末尾加换行符 puts "I'm printing!" +#=> I'm printing! +#=> nil + +# 打印输出,不加换行符 +print "I'm printing!" +#=> I'm printing! => nil # 变量 x = 25 #=> 25 @@ -95,17 +141,16 @@ x = y = 10 #=> 10 x #=> 10 y #=> 10 -# 按照惯例,用 snake_case 作为变量名 +# 按照惯例,使用类似snake_case风格的变量名 snake_case = true -# 使用具有描述性的运算符 +# 使用有意义的变量名 path_to_project_root = '/good/name/' path = '/bad/name/' # 符号(Symbols,也是对象) -# 符号是不可变的,内部用整数类型表示的可重用的值。 -# 通常用它代替字符串来有效地表示有意义的值。 - +# 符号是不可变的,内部用整数值表示的可重用的常数 +# 通常用它代替字符串来有效地表示有意义的值 :pending.class #=> Symbol @@ -120,37 +165,47 @@ status == :approved #=> false # 数组 # 这是一个数组 -[1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] +array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] # 数组可以包含不同类型的元素 -array = [1, "hello", false] #=> => [1, "hello", false] +[1, "hello", false] #=> [1, "hello", false] # 数组可以被索引 # 从前面开始 array[0] #=> 1 array[12] #=> nil -# 像运算符一样,[var]形式的访问 -# 也就是一个语法糖 -# 实际上是调用对象的[] 方法 +# 像运算符一样,[var] 形式的访问 +# 也只是语法糖 +# 实际上是调用对象的 [] 方法 array.[] 0 #=> 1 array.[] 12 #=> nil # 从尾部开始 array[-1] #=> 5 +array.last #=> 5 + +# 同时指定开始的位置和长度 +array[2, 3] #=> [3, 4, 5] -# 同时指定开始的位置和结束的位置 -array[2, 4] #=> [3, 4, 5] +# 将数组逆序 +a=[1,2,3] +a.reverse! #=> [3,2,1] -# 或者指定一个范围 +# 或者指定一个区间 array[1..3] #=> [2, 3, 4] # 像这样往数组增加一个元素 array << 6 #=> [1, 2, 3, 4, 5, 6] +# 或者像这样 +array.push(6) #=> [1, 2, 3, 4, 5, 6] -# 哈希表是Ruby的键值对的基本数据结构 -# 哈希表由大括号定义 +# 检查元素是否包含在数组中 +array.include?(1) #=> true + +# 哈希表是 Ruby 的主要键/值对表示法 +# 哈希表由大括号表示 hash = {'color' => 'green', 'number' => 5} hash.keys #=> ['color', 'number'] @@ -159,22 +214,17 @@ hash.keys #=> ['color', 'number'] hash['color'] #=> 'green' hash['number'] #=> 5 -# 查询一个不存在地键将会返回nil +# 查询一个不存在的键将会返回nil hash['nothing here'] #=> nil -# 用 #each 方法来枚举哈希表: -hash.each do |k, v| - puts "#{k} is #{v}" -end - -# 从Ruby 1.9开始, 用符号作为键的时候有特别的记号表示: +# 从Ruby 1.9开始,用符号作为键的时候有特别的记号表示: -new_hash = { defcon: 3, action: true} +new_hash = { defcon: 3, action: true } new_hash.keys #=> [:defcon, :action] # 小贴士:数组和哈希表都是可枚举的 -# 它们可以共享一些有用的方法,比如each, map, count 等等 +# 它们共享一些有用的方法,比如each,map,count等等 # 控制流 @@ -195,9 +245,15 @@ end #=> iteration 4 #=> iteration 5 -# 然而 -# 没人用for循环 -# 用`each`来代替,就像这样 + +# 但是,没有人用for循环。 +# 你应该使用"each"方法,然后再传给它一个块。 +# 所谓块就是可以传给像"each"这样的方法的代码段。 +# 它类似于其它语言中的lambdas, 匿名函数或闭包。 +# +# 区间上的"each"方法会对区间中的每个元素运行一次块代码。 +# 我们将counter作为一个参数传给了块。 +# 调用带有块的"each"方法看起来如下: (1..5).each do |counter| puts "iteration #{counter}" @@ -208,6 +264,23 @@ end #=> iteration 4 #=> iteration 5 +# 你也可以将块包含在一个大括号中: +(1..5).each { |counter| puts "iteration #{counter}" } + +# 数据结构中的内容也可以使用each来遍历。 +array.each do |element| + puts "#{element} is part of the array" +end +hash.each do |key, value| + puts "#{key} is #{value}" +end + +# 如果你还需要索引值,可以使用"each_with_index",并且定义 +# 一个索引变量 +array.each_with_index do |element, index| + puts "#{element} is number #{index} in the array" +end + counter = 1 while counter <= 5 do puts "iteration #{counter}" @@ -219,6 +292,20 @@ end #=> iteration 4 #=> iteration 5 +# Ruby 中还有很多有用的循环遍历函数, +# 如"map","reduce","inject"等等。 +# 以map为例,它会遍历数组,并根据你在 +# 块中定义的逻辑对它进行处理,然后返回 +# 一个全新的数组。 +array = [1,2,3,4,5] +doubled = array.map do |element| + element * 2 +end +puts doubled +#=> [2,4,6,8,10] +puts array +#=> [1,2,3,4,5] + grade = 'B' case grade @@ -235,6 +322,33 @@ when 'F' else puts "Alternative grading system, eh?" end +#=> "Better luck next time" + +# case也可以用区间 +grade = 82 +case grade +when 90..100 + puts 'Hooray!' +when 80...90 + puts 'OK job' +else + puts 'You failed!' +end +#=> "OK job" + +# 异常处理: +begin + # 这里的代码可能会抛出异常 + raise NoMemoryError, 'You ran out of memory.' +rescue NoMemoryError => exception_variable + puts 'NoMemoryError was raised', exception_variable +rescue RuntimeError => other_exception_variable + puts 'RuntimeError was raised now' +else + puts 'This runs if no exceptions were thrown at all' +ensure + puts 'This code always runs no matter what' +end # 函数 @@ -242,7 +356,7 @@ def double(x) x * 2 end -# 函数 (以及所有的方法块) 隐式地返回了最后语句的值 +# 函数 (以及所有的块) 隐式地返回最后语句的值 double(2) #=> 4 # 当不存在歧义的时候括号是可有可无的 @@ -260,8 +374,8 @@ sum 3, 4 #=> 7 sum sum(3,4), 5 #=> 12 # yield -# 所有的方法都有一个隐式的块参数 -# 可以用yield参数调用 +# 所有的方法都有一个隐式的,可选的块参数 +# 可以用 'yield' 关键字调用 def surround puts "{" @@ -275,45 +389,84 @@ surround { puts 'hello world' } # hello world # } +# 可以向函数传递一个块 +# "&"标记传递的块是一个引用 +def guests(&block) + block.call 'some_argument' +end -# 用class关键字定义一个类 -class Human - - # 一个类变量,它被这个类地所有实例变量共享 - @@species = "H. sapiens" +# 可以传递多个参数,这些参数会转成一个数组, +# 这也是使用星号符 ("*") 的原因: +def guests(*array) + array.each { |guest| puts guest } +end - # 构造函数 - def initialize(name, age=0) - # 将参数name的值赋给实例变量@name - @name = name - # 如果没有给出age, 那么会采用参数列表中地默认地值 - @age = age - end +# 如果函数返回一个数组,在赋值时可以进行拆分: +def foods + ['pancake', 'sandwich', 'quesadilla'] +end +breakfast, lunch, dinner = foods +breakfast #=> 'pancake' +dinner #=> 'quesadilla' - # 基本的 setter 方法 - def name=(name) - @name = name - end +# 按照惯例,所有返回布尔值的方法都以?结尾 +5.even? # false +5.odd? # true - # 基本地 getter 方法 - def name - @name - end +# 如果方法名末尾有!,表示会做一些破坏性的操作,比如修改调用者自身。 +# 很多方法都会有一个!的版本来进行修改,和一个非!的版本 +# 只用来返回更新了的结果 +company_name = "Dunder Mifflin" +company_name.upcase #=> "DUNDER MIFFLIN" +company_name #=> "Dunder Mifflin" +company_name.upcase! # we're mutating company_name this time! +company_name #=> "DUNDER MIFFLIN" - # 一个类方法以self.开头 - # 它可以被类调用,但不能被类的实例调用 - def self.say(msg) - puts "#{msg}" - end - def species - @@species - end +# 用class关键字定义一个类 +class Human + # 一个类变量,它被这个类的所有实例变量共享 + @@species = "H. sapiens" + + # 基本构造函数 + def initialize(name, age = 0) + # 将参数值赋给实例变量"name" + @name = name + # 如果没有给出age,那么会采用参数列表中的默认值 + @age = age + end + + # 基本的setter方法 + def name=(name) + @name = name + end + + # 基本地getter方法 + def name + @name + end + + # 以上的功能也可以用下面的attr_accessor来封装 + attr_accessor :name + + # Getter/setter方法也可以像这样单独创建 + attr_reader :name + attr_writer :name + + # 类方法通过使用self与实例方法区别开来。 + # 它只能通过类来调用,不能通过实例调用。 + def self.say(msg) + puts "#{msg}" + end + + def species + @@species + end end -# 类的例子 +# 初始化一个类 jim = Human.new("Jim Halpert") dwight = Human.new("Dwight K. Schrute") @@ -326,7 +479,132 @@ jim.name #=> "Jim Halpert II" dwight.species #=> "H. sapiens" dwight.name #=> "Dwight K. Schrute" -# 调用对象的方法 -Human.say("Hi") #=> "Hi" +# 调用类方法 +Human.say('Hi') #=> "Hi" + +# 变量的作用域由它们的名字格式定义 +# 以$开头的变量具有全局域 +$var = "I'm a global var" +defined? $var #=> "global-variable" + +# 以@开头的变量具有实例作用域 +@var = "I'm an instance var" +defined? @var #=> "instance-variable" + +# 以@@开头的变量具有类作用域 +@@var = "I'm a class var" +defined? @@var #=> "class variable" + +# 以大写字母开头的变量是常数 +Var = "I'm a constant" +defined? Var #=> "constant" + +# 类也是对象。因此类也可以有实例变量。 +# 类变量在类以及其继承者之间共享。 + +# 基类 +class Human + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(value) + @@foo = value + end +end + +# 派生类 +class Worker < Human +end + +Human.foo # 0 +Worker.foo # 0 + +Human.foo = 2 # 2 +Worker.foo # 2 + +# 类实例变量不能在继承类间共享。 + +class Human + @bar = 0 + + def self.bar + @bar + end + + def self.bar=(value) + @bar = value + end +end + +class Doctor < Human +end + +Human.bar # 0 +Doctor.bar # nil + +module ModuleExample + def foo + 'foo' + end +end + +# '包含'模块后,模块的方法会绑定为类的实例方法 +# '扩展'模块后,模块的方法会绑定为类方法 + +class Person + include ModuleExample +end + +class Book + extend ModuleExample +end + +Person.foo # => NoMethodError: undefined method `foo' for Person:Class +Person.new.foo # => 'foo' +Book.foo # => 'foo' +Book.new.foo # => NoMethodError: undefined method `foo' + +# 当包含或扩展一个模块时,相应的回调代码会被执行。 +module ConcernExample + def self.included(base) + base.extend(ClassMethods) + base.send(:include, InstanceMethods) + end + + module ClassMethods + def bar + 'bar' + end + end + + module InstanceMethods + def qux + 'qux' + end + end +end + +class Something + include ConcernExample +end + +Something.bar # => 'bar' +Something.qux # => NoMethodError: undefined method `qux' +Something.new.bar # => NoMethodError: undefined method `bar' +Something.new.qux # => 'qux' ``` + + +## 其它资源 + +- [Learn Ruby by Example with Challenges](http://www.learneroo.com/modules/61/nodes/338) - A variant of this reference with in-browser challenges. +- [An Interactive Tutorial for Ruby](https://rubymonk.com/) - Learn Ruby through a series of interactive tutorials. +- [Official Documentation](http://ruby-doc.org/core) +- [Ruby from other languages](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/) +- [Programming Ruby](http://www.amazon.com/Programming-Ruby-1-9-2-0-Programmers/dp/1937785491/) - An older [free edition](http://ruby-doc.com/docs/ProgrammingRuby/) is available online. +- [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide) - A community-driven Ruby coding style guide. +- [Try Ruby](http://tryruby.org) - Learn the basic of Ruby programming language, interactive in the browser. diff --git a/zh-cn/rust-cn.html.markdown b/zh-cn/rust-cn.html.markdown new file mode 100644 index 00000000..5d3fc693 --- /dev/null +++ b/zh-cn/rust-cn.html.markdown @@ -0,0 +1,296 @@ +--- +language: rust +contributors: + - ["P1start", "http://p1start.github.io/"] +translators: + - ["Guangming Mao", "http://maogm.com"] +filename: learnrust-cn.rs +lang: zh-cn +--- + +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 不仅高效,并且易用。 + +```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); //不像 `mine`, `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). diff --git a/zh-cn/sass-cn.html.markdown b/zh-cn/sass-cn.html.markdown new file mode 100644 index 00000000..985c6470 --- /dev/null +++ b/zh-cn/sass-cn.html.markdown @@ -0,0 +1,585 @@ +--- +language: sass +filename: learnsass-cn.scss +contributors: + - ["Laura Kyle", "https://github.com/LauraNK"] + - ["Sean Corrales", "https://github.com/droidenator"] + - ["Kyle Mendes", "https://github.com/pink401k"] + - ["Keith Miyake", "https://github.com/kaymmm"] +translators: + - ["Jiang Haiyun", "http://www.atjiang.com"] +lang: zh-cn +--- + +Sass是一种CSS扩展语言,它增加了诸如变量、嵌套、mixin等功能。 +Sass(以及其它预处理器,如[Less](http://lesscss.org/)等) 能帮助开发人员编写易维护和 DRY (Don't Repeat Yourself)的代码。 + +Sass有两种不同的语法可选用。SCSS的语法和CSS的相同,但增加了Sass的额外功能。或者Sass(原来的语法),它使用缩进而非大括号和分号。 + +本教程使用SCSS编写。 + +如果你已熟悉CSS3,你可能相对能较快地掌握Sass。它并没有提供任何新的类型属性,而只是提供了一些工具使你能更高效的编写CSS,并且使维护更加容易。 + +```scss + + +// 单行注释当Sass被编译成CSS后会被删除。 + +/* 多行注释将保留. */ + +/* 变量 +============================== */ + + + +/* 你可以将一个CSS值(如一个颜色值)保存到变量中。 +使用'$'符号来创建一个变量。*/ + +$primary-color: #A3A4FF; +$secondary-color: #51527F; +$body-font: 'Roboto', sans-serif; + +/* 你可以在你的样式文件中使用变量。 + 现在假如你想修改颜色,你只需修改一次即可。*/ + +body { + background-color: $primary-color; + color: $secondary-color; + font-family: $body-font; +} + +/* 以上将编译成: */ +body { + background-color: #A3A4FF; + color: #51527F; + font-family: 'Roboto', sans-serif; +} + +/* 相比于在你的样式文件中逐个进行修改,这种方式维护性更好。 */ + + + +/* 控制指令 +============================== */ + +/* Sass允许你使用@if, @else, @for, @while, 和 @each 来控制 + 你的代码如何编译成CSS */ + +/* @if/@else块的行为和你可能预想的会完全相同 */ + +$debug: true !default; + +@mixin debugmode { + @if $debug { + @debug "Debug mode enabled"; + + display: inline-block; + } + @else { + display: none; + } +} + +.info { + @include debugmode; +} + +/* 如果$debug设置为了true, .info 将会显示; 如果设置为false那么 + .info 将不显示。 + +注意: @debug将在命令行中输出调试信息。 +在调试你的SCSS时它对于检查变量很有用。*/ + +.info { + display: inline-block; +} + +/* @for是控制循环,它能遍历区间值。 +它对于设置一组元素的类型特别有用。 +有两种形式,"through"和"to"。前者包括最末那个值, +而后者止于最末那个值。 +*/ + +@for $c from 1 to 4 { + div:nth-of-type(#{$c}) { + left: ($c - 1) * 900 / 3; + } +} + +@for $c from 1 through 3 { + .myclass-#{$c} { + color: rgb($c * 255 / 3, $c * 255 / 3, $c * 255 / 3); + } +} + +/* 将编译成: */ + +div:nth-of-type(1) { + left: 0; +} + +div:nth-of-type(2) { + left: 300; +} + +div:nth-of-type(3) { + left: 600; +} + +.myclass-1 { + color: #555555; +} + +.myclass-2 { + color: #aaaaaa; +} + +.myclass-3 { + color: white; +// SASS automatically converts #FFFFFF to white +} + +/* @while也非常直白: */ + +$columns: 4; +$column-width: 80px; + +@while $columns > 0 { + .col-#{$columns} { + width: $column-width; + left: $column-width * ($columns - 1); + } + + $columns: $columns - 1; +} + +/* 将输出以下CSS: */ + +.col-4 { + width: 80px; + left: 240px; +} + +.col-3 { + width: 80px; + left: 160px; +} + +.col-2 { + width: 80px; + left: 80px; +} + +.col-1 { + width: 80px; + left: 0px; +} + +/* @each函数类似@for, 除了它使用一个列表而不是序列值 +注意: 你指定列表的方式和指定其它变量一样, +用空格作为分隔符。 */ + +$social-links: facebook twitter linkedin reddit; + +.social-links { + @each $sm in $social-links { + .icon-#{$sm} { + background-image: url("images/#{$sm}.png"); + } + } +} + +/* 将输出: */ + +.social-links .icon-facebook { + background-image: url("images/facebook.png"); +} + +.social-links .icon-twitter { + background-image: url("images/twitter.png"); +} + +.social-links .icon-linkedin { + background-image: url("images/linkedin.png"); +} + +.social-links .icon-reddit { + background-image: url("images/reddit.png"); +} + + +/* Mixins +==============================*/ + +/* 如果你发现你要为多个元素编写相同的代码, +你可能想将那些代码保存到一个mixin中。 + +使用'@mixin'指令,再为你的mixin加上一个名称。*/ + +@mixin center { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} + +/* 你可以通过'@include'及mixin名来调用mixin。 */ + +div { + @include center; + background-color: $primary-color; +} + +/* 将编译成: */ +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #A3A4FF; +} + +/* 你可以使用mixin来创建一个快捷属性。*/ + +@mixin size($width, $height) { + width: $width; + height: $height; +} + +/* 你可以通过传入width和height参数来调用它。*/ + +.rectangle { + @include size(100px, 60px); +} + +.square { + @include size(40px, 40px); +} + +/* 编译成: */ +.rectangle { + width: 100px; + height: 60px; +} + +.square { + width: 40px; + height: 40px; +} + + + +/* 函数 +============================== */ + + + +/* Sass提供的函数可以用来完成各种各样的任务。 + 考虑以下情况 */ + +/* 函数可以通过其名称及传入其所需的参数来调用 */ +body { + width: round(10.25px); +} + +.footer { + background-color: fade_out(#000000, 0.25); +} + +/* 编译成: */ + +body { + width: 10px; +} + +.footer { + background-color: rgba(0, 0, 0, 0.75); +} + +/* 你也可以定义你自己的函数。函数非常类似于mixin。 + 当你在函数和mixin之间抉择时,记住mixin最适合于创建CSS而函数更适合于 + 处理那些可能在你的Sass代码中使用的逻辑。'数学运算符'部分的例子 + 是转成可重用函数的最理想选择。 */ + +/* 该函数将接收一个目标尺寸大小和父结点尺寸大小,然后计算并 + 返回百分数 */ + +@function calculate-percentage($target-size, $parent-size) { + @return $target-size / $parent-size * 100%; +} + +$main-content: calculate-percentage(600px, 960px); + +.main-content { + width: $main-content; +} + +.sidebar { + width: calculate-percentage(300px, 960px); +} + +/* 编译成: */ + +.main-content { + width: 62.5%; +} + +.sidebar { + width: 31.25%; +} + + + +/* 扩展 (继承) +============================== */ + + + +/* 扩展是在选择子间共享属性的一种方法。 */ + +.display { + @include size(5em, 5em); + border: 5px solid $secondary-color; +} + +.display-success { + @extend .display; + border-color: #22df56; +} + +/* 编译成: */ +.display, .display-success { + width: 5em; + height: 5em; + border: 5px solid #51527F; +} + +.display-success { + border-color: #22df56; +} + +/* 扩展一条CSS语句优于创建一个mixin, + 这是由Sass组合所有共享相同基样式的类的方式决定的。 + 如果使用mixin完成,width, height, 和border将会在 + 调用了该mixin的每条语句中重复。虽然它不至于会影响你的工作流, + 但它会在由Sass编译器生成的的文件中添加不必要的代码。*/ + + +/* 嵌套 +============================== */ + + + +/* Sass允许在选择子中嵌套选择子 */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: #FF0000; + } +} + +/* '&'将被父选择子替换。*/ +/* 你也可以嵌套伪类。 */ +/* 注意过度嵌套将导致你的代码难以维护。 +最佳实践推荐在嵌套时不超过3层。 +例如: */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: red; + + &:hover { + background-color: blue; + } + + a { + color: white; + } + } +} + +/* 编译成: */ + +ul { + list-style-type: none; + margin-top: 2em; +} + +ul li { + background-color: red; +} + +ul li:hover { + background-color: blue; +} + +ul li a { + color: white; +} + + + +/* 片段与导入 +============================== */ + + + +/* Sass允许你创建片段文件。它有助于你的Sass代码保持模块化。 + 片段文件应该以 '_' 开头,例如 _reset.css。 + 片段不会输出到CSS中。*/ + +/* 考虑以下的CSS,我们会将它们放入一个叫作_reset.css的文件中 */ + +html, +body, +ul, +ol { + margin: 0; + padding: 0; +} + +/* Sass提供的@import能用来将片段导入到文件中。 + 它与传统的CSS @import语句不同,不需要通过 + 另外的HTTP请求来获取导入的文件。 + Sass提取导入文件并将它与编译后的代码结合起来。 */ + +@import 'reset'; + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + +/* 编译成: */ + +html, body, ul, ol { + margin: 0; + padding: 0; +} + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + + + +/* 占位符选择子 +============================== */ + + + +/* 占位符在创建用于扩展的CSS语句时非常有用。 + 如果你想创建一条只通过@extend使用的CSS语句,你可以利用占位符来实现。 + 占位符以'%'而非'.'或'#'开头。占位符不会出现在编译后的CSS中 */ + +%content-window { + font-size: 14px; + padding: 10px; + color: #000; + border-radius: 4px; +} + +.message-window { + @extend %content-window; + background-color: #0000ff; +} + +/* 编译成: */ + +.message-window { + font-size: 14px; + padding: 10px; + color: #000; + border-radius: 4px; +} + +.message-window { + background-color: #0000ff; +} + + + +/* 数学运算 +============================== */ + + + +/* Sass提供以下的运算符: +, -, *, /, 和 %。它们 + 相比于使用你事先手工计算好了的数值,它们 + 对于直接在你的Sass文件中计算数值很有用。 + 以下是设置一个简单的两列设计的例子。*/ + +$content-area: 960px; +$main-content: 600px; +$sidebar-content: 300px; + +$main-size: $main-content / $content-area * 100%; +$sidebar-size: $sidebar-content / $content-area * 100%; +$gutter: 100% - ($main-size + $sidebar-size); + +body { + width: 100%; +} + +.main-content { + width: $main-size; +} + +.sidebar { + width: $sidebar-size; +} + +.gutter { + width: $gutter; +} + +/* 编译成: */ + +body { + width: 100%; +} + +.main-content { + width: 62.5%; +} + +.sidebar { + width: 31.25%; +} + +.gutter { + width: 6.25%; +} + +``` + +## SASS还是Sass? +该语言的名字,“Sass”,是一个词,不是一个缩写。 +你有没想过Sass是否是一个缩写词?你可能没有,但我反正会告诉你。 +该语言的名字是一个单词,不是一个缩写词。 +由于人们老是将它写成"SASS",语言的作者开玩笑地称它为"Syntactically Awesome StyleSheets"。 + + +## 实践Sass +如果你想在你的浏览器中尝试Sass,参阅[SassMeister](http://sassmeister.com/)。 +你可以选用任一种语法,只需进到设置页然后选择Sass或SCSS。 + + +## 兼容性 +Sass可以用于任何项目中,只要你有程序能将它编译成CSS即可。你还需要验证你所使用的CSS是否与你的目标浏览器兼容。 + +[QuirksMode CSS](http://www.quirksmode.org/css/)和[CanIUse](http://caniuse.com)对于检查兼容性来说都是不错的资源。 + + +## 延伸阅读资料 +* [Official Documentation](http://sass-lang.com/documentation/file.SASS_REFERENCE.html) +* [The Sass Way](http://thesassway.com/) 上提供了教程(初学者-高级)和文章。 diff --git a/zh-cn/scala-cn.html.markdown b/zh-cn/scala-cn.html.markdown index 58f5cd47..f3327b5b 100644 --- a/zh-cn/scala-cn.html.markdown +++ b/zh-cn/scala-cn.html.markdown @@ -4,12 +4,15 @@ filename: learnscala-zh.scala contributors: - ["George Petrov", "http://github.com/petrovg"] - ["Dominic Bou-Samra", "http://dbousamra.github.com"] + - ["Geoff Liu", "http://geoffliu.me"] translators: - ["Peiyong Lin", ""] + - ["Jinchang Ye", "http://github.com/alwayswithme"] + - ["Guodong Qu", "https://github.com/jasonqu"] lang: zh-cn --- -Scala - 一门可拓展性的语言 +Scala - 一门可拓展的语言 ```scala @@ -17,23 +20,31 @@ Scala - 一门可拓展性的语言 自行设置: 1) 下载 Scala - http://www.scala-lang.org/downloads - 2) unzip/untar 到你喜欢的地方,放在路径中的 bin 目录下 - 3) 在终端输入 scala,开启 Scala 的 REPL,你会看到提示符: + 2) unzip/untar 到您喜欢的地方,并把 bin 子目录添加到 path 环境变量 + 3) 在终端输入 scala,启动 Scala 的 REPL,您会看到提示符: scala> - 这就是所谓的 REPL,你现在可以在其中运行命令,让我们做到这一点: + 这就是所谓的 REPL (读取-求值-输出循环,英语: Read-Eval-Print Loop), + 您可以在其中输入合法的表达式,结果会被打印。 + 在教程中我们会进一步解释 Scala 文件是怎样的,但现在先了解一点基础。 */ -println(10) // 打印整数 10 -println("Boo!") // 打印字符串 "BOO!" +///////////////////////////////////////////////// +// 1. 基础 +///////////////////////////////////////////////// +// 单行注释开始于两个斜杠 -// 一些基础 +/* + 多行注释,如您之前所见,看起来像这样 +*/ // 打印并强制换行 println("Hello world!") +println(10) + // 没有强制换行的打印 print("Hello world") @@ -41,13 +52,19 @@ print("Hello world") // val 声明是不可变的,var 声明是可修改的。不可变性是好事。 val x = 10 // x 现在是 10 x = 20 // 错误: 对 val 声明的变量重新赋值 -var x = 10 -x = 20 // x 现在是 20 +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 @@ -64,9 +81,11 @@ true == false // false 2 - 1 // 1 5 * 3 // 15 6 / 2 // 3 +6 / 4 // 1 +6.0 / 4 // 1.5 -// 在 REPL 计算一个命令会返回给你结果的类型和值 +// 在 REPL 计算一个表达式会返回给您结果的类型和值 1 + 7 @@ -77,58 +96,190 @@ true == false // false 这意味着计算 1 + 7 的结果是一个 Int 类型的对象,其值为 8 - 1+7 的结果是一样的 + 注意 "res29" 是一个连续生成的变量名,用以存储您输入的表达式结果, + 您看到的输出可能不一样。 */ +"Scala strings are surrounded by double quotes" +'a' // Scala 的字符 +// '不存在单引号字符串' <= 这会导致错误 -// 包括函数在内,每一个事物都是对象。在 REPL 中输入: +// String 有常见的 Java 字符串方法 +"hello world".length +"hello world".substring(2, 6) +"hello world".replace("C", "3") -7 // 结果 res30: Int = 7 (res30 是一个生成的结果的 var 命名) +// 也有一些额外的 Scala 方法,另请参见:scala.collection.immutable.StringOps +"hello world".take(5) +"hello world".drop(5) -// 下一行给你一个接收一个 Int 类型并返回该数的平方的函数 -(x:Int) => x * x +// 字符串改写:留意前缀 "s" +val n = 45 +s"We have $n apples" // => "We have 45 apples" -// 你可以分配给函数一个标识符,像这样: -val sq = (x:Int) => x * x +// 在要改写的字符串中使用表达式也是可以的 +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" -/* 上面的例子说明 - - sq: Int => Int = <function1> +// 添加 "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" - 意味着这次我们给予了 sq 这样一个显式的名字给一个接受一个 Int 类型值并返回 一个 Int 类型值的函数 +// 未处理的字符串,忽略特殊字符。 +raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r." - sq 可以像下面那样被执行: -*/ +// 一些字符需要转义,比如字符串中的双引号 +"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown"" -sq(10) // 返回给你:res33: Int = 100. +// 三个双引号可以使字符串跨越多行,并包含引号 +val html = """<form id="daform"> + <p>Press belo', Joe</p> + <input type="submit"> + </form>""" -// Scala 允许方法和函数返回或者接受其它的函数或者方法作为参数。 -val add10: Int => Int = _ + 10 // 一个接受一个 Int 类型参数并返回一个 Int 类型值的函数 -List(1, 2, 3) map add10 // List(11, 12, 13) - add10 被应用到每一个元素 +///////////////////////////////////////////////// +// 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 +} -// 匿名函数可以被使用来代替有命名的函数: -List(1, 2, 3) map (x => x + 10) +// 如果函数体是单行表达式,{ } 可以省略: +def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y -// 下划线标志,如果匿名函数只有一个参数可以被使用来表示该参数变量 -List(1, 2, 3) map (_ + 10) +// 函数调用的语法是熟知的: +sumOfSquares(3, 4) // => 25 -// 如果你所应用的匿名块和匿名函数都接受一个参数,那么你甚至可以省略下划线 -List("Dom", "Bob", "Natalia") foreach println +// 在多数情况下 (递归函数是需要注意的例外), 函数返回值可以省略, +// 变量所用的类型推导一样会应用到函数返回值中: +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) // 这会抛出一个异常 +a(21) // 抛出异常 val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo") m("fork") m("spoon") -m("bottle") // 这会抛出一个异常 +m("bottle") // 抛出异常 val safeM = m.withDefaultValue("no lo se") safeM("bottle") @@ -137,9 +288,9 @@ val s = Set(1, 3, 7) s(0) s(1) -/* 查看 map 的文档 - * 点击[这里](http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map) - * 确保你可以读它 +/* 这里查看 map 的文档 - + * http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map + * 并确保你会阅读 */ @@ -154,13 +305,11 @@ s(1) (a, 2, "three") // 为什么有这个? - val divideInts = (x:Int, y:Int) => (x / y, x % y) -divideInts(10,3) // 函数 divideInts 返回你结果和余数 +divideInts(10,3) // 函数 divideInts 同时返回结果和余数 // 要读取元组的元素,使用 _._n,n是从1开始的元素索引 - val d = divideInts(10,3) d._1 @@ -168,234 +317,289 @@ d._1 d._2 +///////////////////////////////////////////////// +// 5. 面向对象编程 +///////////////////////////////////////////////// -// 选择器 - -s.map(sq) - -val sSquared = s. map(sq) - -sSquared.filter(_ < 10) +/* + 旁白: 教程中到现在为止我们所做的一切只是简单的表达式(值,函数等)。 + 这些表达式可以输入到命令行解释器中作为快速测试,但它们不能独立存在于 Scala + 文件。举个例子,您不能在 Scala 文件上简单的写上 "val x = 5"。相反 Scala 文件 + 允许的顶级结构是: -sSquared.reduce (_+_) + - objects + - classes + - case classes + - traits -// filter 函数接受一个预测(一个函数,形式为 A -> Boolean) 并选择出所有的元素满足这个预测 + 现在来解释这些是什么。 +*/ -List(1, 2, 3) filter (_ > 2) // List(3) -List( - Person(name = "Dom", age = 23), - Person(name = "Bob", age = 30) -).filter(_.age > 25) // List(Person("Bob", 30)) +// 类和其他语言的类相似,构造器参数在类名后声明,初始化在类结构体中完成。 +class Dog(br: String) { + // 构造器代码在此 + var breed: String = br + // 定义名为 bark 的方法,返回字符串 + def bark = "Woof, woof!" -// Scala 的 foreach 方法定义在特定的接受一个类型的集合上 -// 返回 Unit(一个 void 方法) -aListOfNumbers foreach (x => println(x)) -aListOfNumbers foreach println + // 值和方法作用域假定为 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!" -// For 包含 +// "object" 关键字创造一种类型和该类型的单例。 +// Scala 的 class 常常也含有一个 “伴生对象”,class 中包含每个实例的行为,所有实例 +// 共用的行为则放入 object 中。两者的区别和其他语言中类方法和静态方法类似。 +// 请注意 object 和 class 可以同名。 +object Dog { + def allKnownBreeds = List("pitbull", "shepherd", "retriever") + def createDog(breed: String) = new Dog(breed) +} -for { n <- s } yield sq(n) -val nSquared2 = for { n <- s } yield sq(n) +// Case 类是有额外内建功能的类。Scala 初学者常遇到的问题之一便是何时用类 +// 和何时用 case 类。界线比较模糊,但通常类倾向于封装,多态和行为。类中的值 +// 的作用域一般为 private , 只有方法是暴露的。case 类的主要目的是放置不可变 +// 数据。它们通常只有几个方法,且方法几乎没有副作用。 +case class Person(name: String, phoneNumber: String) -for { n <- nSquared2 if n < 10 } yield n +// 创造新实例,注意 case 类不需要使用 "new" 关键字 +val george = Person("George", "1234") +val kate = Person("Kate", "4567") -for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared +// 使用 case 类,您可以轻松得到一些功能,像 getters: +george.phoneNumber // => "1234" -/* 注意:这些不是 for 循环. 一个 for 循环的语义是 '重复'('repeat'), - 然而,一个 for-包含 定义了一个两个数据结合间的关系 */ +// 每个字段的相等性比较(无需覆盖 .equals) +Person("George", "1234") == Person("Kate", "1236") // => false +// 简单的拷贝方式 +// otherGeorge == Person("george", "9876") +val otherGeorge = george.copy(phoneNumber = "9876") +// 还有很多。case 类同时可以用于模式匹配,接下来会看到。 -// 循环和迭代 -1 to 5 -val r = 1 to 5 -r.foreach( println ) +// 敬请期待 Traits ! -r foreach println -// 注意:Scala 是相当宽容的当它遇到点和括号 - 分别地学习这些规则。 -// 这帮助你编写读起来像英语的 DSLs 和 APIs -(5 to 1 by -1) foreach ( println ) +///////////////////////////////////////////////// +// 6. 模式匹配 +///////////////////////////////////////////////// -// 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 then 10"); - x += 1 -} while (x < 10) +// 模式匹配是一个强大和常用的 Scala 特性。这是用模式匹配一个 case 类的例子。 +// 附注:不像其他语言, Scala 的 case 不需要 break, 其他语言中 switch 语句的 +// fall-through 现象不会发生。 -// 在 Scala中,尾递归是一种惯用的执行循环的方式。 -// 递归函数需要显示的返回类型,编译器不能推断出类型。 -// 这里它是 Unit。 -def showNumbersInRange(a:Int, b:Int):Unit = { - print(a) - if (a < b) - showNumbersInRange(a + 1, b) +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" -// 条件语句 - -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") + // 匹配类型: + case x: Double => "Got a Double: " + x -println(if (x == 10) "yeah" else "nope") -val text = if (x == 10) "yeah" else "nope" + // 匹配时指定条件 + case x: Int if x > 10000 => "Got a pretty big number!" -var i = 0 -while (i < 10) { println("i " + i); i+=1 } + // 像之前一样匹配 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" -// 类名是 Dog -class Dog { - //bark 方法,返回字符串 - def bark: String = { - // the body of the method - "Woof, woof!" - } + // 模式可以嵌套 + case List(List((1, 2,"YAY"))) => "Got a list of list of tuple" } -// 类可以包含几乎其它的构造,包括其它的类, -// 函数,方法,对象,case 类,特性等等。 - - +// 事实上,你可以对任何有 "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" +} -// Case 类 -case class Person(name:String, phoneNumber:String) +///////////////////////////////////////////////// +// 7. 函数式编程 +///////////////////////////////////////////////// -Person("George", "1234") == Person("Kate", "1236") +// 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 me = Person("George", "1234") -me match { case Person(name, number) => { - "We matched someone : " + name + ", phone : " + number }} +// 组合子 -me match { case Person(name, number) => "Match : " + name; case _ => "Hm..." } +// 译注: val sq: Int => Int = x => x * x +s.map(sq) -me match { case Person("George", number) => "Match"; case _ => "Hm..." } +val sSquared = s. map(sq) -me match { case Person("Kate", number) => "Match"; case _ => "Hm..." } +sSquared.filter(_ < 10) -me match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" } +sSquared.reduce (_+_) -val kate = Person("Kate", "1234") +// 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)) -kate match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" } +// 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 email = "(.*)@(.*)".r // 在字符串上调用 r 会使它变成一个正则表达式 +val nSquared2 = for { n <- s } yield sq(n) -val email(user, domain) = "henry@zkpr.com" +for { n <- nSquared2 if n < 10 } yield n -"mrbean@pyahoo.com" match { - case email(name, domain) => "I know your name, " + name -} +for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared +/* 注意,这些不是 for 循环,for 循环的语义是‘重复’,然而 for 推导式定义 + 两个数据集合的关系。 */ -// 字符串 +///////////////////////////////////////////////// +// 8. 隐式转换 +///////////////////////////////////////////////// -"Scala 字符串被双引号包围" // -'a' // Scala 字符 -'单引号的字符串不存在' // 错误 -"字符串拥有通常的 Java 方法定义在其上".length -"字符串也有额外的 Scala 方法".reverse +/* 警告 警告: 隐式转换是 Scala 中一套强大的特性,因此容易被滥用。 + * Scala 初学者在理解它们的工作原理和最佳实践之前,应抵制使用它的诱惑。 + * 我们加入这一章节仅因为它们在 Scala 的库中太过常见,导致没有用隐式转换的库 + * 就不可能做有意义的事情。这章节主要让你理解和使用隐式转换,而不是自己声明。 + */ -// 参见: scala.collection.immutable.StringOps +// 可以通过 "implicit" 声明任何值(val, 函数,对象等)为隐式值, +// 请注意这些例子中,我们用到第5部分的 Dog 类。 +implicit val myImplicitInt = 100 +implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed) -println("ABCDEF".length) -println("ABCDEF".substring(2, 6)) -println("ABCDEF".replace("C", "3")) +// implicit 关键字本身不改变值的行为,所以上面的值可以照常使用。 +myImplicitInt + 2 // => 102 +myImplicitFunction("Pitbull").breed // => "Golden Pitbull" -val n = 45 -println(s"We have $n apples") +// 区别在于,当另一段代码“需要”隐式值时,这些值现在有资格作为隐式值。 +// 一种情况是隐式函数参数。 +def sendGreetings(toWhom: String)(implicit howMany: Int) = + s"Hello $toWhom, $howMany blessings to you and yours!" -val a = Array(11, 9, 6) -println(s"My second daughter is ${a(2-1)} years old") +// 如果提供值给 “howMany”,函数正常运行 +sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!" -// 一些字符需要被转义,举例来说,字符串中的双引号: -val a = "They stood outside the \"Rose and Crown\"" +// 如果省略隐式参数,会传一个和参数类型相同的隐式值, +// 在这个例子中, 是 “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] = ... -val html = """<form id="daform"> - <p>Press belo', Joe</p> - | <input type="submit"> - </form>""" +// 编译器寻找隐式值另一种情况是你调用方法时 +// obj.method(...) +// 但 "obj" 没有一个名为 "method" 的方法。这样的话,如果有一个参数类型为 A +// 返回值类型为 B 的隐式转换,obj 的类型是 A,B 有一个方法叫 "method" ,这样 +// 转换就会被应用。所以作用域里有上面的 myImplicitFunction, 我们可以这样做: +"Retriever".breed // => "Golden Retriever" +"Sheperd".bark // => "Woof, woof!" +// 这里字符串先被上面的函数转换为 Dog 对象,然后调用相应的方法。 +// 这是相当强大的特性,但再次提醒,请勿轻率使用。 +// 事实上,当你定义上面的隐式函数时,编译器会作出警告,除非你真的了解 +// 你正在做什么否则不要使用。 -// 应用结果和组织 +///////////////////////////////////////////////// +// 9. 杂项 +///////////////////////////////////////////////// -// import +// 导入类 import scala.collection.immutable.List -// Import 所有的子包 +// 导入所有子包 import scala.collection.immutable._ -// 在一条语句中 Import 多个类 +// 一条语句导入多个类 import scala.collection.immutable.{List, Map} -// 使用 '=>' 来重命名一个 import +// 使用 ‘=>’ 对导入进行重命名 import scala.collection.immutable.{ List => ImmutableList } -// import 除了一些类的其它所有的类。下面的例子除去了 Map 类和 Set 类: +// 导入所有类,排除其中一些。下面的语句排除了 Map 和 Set: import scala.collection.immutable.{Map => _, Set => _, _} -// 在 scala 源文件中,你的程序入口点使用一个拥有单一方法 main 的对象来定义: - +// 在 Scala 文件用 object 和单一的 main 方法定义程序入口: object Application { def main(args: Array[String]): Unit = { // stuff goes here. } } -// 文件可以包含多个类和对象。由 scalac 来编译 +// 文件可以包含多个 class 和 object,用 scalac 编译源文件 // 输入和输出 -// 一行一行读取文件 +// 按行读文件 import scala.io.Source -for(line <- Source.fromPath("myfile.txt").getLines()) +for(line <- Source.fromFile("myfile.txt").getLines()) println(line) -// 使用 Java 的 PrintWriter 来写文件 - +// 用 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() ``` diff --git a/zh-cn/standard-ml-cn.html.markdown b/zh-cn/standard-ml-cn.html.markdown new file mode 100644 index 00000000..269d6bc3 --- /dev/null +++ b/zh-cn/standard-ml-cn.html.markdown @@ -0,0 +1,438 @@ +--- +language: "Standard ML" +contributors: + - ["Simon Shine", "http://shine.eu.org/"] + - ["David Pedersen", "http://lonelyproton.com/"] + - ["James Baker", "http://www.jbaker.io/"] + - ["Leo Zovic", "http://langnostic.inaimathi.ca/"] +filename: standard-ml-cn.html +translators: + - ["Buqian Zheng", "https://github.com/zhengbuqian"] +lang: zh-cn +--- + +Standard ML是一门拥有类型推断和一些副作用的函数式编程语言。学习Standard ML的一些 +难点在于:递归、模式匹配和类型推断(猜测正确的类型但是决不允许隐式类型转换)。与Haskell的 +不同之处在于Standard ML拥有引用,允许对变量进行更新。 + +```ocaml +(* Standard ML的注释以 (* 开头,以 *) 结束。注释可以嵌套,也就意味着所有的 (* 标签都 + 需要由一个 *) 结束。这条注释就是两个嵌套的注释的例子。*) + +(* 一个Standard ML程序包括声明,例如值声明: *) +val rent = 1200 +val phone_no = 5551337 +val pi = 3.14159 +val negative_number = ~15 (* 是的,一元运算符用波浪符号`~`表示 *) + +(* 你当然也可以显示的声明类型,但这并不是必须的,因为ML会自动推断出值的类型。*) +val diameter = 7926 : int +val e = 2.718 : real +val name = "Bobby" : string + +(* 同样重要的还有函数: *) +fun is_large(x : int) = if x > 37 then true else false + +(* 浮点数被叫做实数: "real". *) +val tau = 2.0 * pi (* 两个real可以相乘 *) +val twice_rent = 2 * rent (* 两个int也可以相乘 *) +(* val meh = 1.25 * 10 *) (* 但是你不能让int和real相乘。 *) +val yeh = 1.25 * (Real.fromInt 10) (* ...除非你显示的把一个转换为另一个*) + +(* +, - 和 * 被重载过,所以可以作用于int和real。*) +(* 但是除法有单独的运算符: *) +val real_division = 14.0 / 4.0 (* 结果是 3.5 *) +val int_division = 14 div 4 (* 结果是 3, 向下取整 *) +val int_remainder = 14 mod 4 (* 结果是 2, 因为 3*4 = 12 *) + +(* ~ 有时其实是函数 (比如被放在变量前面的时候) *) +val negative_rent = ~(rent) (* 即使rent是"real"也正确 *) + +(* 当然也有布尔值和相关的运算符 *) +val got_milk = true +val got_bread = false +val has_breakfast = got_milk andalso got_bread (* 'andalso' 是运算符 *) +val has_something = got_milk orelse got_bread (* 'orelse' 是运算符 *) +val is_sad = not(has_something) (* not 是一个函数 *) + +(* 很多值都可以用判等性运算符进行比较: = 和 <> *) +val pays_same_rent = (rent = 1300) (* false *) +val is_wrong_phone_no = (phone_no <> 5551337) (* false *) + +(* <> 运算符就是其他大部分语言里的 != 。 *) +(* 'andalso' 和 'orelse' 在很多其他语言里被叫做 && 和 || 。 *) + +(* 实际上,上面大部分的圆括号都是不需要的。比如表达上面内容的一些不同的方式: *) +fun is_large x = x > 37 +val is_sad = not has_something +val pays_same_rent = rent = 1300 (* 看起来很奇怪,但是就是这样的。 *) +val is_wrong_phone_no = phone_no <> 5551337 +val negative_rent = ~rent (* ~ rent (注意空格) 也正确 *) + +(* 圆括号大部分时候用来把东西们组合在一起 *) +val some_answer = is_large (5 + 5) (* 没有圆括号的话会出错! *) +(* val some_answer = is_large 5 + 5 *) (* 会被理解为: (is_large 5) + 5. 错了! *) + +(* 除了boolean, int, real,Standard ML也有char和string *) +val foo = "Hello, World!\n" (* \n是换行的转移字符 *) +val one_letter = #"a" (* 这种酷炫的语法表示一个字符a *) + +val combined = "Hello " ^ "there, " ^ "fellow!\n" (* 拼接字符串 *) + +val _ = print foo (* 你可以打印一些东西,这儿我们队打印的结果并不感兴趣,因此 *) +val _ = print combined (* 用 _ 把结果丢掉了 *) +(* val _ = print one_letter *) (* 只有字符串才能被这样打印 *) + + +val bar = [ #"H", #"e", #"l", #"l", #"o" ] (* SML 也有列表! *) +(* val _ = print bar *) (* 然而列表和string是不同的 *) + +(* 当然这二者可以相互转换。String是一个库,implode和size是这个库里接受string作为 + 参数的函数。*) +val bob = String.implode bar (* 结果是 "Hello" *) +val bob_char_count = String.size bob (* 结果是 5 *) +val _ = print (bob ^ "\n") (* 为了好看加了个换行符 *) + +(* 列表可以包含任意类型的元素 *) +val numbers = [1, 3, 3, 7, 229, 230, 248] (* : int list *) +val names = [ "Fred", "Jane", "Alice" ] (* : string list *) + +(* 列表甚至可以包含列表! *) +val groups = [ [ "Alice", "Bob" ], + [ "Huey", "Dewey", "Louie" ], + [ "Bonnie", "Clyde" ] ] (* : string list list *) + +val number_count = List.length numbers (* 结果是 7 *) + +(* 你可以使用 :: 操作符把单个值放到同样类型列表的最前面。 + :: 叫做con操作符(名字来自Lisp) *) +val more_numbers = 13 :: numbers (* 结果是 [13, 1, 3, 3, 7, ...] *) +val more_groups = ["Batman","Superman"] :: groups + +(* 拥有同样类型元素的列表可以使用 @ 操作符连接起来 *) +val guest_list = [ "Mom", "Dad" ] @ [ "Aunt", "Uncle" ] + +(* 使用 :: 操作符也能完成这项工作。但是这有点绕,因为左手边必须是单个元素 + 而右边必须是这种元素的列表 *) +val guest_list = "Mom" :: "Dad" :: [ "Aunt", "Uncle" ] +val guest_list = "Mom" :: ("Dad" :: ("Aunt" :: ("Uncle" :: []))) + +(* 如果你有很多同样类型的列表,也可以整个拼接成一个。 *) +val everyone = List.concat groups (* [ "Alice", "Bob", "Huey", ... ] *) + +(* 列表可以包含任意(无限)数量的元素 *) +val lots = [ 5, 5, 5, 6, 4, 5, 6, 5, 4, 5, 7, 3 ] (* still just an int list *) + +(* 但是列表只能包含一种类型的元素 *) +(* val bad_list = [ 1, "Hello", 3.14159 ] : ??? list *) + +(* 而元组Tuples则可以包含有限固定数量的不同类型的元素 *) +val person1 = ("Simon", 28, 3.14159) (* : string * int * real *) + +(* 你甚至可以让列表和元组相互嵌套 *) +val likes = [ ("Alice", "ice cream"), + ("Bob", "hot dogs"), + ("Bob", "Alice") ] (* : (string * string) list *) + +val mixup = [ ("Alice", 39), + ("Bob", 37), + ("Eve", 41) ] (* : (string * int) list *) + +val good_bad_stuff = + (["ice cream", "hot dogs", "chocolate"], + ["liver", "paying the rent" ]) (* : string list * string list *) + +(* 记录Record是每个位置带名字的元组 *) + +val rgb = { r=0.23, g=0.56, b=0.91 } (* : {b:real, g:real, r:real} *) + +(* 使用Record时不需要提前声明每个位置的名字。 有不同名字的Record属于不同的类型 + 即使他们的值的类型是相同的。比如说:*) +val Hsl = { H=310.3, s=0.51, l=0.23 } (* : {H:real, l:real, s:real} *) +val Hsv = { H=310.3, s=0.51, v=0.23 } (* : {H:real, s:real, v:real} *) + +(* ...如果你想判断 `Hsv = Hsl` 或者 `rgb = Hsl` 的话,会得到一个类型错误。虽然他们都包含3个 + real,但是由于名字不同,其类型也不同。 *) + +(* 可以使用 # 符号取出元组的值 *) + +val H = #H Hsv (* : real *) +val s = #s Hsl (* : real *) + +(* 函数! *) +fun add_them (a, b) = a + b (* 一个简单的加法函数 *) +val test_it = add_them (3, 4) (* 结果是 7 *) + +(* 复杂函数通常会为了可读性写成多行 *) +fun thermometer temp = + if temp < 37 + then "Cold" + else if temp > 37 + then "Warm" + else "Normal" + +val test_thermo = thermometer 40 (* 结果是 "Warm" *) + +(* if 实际上是表达式而不是声明。一个函数体只可以包含一个表达式。但是还是有一些小技巧 + 让一个函数做更多的事。 *) + +(* 函数也可以使用调用自己的结果 (递归!) *) +fun fibonacci n = + if n = 0 then 0 else (* 终止条件 *) + if n = 1 then 1 else (* 终止条件 *) + fibonacci (n - 1) + fibonacci (n - 2) (* 递归 *) + +(* 有的时候,手写出递归函数的执行过程能帮助理解递归概念: + + fibonacci 4 + ~> fibonacci (4 - 1) + fibonacci (4 - 2) + ~> fibonacci 3 + fibonacci 2 + ~> (fibonacci (3 - 1) + fibonacci (3 - 2)) + fibonacci 2 + ~> (fibonacci 2 + fibonacci 1) + fibonacci 2 + ~> ((fibonacci (2 - 1) + fibonacci (2 - 2)) + fibonacci 1) + fibonacci 2 + ~> ((fibonacci 1 + fibonacci 0) + fibonacci 1) + fibonacci 2 + ~> ((1 + fibonacci 0) + fibonacci 1) + fibonacci 2 + ~> ((1 + 0) + fibonacci 1) + fibonacci 2 + ~> (1 + fibonacci 1) + fibonacci 2 + ~> (1 + 1) + fibonacci 2 + ~> 2 + fibonacci 2 + ~> 2 + (fibonacci (2 - 1) + fibonacci (2 - 2)) + ~> 2 + (fibonacci (2 - 1) + fibonacci (2 - 2)) + ~> 2 + (fibonacci 1 + fibonacci 0) + ~> 2 + (1 + fibonacci 0) + ~> 2 + (1 + 0) + ~> 2 + 1 + ~> 3 第四个斐波那契数 + + *) + +(* 函数不能改变它引用的值。它只能暂时的使用同名的新变量来覆盖这个值。也就是说,变量其实是 + 常数,只有在递归的时候才表现的比较像变量。因此,变量也被叫做值绑定。举个例子: *) + +val x = 42 +fun answer(question) = + if question = "What is the meaning of life, the universe and everything?" + then x + else raise Fail "I'm an exception. Also, I don't know what the answer is." +val x = 43 +val hmm = answer "What is the meaning of life, the universe and everything?" +(* 现在 hmm 的值是 42。 这是因为函数 answer 引用的x是函数定义之前的x。 *) + +(* 函数通过接受一个元组来接受多个参数。 *) +fun solve2 (a : real, b : real, c : real) = + ((~b + Math.sqrt(b * b - 4.0 * a * c)) / (2.0 * a), + (~b - Math.sqrt(b * b - 4.0 * a * c)) / (2.0 * a)) + +(* 有时候同样的计算会被计算多次,因此把结果保存下来以重复使用是很有必要的。 + 这时可以使用 let 绑定。 *) +fun solve2 (a : real, b : real, c : real) = + let val discr = b * b - 4.0 * a * c + val sqr = Math.sqrt discr + val denom = 2.0 * a + in ((~b + sqr) / denom, + (~b - sqr) / denom) + end + +(* 模式匹配是函数式编程的一个精巧的部分,它是实现 if 的另一种方式。 + 斐波那契函数可以被重写为如下方式: *) +fun fibonacci 0 = 0 (* 终止条件 *) + | fibonacci 1 = 1 (* 终止条件 *) + | fibonacci n = fibonacci (n - 1) + fibonacci (n - 2) (* 递归 *) + +(* 模式匹配也可以用于比如元组、列表和记录的复合类型。"fun solve2 (a, b, c) = ..." + 的写法实际上也是对于一个三元素元组的模式匹配。类似但是比较不直观的是你也可以从列表的开头 + 对列表元素进行匹配。 *) +fun first_elem (x::xs) = x +fun second_elem (x::y::xs) = y +fun evenly_positioned_elems (odd::even::xs) = even::evenly_positioned_elems xs + | evenly_positioned_elems [odd] = [] (* 终止条件:丢弃结果 *) + | evenly_positioned_elems [] = [] (* 终止条件 *) + +(* 匹配记录的时候,比如使用每个位置的名字,每个位置的值都需要绑定,但是顺序并不重要。 *) + +fun rgbToTup {r, g, b} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *) +fun mixRgbToTup {g, b, r} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *) + +(* 如果传入参数 {r=0.1, g=0.2, b=0.3},上面的两个函数都会返回 (0.1, 0.2, 0.3)。 + 但是传入参数 {r=0.1, g=0.2, b=0.3, a=0.4} 的话则会得到类型错误 *) + +(* 高阶函数: 可以接受其他函数作为参数的函数 + 函数只不过是另一种类型的值,不需要依附与一个名字而存在。 + 没有名字的函数被叫做匿名函数或者lambda表达式或者闭包(因为匿名函数也依赖于词法作用域)*) +val is_large = (fn x => x > 37) +val add_them = fn (a,b) => a + b +val thermometer = + fn temp => if temp < 37 + then "Cold" + else if temp > 37 + then "Warm" + else "Normal" + +(* 下面的代码就是用了匿名函数,结果是 "ColdWarm" *) +val some_result = (fn x => thermometer (x - 5) ^ thermometer (x + 5)) 37 + +(* 这是一个作用于列表的高阶函数 *) +(* map f l + 把f从左至右作用于l的每一个元素,并返回结果组成的列表。 *) +val readings = [ 34, 39, 37, 38, 35, 36, 37, 37, 37 ] (* 先定义一个列表 *) +val opinions = List.map thermometer readings (* 结果是 [ "Cold", "Warm", ... ] *) + +(* filter 函数用于筛选列表 *) +val warm_readings = List.filter is_large readings (* 结果是 [39, 38] *) + +(* 你也可以创建自己的高阶函数。函数也可以通过 curry 来接受多个参数。 + 从语法上来说,curry就是使用空格来分隔参数,而不是逗号和括号。 *) +fun map f [] = [] + | map f (x::xs) = f(x) :: map f xs + +(* map 的类型是 ('a -> 'b) -> 'a list -> 'b list ,这就是多态。 *) +(* 'a 被叫做类型变量 *) + + +(* 函数可以被声明为中缀的。 *) +val plus = add_them (* plus 现在和 add_them 是同一个函数。 *) +infix plus (* plus 现在是一个中缀操作符。 *) +val seven = 2 plus 5 (* seven 现在被绑定上了 7 *) + +(* 函数也可以在声明之前就声明为中缀 *) +infix minus +fun x minus y = x - y (* 这样有点不容易判断哪个是参数。 *) +val four = 8 minus 4 (* four 现在被绑定上了 4 *) + +(* 中缀函数/操作符也可以使用 'op' 函数变回前缀函数。 *) +val n = op + (5, 5) (* n is now 10 *) + +(* 'op' 在结合高阶函数的时候非常有用,因为高阶函数接受的是函数而不是操作符作为参数。 + 大部分的操作符其实都是中缀函数。 *) +(* foldl f init [x1, x2, ..., xn] + 返回 + f(xn, ...f(x2, f(x1, init))...) + 或者如果列表为空时返回 init *) +val sum_of_numbers = foldl op+ 0 [1, 2, 3, 4, 5] + + +(* 可以很方便的使用 datatype 定义或简单或复杂的数据结构。 *) +datatype color = Red | Green | Blue + +(* 这个函数接受 color 之一作为参数。 *) +fun say(col) = + if col = Red then "You are red!" else + if col = Green then "You are green!" else + if col = Blue then "You are blue!" else + raise Fail "Unknown color" + +val _ = print (say(Red) ^ "\n") + +(* datatype 经常和模式匹配一起使用。 *) +fun say Red = "You are red!" + | say Green = "You are green!" + | say Blue = "You are blue!" + | say _ = raise Fail "Unknown color" + + +(* 一个二叉树 datatype *) +datatype 'a btree = Leaf of 'a + | Node of 'a btree * 'a * 'a btree (* 三个参数的构造器 *) + +(* 一颗二叉树: *) +val myTree = Node (Leaf 9, 8, Node (Leaf 3, 5, Leaf 7)) + +(* 画出来应该是这个样子: + + 8 + / \ + leaf -> 9 5 + / \ + leaf -> 3 7 <- leaf + *) + +(* 这个函数计算所有节点值的和。 *) +fun count (Leaf n) = n + | count (Node (leftTree, n, rightTree)) = count leftTree + n + count rightTree + +val myTreeCount = count myTree (* myTreeCount is now bound to 32 *) + + +(* 异常! *) +(* 使用关键字 'raise' 来抛出异常: *) +fun calculate_interest(n) = if n < 0.0 + then raise Domain + else n * 1.04 + +(* 使用 "handle" 关键字来处理异常 *) +val balance = calculate_interest ~180.0 + handle Domain => ~180.0 (* x 现在的值是 ~180.0 *) + +(* 某些异常还包含额外信息 *) +(* 一些内建异常的例子: *) +fun failing_function [] = raise Empty (* 空列表异常 *) + | failing_function [x] = raise Fail "This list is too short!" + | failing_function [x,y] = raise Overflow (* 用作计算 *) + | failing_function xs = raise Fail "This list is too long!" + +(* 使用 'handle' 时也可以使用模式匹配来保证异常都被处理。 *) +val err_msg = failing_function [1,2] handle Fail _ => "Fail was raised" + | Domain => "Domain was raised" + | Empty => "Empty was raised" + | _ => "Unknown exception" + +(* err_msg 的值会是 "Unknown exception" + 因为 Overflow 没有在模式中列出,因此匹配到了通配符_。 *) + +(* 我们也可以定义自己的异常 *) +exception MyException +exception MyExceptionWithMessage of string +exception SyntaxError of string * (int * int) + +(* 文件读写! *) +(* 把一首诗写进文件: *) +fun writePoem(filename) = + let val file = TextIO.openOut(filename) + val _ = TextIO.output(file, "Roses are red,\nViolets are blue.\n") + val _ = TextIO.output(file, "I have a gun.\nGet in the van.\n") + in TextIO.closeOut(file) + end + +(* 把一首诗读进一个字符串列表: *) +fun readPoem(filename) = + let val file = TextIO.openIn filename + val poem = TextIO.inputAll file + val _ = TextIO.closeIn file + in String.tokens (fn c => c = #"\n") poem + end + +val _ = writePoem "roses.txt" +val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", + "Violets are blue.", + "I have a gun.", + "Get in the van." ] *) + +(* 我们还可以创建指向值的引用,引用可以被更新。 *) +val counter = ref 0 (* 使用 ref 函数创建一个引用。 *) + +(* 使用赋值运算符给引用复制 *) +fun set_five reference = reference := 5 + +(* 使用解引用运算符得到引用的值 *) +fun equals_five reference = !reference = 5 + +(* 递归很复杂的时候,也可以使用 while 循环 *) +fun decrement_to_zero r = if !r < 0 + then r := 0 + else while !r >= 0 do r := !r - 1 + +(* 这将会返回 unit (也就是什么都没有,一个0元素的元组) *) + +(* 要返回值,可以使用分号来分开表达式。 *) +fun decrement_ret x y = (x := !x - 1; y) +``` + +## 阅读更多 + +* 安装交互式编译器 (REPL),如: + [Poly/ML](http://www.polyml.org/), + [Moscow ML](http://mosml.org), + [SML/NJ](http://smlnj.org/). +* 上Coursera上的课程 [Programming Languages](https://www.coursera.org/course/proglang). +* 购买 Larry C. Paulson 写的 *ML for the Working Programmer* 书。 +* 使用 [StackOverflow's sml 标签](http://stackoverflow.com/questions/tagged/sml). diff --git a/zh-cn/swift-cn.html.markdown b/zh-cn/swift-cn.html.markdown index b9696c72..c25b2918 100644 --- a/zh-cn/swift-cn.html.markdown +++ b/zh-cn/swift-cn.html.markdown @@ -5,223 +5,603 @@ contributors: - ["Grant Timmerman", "http://github.com/grant"] translators: - ["Xavier Yao", "http://github.com/xavieryao"] + - ["Joey Huang", "http://github.com/kamidox"] + - ["CY Lim", "http://github.com/cylim"] lang: zh-cn --- -Swift 是Apple 开发的用于iOS 和OS X 开发的编程语言。Swift 于2014年Apple WWDC (全球开发者大会)中被引入,用以与Objective-C 共存,同时对错误代码更具弹性。Swift 由Xcode 6 beta 中包含的LLVM编译器编译。 +Swift 是 Apple 开发的用于 iOS 和 OS X 开发的编程语言。Swift 于2014年 Apple WWDC (全球开发者大会)中被引入,用以与 Objective-C 共存,同时对错误代码更具弹性。Swift 由 Xcode 6 beta 中包含的 LLVM 编译器编译。 -参阅:Apple's [getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html) ——一个完整的Swift 教程 +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/DevelopiOSAppsSwift/) ——一个完整的Swift 教程 ```swift +// 导入外部模块 +import UIKit + // -// 基础 +// MARK: 基础 // -println("Hello, world") +// XCODE 支持给注释代码作标记,这些标记会列在 XCODE 的跳转栏里,支持的标记为 +// MARK: 普通标记 +// TODO: TODO 标记 +// FIXME: FIXME 标记 + +// Swift2.0 println() 及 print() 已经整合成 print()。 +print("Hello, world") // 这是原本的 println(),会自动进入下一行 +print("Hello, world", terminator: "") // 如果不要自动进入下一行,需设定结束符为空串 + +// 变量 (var) 的值设置后可以随意改变 +// 常量 (let) 的值设置后不能改变 var myVariable = 42 +let øπΩ = "value" // 可以支持 unicode 变量名 +let π = 3.1415926 let myConstant = 3.1415926 -let explicitDouble: Double = 70 -let label = "some text " + String(myVariable) // Casting -let piText = "Pi = \(myConstant)" // String interpolation -var optionalString: String? = "optional" // Can be nil -optionalString = nil +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 + print("Not printed") + let buildValue = 3 +#else + let buildValue = 7 +#endif +print("Build value: \(buildValue)") // Build value: 7 + +/* + Optionals 是 Swift 的新特性,它允许你存储两种状态的值给 Optional 变量:有效值或 None 。 + 可在值名称后加个问号 (?) 来表示这个值是 Optional。 + + Swift 要求所有的 Optinal 属性都必须有明确的值,如果为空,则必须明确设定为 nil + + Optional<T> 是个枚举类型 +*/ +var someOptionalString: String? = "optional" // 可以是 nil +// 下面的语句和上面完全等价,上面的写法更推荐,因为它更简洁,问号 (?) 是 Swift 提供的语法糖 +var someOptionalString2: Optional<String> = "optional" + +if someOptionalString != nil { + // 变量不为空 + if someOptionalString!.hasPrefix("opt") { + print("has the prefix") + } + + let empty = someOptionalString?.isEmpty +} +someOptionalString = nil + +/* + 使用 (!) 可以解决无法访问optional值的运行错误。若要使用 (!)来强制解析,一定要确保 Optional 里不是 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 = [String]() // 使用 let 定义常量,此时 emptyArray 数组不能添加或删除内容 +let emptyArray2 = Array<String>() // 与上一语句等价,上一语句更常用 +var emptyMutableArray = [String]() // 使用 var 定义变量,可以向 emptyMutableArray 添加数组元素 +var explicitEmptyMutableStringArray: [String] = [] // 与上一语句等价 // 字典 var occupations = [ - "Malcolm": "Captain", - "kaylee": "Mechanic" + "Malcolm": "Captain", + "kaylee": "Mechanic" ] -occupations["Jayne"] = "Public Relations" -let emptyDictionary = Dictionary<String, Float>() +occupations["Jayne"] = "Public Relations" // 修改字典,如果 key 不存在,自动添加一个字典元素 +let emptyDictionary = [String: Float]() // 使用 let 定义字典常量,字典常量不能修改里面的值 +let emptyDictionary2 = Dictionary<String, Float>() // 与上一语句类型等价,上一语句更常用 +var emptyMutableDictionary = [String: Float]() // 使用 var 定义字典变量 +var explicitEmptyMutableDictionary: [String: Float] = [:] // 与上一语句类型等价 // -// 控制流 +// MARK: 控制流 // -// 用于数组的for 循环 +// 数组的 for 循环 let myArray = [1, 1, 2, 3, 5] for value in myArray { - if value == 1 { - println("One!") - } else { - println("Not one!") - } + if value == 1 { + print("One!") + } else { + print("Not one!") + } } -// 用于字典的for 循环 +// 字典的 for 循环 +var dict = ["one": 1, "two": 2] for (key, value) in dict { - println("\(key): \(value)") + print("\(key): \(value)") } -// 用于区间的for 循环 -for i in -1...1 { // [-1, 0, 1] - println(i) +// 区间的 loop 循环:其中 `...` 表示闭环区间,即[-1, 3];`..<` 表示半开闭区间,即[-1,3) +for i in -1...shoppingList.count { + print(i) } -// 使用 .. 表示的区间不包含最后一个元素 [-1,0,1) +shoppingList[1...2] = ["steak", "peacons"] +// 可以使用 `..<` 来去掉最后一个元素 // while 循环 var i = 1 while i < 1000 { - i *= 2 + i *= 2 } -// do-while 循环 -do { - println("hello") +// repeat-while 循环 +repeat { + print("hello") } while 1 == 2 -// Switch +// 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." + 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 x where x.hasSuffix("pepper"): - let vegetableComment = "Is it a spicy \(x)?" -default: // 必须 (为了覆盖所有可能的输入) - let vegetableComment = "Everything tastes good in soup." + 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)." + return "Hello \(name), today is \(day)." } -greet("Bob", "Tuesday") +greet("Bob", day: "Tuesday") -// 使用多元数组返回多返回值的函数 -func getGasPrices() -> (Double, Double, Double) { - return (3.59, 3.69, 3.79) +// 第一个参数表示外部参数名和内部参数名使用同一个名称。 +// 第二个参数表示外部参数名使用 `externalParamName` ,内部参数名使用 `localParamName` +func greet2(requiredName requiredName: String, externalParamName localParamName: String) -> String { + return "Hello \(requiredName), the day is \(localParamName)" } +greet2(requiredName:"John", externalParamName: "Sunday") // 调用时,使用命名参数来指定参数的值 -// 不定参数 -func setup(numbers: Int...) {} +// 函数可以通过元组 (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 +print(price1 == pricesTuple.1) // true +print("Gas price: \(price)") + +// 可变参数 +func setup(numbers: Int...) { + // 可变参数是个数组 + let _ = numbers[0] + let _ = numbers.count +} -// 传递、返回函数 +// 函数变量以及函数作为返回值返回 func makeIncrementer() -> (Int -> Int) { - func addOne(number: Int) -> Int { - return 1 + number - } - return addOne + 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, b: &someIntB) +print(someIntB) // 7 + // -// 闭包 +// MARK: 闭包 // +var numbers = [1, 2, 6] -// 函数是特殊的闭包({}) +// 函数是闭包的一个特例 ({}) -// 闭包示例. -// `->` 分隔参数和返回类型 -// `in` 分隔闭包头和闭包体 +// 闭包实例 +// `->` 分隔了闭包的参数和返回值 +// `in` 分隔了闭包头 (包括参数及返回值) 和闭包体 +// 下面例子中,`map` 的参数是一个函数类型,它的功能是把数组里的元素作为参数,逐个调用 `map` 参数传递进来的函数。 numbers.map({ - (number: Int) -> Int in - let result = 3 * number - return result - }) + (number: Int) -> Int in + let result = 3 * number + return result +}) -// 当类型已知时,可以这样做: -var numbers = [1, 2, 6] +// 当闭包的参数类型和返回值都是己知的情况下,且只有一个语句作为其返回值时,我们可以简化闭包的写法 numbers = numbers.map({ number in 3 * number }) +// 我们也可以使用 $0, $1 来指代第 1 个,第 2 个参数,上面的语句最终可简写为如下形式 +// numbers = numbers.map({ $0 * 3 }) + print(numbers) // [3, 6, 18] +// 简洁的闭包 +numbers = numbers.sort { $0 > $1 } + +print(numbers) // [18, 6, 3] + // -// 类 +// MARK: 结构体 // -// 类的全部方法和属性都是public 的 -// 如果你在一个数据结构中只需储存数据, -// 应使用 `struct` +// 结构体和类非常类似,可以有属性和方法 -// 集成自`Shape` 类的简单的类`Square -class Rect: Shape { - var sideLength: Int = 1 +struct NamesTable { + let names: [String] - // Custom getter and setter property - var perimeter: Int { - get { - return 4 * sideLength + // 自定义下标运算符 + subscript(index: Int) -> String { + return names[index] } - set { - sideLength = newValue / 4 +} + +// 结构体有一个自动生成的隐含的命名构造函数 +let namesTable = NamesTable(names: ["Me", "Them"]) +let name = namesTable[1] +print("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) { + print(someIdentifier) + } + // `didSet` 的参数是已经被覆盖掉的旧的值,参数名也可以指定,如果没有指定,就是 `oldValue` + didSet { + print(oldValue) + } + } + + // 命名构造函数 (designated inits),它必须初始化所有的成员变量, + // 然后调用父类的命名构造函数继续初始化父类的所有变量。 + init(sideLength: Int) { + self.sideLength = sideLength + // 必须显式地在构造函数最后调用父类的构造函数 super.init + super.init() } - } - init(sideLength: Int) { - super.init() - self.sideLength = sideLength - } + func shrink() { + if sideLength > 0 { + sideLength -= 1 + } + } - func shrink() { - if sideLength > 0 { - --sideLength + // 函数重载使用 override 关键字 + override func getArea() -> Int { + return sideLength * sideLength } - } +} - 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 = new Square(sideLength: 5) + +var mySquare = Square() print(mySquare.getArea()) // 25 mySquare.shrink() print(mySquare.sideLength) // 4 -// 如果你不需要自定义getter 和setter, -// 但仍希望在获取或设置一个属性之前或之后运行 -// 一些代码,你可以使用`willSet` 和 `didSet` +// 类型转换 +let aShape = mySquare as Shape + +// 使用三个等号来比较是不是同一个实例 +if mySquare === aShape { + print("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) +print(myCircle?.getArea()) // Optional(3) +print(myCircle!.getArea()) // 3 +var myEmptyCircle = Circle(radius: -1) +print(myEmptyCircle?.getArea()) // "nil" +if let circle = myEmptyCircle { + // 此语句不会输出,因为 myEmptyCircle 变量值为 nil + print("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 "♧" + 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" +} +print("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) +print(desk.description()) // "Desk with 80 cm" +var chair = Furniture.chair(brand: "Foo", height: 40) +print(chair.description()) // "Chair of Foo with 40 cm" + // -// 其它 +// MARK: 协议 +// 与 Java 的 interface 类似 // -// `协议(protocol)`: 与Java 的接口(Interface) 类似. -// `扩展(extension)`: 为现有类型添加额外特性 -// 泛型: 与Java 相似。使用`where` 关键字指定 -// 泛型的要求. +// 协议可以让遵循同一协议的类型实例拥有相同的属性,方法,类方法,操作符或下标运算符等 +// 下面代码定义一个协议,这个协议包含一个名为 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 reshape() + optional func canReshape() -> Bool +} + +class MyShape: Rect { + var delegate: TransformShape? + + func grow() { + sideLength += 2 + + // 在 optional 属性,方法或下标运算符后面加一个问号,可以优雅地忽略 nil 值,返回 nil。 + // 这样就不会引起运行时错误 (runtime error) + if let reshape = self.delegate?.canReshape?() where reshape { + // 注意语句中的问号 + self.delegate?.reshape?() + } + } +} + + +// +// MARK: 其它 +// + +// 扩展: 给一个已经存在的数据类型添加功能 + +// 给 Square 类添加 `CustomStringConvertible` 协议的实现,现在其支持 `CustomStringConvertible` 协议 +extension Square: CustomStringConvertible { + var description: String { + return "Area: \(self.getArea()) - ID: \(self.identifier)" + } +} + +print("Square: \(mySquare)") // Area: 16 - ID: defaultID + +// 也可以给系统内置类型添加功能支持 +extension Int { + var customProperty: String { + return "This is \(self)" + } + + func multiplyBy(num: Int) -> Int { + return num * self + } +} + +print(7.customProperty) // "This is 7" +print(14.multiplyBy(3)) // 42 + +// 泛型: 和 Java 及 C# 的泛型类似,使用 `where` 关键字来限制类型。 +// 如果只有一个类型限制,可以省略 `where` 关键字 +func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? { + for (index, value) in array.enumerate() { + if value == valueToFind { + return index + } + } + return nil +} +let foundAtIndex = findIndex([1, 2, 3, 4], 3) +print(foundAtIndex == 2) // true + +// 自定义运算符: +// 自定义运算符可以以下面的字符打头: +// / = - + * % < > ! & | ^ . ~ +// 甚至是 Unicode 的数学运算符等 +prefix operator !!! {} + +// 定义一个前缀运算符,使矩形的边长放大三倍 +prefix func !!! (inout shape: Square) -> Square { + shape.sideLength *= 3 + return shape +} + +// 当前值 +print(mySquare.sideLength) // 4 + +// 使用自定义的 !!! 运算符来把矩形边长放大三倍 +!!!mySquare +print(mySquare.sideLength) // 12 + +// 运算符也可以是泛型 +infix operator <-> {} +func <-><T: Equatable> (inout a: T, inout b: T) { + let c = a + a = b + b = c +} + +var foo: Float = 10 +var bar: Float = 20 + +foo <-> bar +print("foo is \(foo), bar is \(bar)") // "foo is 20.0, bar is 10.0" ``` diff --git a/zh-cn/tmux-cn.html.markdown b/zh-cn/tmux-cn.html.markdown new file mode 100644 index 00000000..cf865dce --- /dev/null +++ b/zh-cn/tmux-cn.html.markdown @@ -0,0 +1,253 @@ +--- +category: tool +tool: tmux +filename: LearnTmux-cn.txt +contributors: + - ["mdln", "https://github.com/mdln"] +translators: + - ["Arnie97", "https://github.com/Arnie97"] +lang: zh-cn +--- + + +[tmux](http://tmux.github.io)是一款终端复用工具。 +在它的帮助下,你可以在同一个控制台上建立、访问并控制多个终端。 +你可以断开与一个 tmux 终端的连接,此时程序将在后台运行, +当你需要时,可以随时重新连接到这个终端。 + +``` + + tmux [command] # 运行一条命令 + # 如果单独使用 'tmux' 而不指定某个命令,将会建立一个新的会话 + + new # 创建一个新的会话 + -s "Session" # 创建一个会话,并命名为“Session” + -n "Window" # 创建一个窗口,并命名为“Window” + -c "/dir" # 在指定的工作目录中启动会话 + + attach # 连接到上一次的会话(如果可用) + -t "#" # 连接到指定的会话 + -d # 断开其他客户端的会话 + + ls # 列出打开的会话 + -a # 列出所有打开的会话 + + lsw # 列出窗口 + -a # 列出所有窗口 + -s # 列出会话中的所有窗口 + + lsp # 列出窗格 + -a # 列出所有窗格 + -s # 列出会话中的所有窗格 + -t "#" # 列出指定窗口中的所有窗格 + + kill-window # 关闭当前窗口 + -t "#" # 关闭指定的窗口 + -a # 关闭所有窗口 + -a -t "#" # 关闭除指定窗口以外的所有窗口 + + kill-session # 关闭当前会话 + -t "#" # 关闭指定的会话 + -a # 关闭所有会话 + -a -t "#" # 关闭除指定会话以外的所有会话 + +``` + + +### 快捷键 + +通过“前缀”快捷键,可以控制一个已经连入的 tmux 会话。 + +``` +---------------------------------------------------------------------- + (C-b) = Ctrl + b # 在使用下列快捷键之前,需要按这个“前缀”快捷键 + + (M-1) = Meta + 1 或 Alt + 1 +---------------------------------------------------------------------- + + ? # 列出所有快捷键 + : # 进入 tmux 的命令提示符 + r # 强制重绘当前客户端 + c # 创建一个新窗口 + + ! # 将当前窗格从窗口中移出,成为为一个新的窗口 + % # 将当前窗格分为左右两半 + " # 将当前窗格分为上下两半 + + n # 切换到下一个窗口 + p # 切换到上一个窗口 + { # 将当前窗格与上一个窗格交换 + } # 将当前窗格与下一个窗格交换 + + s # 在交互式界面中,选择并连接至另一个会话 + w # 在交互式界面中,选择并激活一个窗口 + 0 至 9 # 选择 0 到 9 号窗口 + + d # 断开当前客户端 + D # 选择并断开一个客户端 + + & # 关闭当前窗口 + x # 关闭当前窗格 + + Up, Down # 将焦点移动至相邻的窗格 + Left, Right + + M-1 到 M-5 # 排列窗格: + # 1) 水平等分 + # 2) 垂直等分 + # 3) 将一个窗格作为主要窗格,其他窗格水平等分 + # 4) 将一个窗格作为主要窗格,其他窗格垂直等分 + # 5) 平铺 + + C-Up, C-Down # 改变当前窗格的大小,每按一次增减一个单位 + C-Left, C-Right + + M-Up, M-Down # 改变当前窗格的大小,每按一次增减五个单位 + M-Left, M-Right + +``` + + +### 配置 ~/.tmux.conf + +tmux.conf 可以在 tmux 启动时自动设置选项,类似于 .vimrc 或 init.el 的用法。 + +``` +# tmux.conf 示例 +# 2014.10 + + +### 通用设置 +########################################################################### + +# 启用 UTF-8 编码 +setw -g utf8 on +set-option -g status-utf8 on + +# 命令回滚/历史数量限制 +set -g history-limit 2048 + +# 从 1 开始编号,而不是从 0 开始 +set -g base-index 1 + +# 启用鼠标 +set-option -g mouse-select-pane on + +# 重新加载配置文件 +unbind r +bind r source-file ~/.tmux.conf + + +### 快捷键设置 +########################################################################### + +# 取消默认的前缀键 C-b +unbind C-b + +# 设置新的前缀键 ` +set-option -g prefix ` + +# 多次按下前缀键时,切换到上一个窗口 +bind C-a last-window +bind ` last-window + +# 按下F11/F12,可以选择不同的前缀键 +bind F11 set-option -g prefix C-a +bind F12 set-option -g prefix ` + +# Vim 风格的快捷键绑定 +setw -g mode-keys vi +set-option -g status-keys vi + +# 使用 Vim 风格的按键在窗格间移动 +bind h select-pane -L +bind j select-pane -D +bind k select-pane -U +bind l select-pane -R + +# 循环切换不同的窗口 +bind e previous-window +bind f next-window +bind E swap-window -t -1 +bind F swap-window -t +1 + +# 较易于使用的窗格分割快捷键 +bind = split-window -h +bind - split-window -v +unbind '"' +unbind % + +# 在嵌套使用 tmux 的情况下,激活最内层的会话,以便向其发送命令 +bind a send-prefix + + +### 外观主题 +########################################################################### + +# 状态栏颜色 +set-option -g status-justify left +set-option -g status-bg black +set-option -g status-fg white +set-option -g status-left-length 40 +set-option -g status-right-length 80 + +# 窗格边框颜色 +set-option -g pane-active-border-fg green +set-option -g pane-active-border-bg black +set-option -g pane-border-fg white +set-option -g pane-border-bg black + +# 消息框颜色 +set-option -g message-fg black +set-option -g message-bg green + +# 窗口状态栏颜色 +setw -g window-status-bg black +setw -g window-status-current-fg green +setw -g window-status-bell-attr default +setw -g window-status-bell-fg red +setw -g window-status-content-attr default +setw -g window-status-content-fg yellow +setw -g window-status-activity-attr default +setw -g window-status-activity-fg yellow + + +### 用户界面 +########################################################################### + +# 通知方式 +setw -g monitor-activity on +set -g visual-activity on +set-option -g bell-action any +set-option -g visual-bell off + +# 自动设置窗口标题 +set-option -g set-titles on +set-option -g set-titles-string '#H:#S.#I.#P #W #T' # 窗口编号,程序名称,是否活动 + +# 调整状态栏 +set -g status-left "#[fg=red] #H#[fg=green]:#[fg=white]#S#[fg=green] |#[default]" + +# 在状态栏中显示性能计数器 +# 需要用到 https://github.com/thewtex/tmux-mem-cpu-load +set -g status-interval 4 +set -g status-right "#[fg=green] | #[fg=white]#(tmux-mem-cpu-load)#[fg=green] | #[fg=cyan]%H:%M #[default]" + +``` + + +### 参考资料 + +[Tmux 主页](http://tmux.github.io) + +[Tmux 手册](http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/tmux.1?query=tmux) + +[FreeBSDChina Wiki](https://wiki.freebsdchina.org/software/t/tmux) + +[Archlinux Wiki](https://wiki.archlinux.org/index.php/Tmux_(简体中文)) + +[Tmux 快速教程](http://blog.jeswang.org/blog/2013/06/24/tmux-kuai-su-jiao-cheng) + +[如何在 tmux 状态栏中显示 CPU / 内存占用的百分比](https://stackoverflow.com/questions/11558907/is-there-a-better-way-to-display-cpu-usage-in-tmux) + +[管理复杂 tmux 会话的工具 - tmuxinator](https://github.com/tmuxinator/tmuxinator) diff --git a/zh-cn/typescript-cn.html.markdown b/zh-cn/typescript-cn.html.markdown new file mode 100644 index 00000000..2651b1cb --- /dev/null +++ b/zh-cn/typescript-cn.html.markdown @@ -0,0 +1,173 @@ +--- +language: TypeScript +category: language +contributors: + - ["Philippe Vlérick", "https://github.com/pvlerick"] +translators: + - ["Shawn Zhang", "https://github.com/shawnzhang009"] +filename: learntypescript-cn.ts +lang: zh-cn +--- + +TypeScript是一门为开发大型JavaScript应用而设计的语言。TypeScript在JavaScript的基础上增加了类、模块、接口、泛型和静态类型(可选)等常见的概念。它是JavaScript的一个超集:所有JavaScript代码都是有效的TypeScript代码,所以任何JavaScript项目都可以无缝引入TypeScript. TypeScript编译器会把TypeScript代码编译成JavaScript代码。 + +本文只关注TypeScript额外增加的区别于[JavaScript](../javascript-cn/)的语法,. + +如需测试TypeScript编译器,你可以在[Playground](http://www.typescriptlang.org/Playground)码代码,它会自动编译成JavaScript代码然后直接显示出来。 + +```js +// TypeScript有三种基本类型 +var isDone: boolean = false; +var lines: number = 42; +var name: string = "Anders"; + +// 如果不知道是什么类型,可以使用"any"(任意)类型 +var notSure: any = 4; +notSure = "maybe a string instead"; +notSure = false; // 亦可,定义为布尔型 + +// 对于集合的声明, 有类型化数组和泛型数组 +var list: number[] = [1, 2, 3]; +// 另外一种,使用泛型数组 +var list: Array<number> = [1, 2, 3]; + +// 枚举: +enum Color {Red, Green, Blue}; +var c: Color = Color.Green; + +// 最后,"void"用于函数没有任何返回的特殊情况下 +function bigHorribleAlert(): void { + alert("I'm a little annoying box!"); +} + +// 函数是"第一等公民"(first class citizens), 支持使用箭头表达式和类型推断 + +// 以下是相等的,TypeScript编译器会把它们编译成相同的JavaScript代码 +var f1 = function(i: number): number { return i * i; } +// 返回推断类型的值 +var f2 = function(i: number) { return i * i; } +var f3 = (i: number): number => { return i * i; } +// 返回推断类型的值 +var f4 = (i: number) => { return i * i; } +// 返回推断类型的值, 单行程式可以不需要return关键字和大括号 +var f5 = (i: number) => i * i; + +// 接口是结构化的,任何具有这些属性的对象都与该接口兼容 +interface Person { + name: string; + // 可选属性,使用"?"标识 + age?: number; + // 函数 + move(): void; +} + +// 实现"Person"接口的对象,当它有了"name"和"move"方法之后可被视为一个"Person" +var p: Person = { name: "Bobby", move: () => {} }; +// 带了可选参数的对象 +var validPerson: Person = { name: "Bobby", age: 42, move: () => {} }; +// 因为"age"不是"number"类型所以这不是一个"Person" +var invalidPerson: Person = { name: "Bobby", age: true }; + +// 接口同样可以描述一个函数的类型 +interface SearchFunc { + (source: string, subString: string): boolean; +} +// 参数名并不重要,参数类型才是重要的 +var mySearch: SearchFunc; +mySearch = function(src: string, sub: string) { + return src.search(sub) != -1; +} + +// 类 - 成员默认为公共的(public) +class Point { + // 属性 + x: number; + + // 构造器 - 这里面的public/private关键字会为属性生成样板代码和初始化值 + // 这个例子中,y会被同x一样定义,不需要额外代码 + // 同样支持默认值 + + constructor(x: number, public y: number = 0) { + this.x = x; + } + + // 函数 + dist() { return Math.sqrt(this.x * this.x + this.y * this.y); } + + // 静态成员 + static origin = new Point(0, 0); +} + +var p1 = new Point(10 ,20); +var p2 = new Point(25); //y为0 + +// 继承 +class Point3D extends Point { + constructor(x: number, y: number, public z: number = 0) { + super(x, y); // 必须显式调用父类的构造器 + } + + // 重写 + dist() { + var d = super.dist(); + return Math.sqrt(d * d + this.z * this.z); + } +} + +// 模块, "."可以作为子模块的分隔符 +module Geometry { + export class Square { + constructor(public sideLength: number = 0) { + } + area() { + return Math.pow(this.sideLength, 2); + } + } +} + +var s1 = new Geometry.Square(5); + +// 引入模块并定义本地别名 +import G = Geometry; + +var s2 = new G.Square(10); + +// 泛型 +// 类 +class Tuple<T1, T2> { + constructor(public item1: T1, public item2: T2) { + } +} + +// 接口 +interface Pair<T> { + item1: T; + item2: T; +} + +// 以及函数 +var pairToTuple = function<T>(p: Pair<T>) { + return new Tuple(p.item1, p.item2); +}; + +var tuple = pairToTuple({ item1:"hello", item2:"world"}); + +// 引用定义文件 +// <reference path="jquery.d.ts" /> + +// 模板字符串(使用反引号的字符串) +// 嵌入变量的模板字符串 +var name = 'Tyrone'; +var greeting = `Hi ${name}, how are you?` +// 有多行内容的模板字符串 +var multiline = `This is an example +of a multiline string`; + +``` + +## 参考资料 + * [TypeScript官网](http://www.typescriptlang.org/) + * [TypeScript语言规范说明书(pdf)](http://go.microsoft.com/fwlink/?LinkId=267238) + * [Anders Hejlsberg - TypeScript介绍](http://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript) + * [GitHub源码](https://github.com/Microsoft/TypeScript) + * [Definitely Typed - 类型定义仓库](http://definitelytyped.org/) diff --git a/zh-cn/vim-cn.html.markdown b/zh-cn/vim-cn.html.markdown new file mode 100644 index 00000000..22dbace6 --- /dev/null +++ b/zh-cn/vim-cn.html.markdown @@ -0,0 +1,238 @@ +--- +category: tool +tool: vim +filename: LearnVim-cn.txt +contributors: + - ["RadhikaG", "https://github.com/RadhikaG"] +translators: + - ["Jiang Haiyun", "https://github.com/haiiiiiyun"] +lang: zh-cn +--- + + +[Vim](http://www.vim.org) +(Vi IMproved) 是 Unix 上的流行编辑器 vi 的克隆版本。这个文本编辑器 +是为性能和提升效率而设计的,并且在大多数基于 unix 的系统上普遍存在。 +它有大量的快捷键可用来快速导航到文件的特定位置,以便进行快速编辑。 + +## Vim 导航基础 + +``` + vim <filename> # 在 Vim 中打开 <filename> + :q # 退出 Vim + :w # 保存当前文件 + :wq # 保存文件并退出 Vim + :q! # 退出 Vim 并且不保存文件 + # ! *强制* 执行 :q, 因此没有保存就退出 Vim + :x # 保存文件并且退出 Vim, 是 :wq 的简写版本 + + u # 撤销 + CTRL+R # 重做 + + h # 左移一个字符 + j # 下移一行 + k # 上移一行 + l # 右移一个字符 + + # 在行内移动 + + 0 # 移到行首 + $ # 移到行尾 + ^ # 移到行内的第一个非空白字符处 + + # 在文本中查找 + + /word # 光标之后的所有该词都高亮显示 + ?word # 光标之前的所有该词都高亮显示 + n # 查找后将光标移到该词的下一个出现位置 + N # 光标移到该词的上一个出现位置 + + :%s/foo/bar/g # 将文件每一行上的所有 'foo' 都改成 'bar' + :s/foo/bar/g # 将当前行上的所有 'foo' 都改成 'bar' + + # 跳到字符处 + + f<字符> # 向前跳移到 <字符> 上 + t<字符> # 向前跳移到 <字符> 的左侧 + + # 例如, + f< # 向前跳移到 < 上 + t< # 向前跳移到 < 的左侧 + + # 按词移动 + # 默认一个单词由字母,数字和下划线组成 + + w # 移动到下一个词首 + b # 移动到前一个词首 + e # 移动到下一个词尾 + + + # 移动的其它命令 + + gg # 移到文件顶部 + G # 移到文件末尾 + :NUM # 移到第 NUM 行 (NUM 是任意数字) + H # 移到屏幕顶部 + M # 移到屏幕中间位置 + L # 移到屏幕末尾 +``` + +## 模式: + +Vim 基于 **模式** 这个概念。 + +命令模式 - Vim 启动后就处于这个模式,用于导航和操作命令 +插入模式 - 用于在你的文件中进行修改 +可视模式 - 用于高亮文本并对它们进行操作 +Ex 模式 - 用于跳到底部的 ':' 提示行上输入命令 + +``` + i # 在光标位置前,将 Vim 切换到插入模式 + a # 在光标位置后,将 Vim 切换到插入模式 + v # 将 Vim 切换到可视模式 + : # 将 Vim 切换到 ex 模式 + <esc> # 无论你当前处于什么模式,都返回到命令模式 + + # 复制和粘贴文本 + + y # 复制所选的内容 + yy # 复制当前行 + d # 删除所选的内容 + dd # 删除当前行 + p # 在当前光标位置后粘贴复制的文本 + P # 在当前光标位置前粘贴复制的文本 + x # 删除当前光标位置处的字符 +``` + +## Vim 的 '语法' + +Vim 可以被认为是按 '动词-修饰词-名词' 格式编排的一组命令: + +动词 - 你的动作 +修饰词 - 你如何执行你的动作 +名词 - 你的动作所作用于的对象 + +关于 '动词','修饰词',和 '名词' 的几个重要例子: + +``` + # '动词' + + d # 删除 + c # 修改 + y # 复制 + v # 可视化选择 + + # '修饰词' + + i # 内部的 + a # 周围的 + NUM # 数字 (NUM 是任意数字) + f # 查找文本并位于其上 + t # 查找文本并停于其前面 + / # 从光标处开始查找字符串 + ? # 在光标前查找字符串 + + # '名词' + + w # 词 + s # 句子 + p # 段落 + b # 块 + + # 示例 '语句' 或命令 + + d2w # 删除 2 个词 + cis # 修改段落内的内容 + yip # 复制段落内的内容 (复制你所在的段落) + ct< # 修改直到括号开启处 + # 对你的当前位置直到下个括号开启处的内容进行修改 + d$ # 删除直到行尾 +``` + +## 一些快捷键和技巧 + + <!--TODO: Add more!--> +``` + > # 将所选内容缩进一级 + < # 将所选内容取消缩进一级 + :earlier 15m # 将文档还原到 15 分钟前的状态 + :later 15m # 逆转上述命令 + ddp # 相邻行交换位置,先 dd 再 p + . # 重复之前动作 +``` + +## 宏 + +宏基本上来说就是可录制的动作。 +当你开始录制宏时,它会记录你使用的 **每个** 动作和命令, +直到你停止录制。当调用宏时,它会将这个完全相同的动作和命令序列 +再次应用于所选文本之上。 + +``` + qa # 开始录制一个叫 'a' 的宏 + q # 停止录制 + @a # 重播宏 +``` + +### 配置 ~/.vimrc + +.vimrc 可用于在启动时对 Vim 进行配置。 + +这里是一个示例 ~/.vimrc 文件: + +``` +" 示例 ~/.vimrc +" 2015.10 + +" 需要 Vim iMproved 版本 +set nocompatible + +" 根据文件名检测文件类型,以便能进行智能自动缩进等操作。 +filetype indent plugin on + +" 开启语法高亮 +syntax on + +" 更好的命令行补全 +set wildmenu + +" 除了当使用大写字母时使用大小写无关查找 +set ignorecase +set smartcase + +" 当新开一行时,如果没有开启文件特定的缩进规则, +" 则缩进保持与你当前行一致 +set autoindent + +" 在左侧显示行号 +set number + +" 缩进选项,根据个人偏好进行修改 + +" 每个 TAB 的可视空格数 +set tabstop=4 + +" 编辑时 TAB 对应的空格数 +set softtabstop=4 + +" 当使用缩进操作 (>> 和 <<) 时缩进的空格数 +set shiftwidth=4 + +" 将 TAB 转换成空格 +set expandtab + +" 为缩进和对齐开启智能化的 TAB 和空格切换功能 +set smarttab +``` + +### 参考 + +[Vim | Home](http://www.vim.org/index.php) + +`$ vimtutor` + +[A vim Tutorial and Primer](https://danielmiessler.com/study/vim/) + +[What are the dark corners of Vim your mom never told you about? (Stack Overflow thread)](http://stackoverflow.com/questions/726894/what-are-the-dark-corners-of-vim-your-mom-never-told-you-about) + +[Arch Linux Wiki](https://wiki.archlinux.org/index.php/Vim) diff --git a/zh-cn/visualbasic-cn.html.markdown b/zh-cn/visualbasic-cn.html.markdown index 95f01ed6..8bdfabc6 100644 --- a/zh-cn/visualbasic-cn.html.markdown +++ b/zh-cn/visualbasic-cn.html.markdown @@ -3,7 +3,7 @@ language: Visual Basic contributors: - ["Brian Martin", "http://brianmartin.biz"] translators: - - ["Abner Chou", "http://github.com/NoahDragon"] + - ["Abner Chou", "http://cn.abnerchou.me"] lang: zh-cn filename: learnvisualbasic.vb-cn --- @@ -77,7 +77,7 @@ Module Module1 ' 使用 private subs 声明函数。 Private Sub HelloWorldOutput() ' 程序名 - Console.Title = "Hello World Ouput | Learn X in Y Minutes" + Console.Title = "Hello World Output | Learn X in Y Minutes" ' 使用 Console.Write("") 或者 Console.WriteLine("") 来输出文本到屏幕上 ' 对应的 Console.Read() 或 Console.Readline() 用来读取键盘输入 Console.WriteLine("Hello World") diff --git a/zh-cn/yaml-cn.html.markdown b/zh-cn/yaml-cn.html.markdown index fc510eb5..bbda20e9 100644 --- a/zh-cn/yaml-cn.html.markdown +++ b/zh-cn/yaml-cn.html.markdown @@ -4,33 +4,36 @@ contributors: - ["Adam Brenecki", "https://github.com/adambrenecki"] translators: - ["Zach Zhang", "https://github.com/checkcheckzz"] + - ["Jiang Haiyun", "https://github.com/haiiiiiyun"] filename: learnyaml-cn.yaml lang: zh-cn --- -YAML是一个数据序列化语言,被设计成人类直接可写可读的。 +YAML 是一个数据序列化语言,被设计成人类直接可写可读的。 -它是JSON的严格超集,增加了语法显著换行符和缩进,就像Python。但和Python不一样, -YAML根本不容许文字制表符。 +它是 JSON 的严格超集,增加了语法显著换行符和缩进,就像 Python。但和 Python 不一样, +YAML 根本不容许文字制表符。 ```yaml -# YAML中的注解看起来像这样。 +# YAML 中的注解看起来像这样。 ################ -# 标量类型 # +# 标量类型 # ################ -# 我们的根对象 (它们在整个文件里延续) 将会是一个地图, +# 我们的根对象 (它们在整个文件里延续) 将会是一个映射, # 它等价于在别的语言里的一个字典,哈西表或对象。 key: value another_key: Another value goes here. a_number_value: 100 +# 如果你想将数字 1 作为值,你必须要将它括在引号中。 +# 不然 YAML 解析器会假定它是一个布尔值 true。 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." # 多行字符串既可以写成像一个'文字块'(使用 |), @@ -54,7 +57,7 @@ folded_style: > this text will appear over two lines. #################### -# 集合类型 # +# 集合类型 # #################### # 嵌套是通过缩进完成的。 @@ -64,18 +67,24 @@ a_nested_map: another_nested_map: hello: hello -# 地图不用有字符串键值。 +# 映射的键值不必是字符串。 0.25: a float key -# 键值也可以是多行对象,用?表明键值的开始。 +# 键值也可以是复合型的,比如多行对象 +# 我们用 ? 后跟一个空格来表示一个复合键的开始。 ? | This is a key that has multiple lines : and this is its value -# YAML也容许键值是集合类型,但是很多语言将会抱怨。 +# YAML 也允许使用复杂键语法表示序列间的映射关系。 +# 但有些语言的解析器可能会不支持。 +# 一个例子: +? - Manchester United + - Real Madrid +: [ 2001-01-01, 2002-02-02 ] -# 序列 (等价于表或数组) 看起来像这样: +# 序列 (等价于列表或数组) 看起来像这样: a_sequence: - Item 1 - Item 2 @@ -87,50 +96,75 @@ a_sequence: - This is a sequence - inside another sequence -# 因为YAML是JSON的超集,你也可以写JSON风格的地图和序列: +# 因为 YAML 是 JSON 的超集,你也可以写 JSON 风格的映射和序列: json_map: {"key": "value"} json_seq: [3, 2, 1, "takeoff"] ####################### -# 其余的YAML特点 # +# 其余的 YAML 特性 # ####################### -# YAML还有一个方便的特点叫'锚',它让你简单地在整个文件里重复内容。 -# 两个键值将会有相同的值: +# YAML 还有一个方便的特性叫 '锚',它能让你很容易在文档中进行文本复用。 +# 如下两个键会有相同的值: anchored_content: &anchor_name This string will appear as the value of two keys. other_anchor: *anchor_name -# YAML还有标签,你可以用它显示地声明类型。 +# 锚也可被用来复制/继承属性 +base: &base + name: Everyone has same name + +foo: &foo + <<: *base + age: 10 + +bar: &bar + <<: *base + age: 20 + +# foo 和 bar 将都含有 name: Everyone has same name + +# YAML 还有标签,你可以用它显示地声明类型。 explicit_string: !!str 0.5 -# 一些解析器实现特定语言的标签,就像这个为了Python的复数类型。 +# 一些解析器实现特定语言的标签,就像这个针对 Python 的复数类型。 python_complex_number: !!python/complex 1+2j +# 我们也可以在 YAML 的复合键中使用特定语言的标签 +? !!python/tuple [5, 7] +: Fifty Seven +# 将会是 Python 中的 {(5, 7): 'Fifty Seven'} + #################### -# 其余的YAML类型 # +# 其余的 YAML 类型 # #################### -# 字符串和数字不是仅有的YAML可以理解的标量。 -# ISO 格式的日期和日期时间文字也是可以被解析的。 +# 除了字符串和数字,YAML 还能理解其它标量。 +# ISO 格式的日期和日期时间文本也可以被解析。 datetime: 2001-12-15T02:59:43.1Z datetime_with_spaces: 2001-12-14 21:59:43.10 -5 date: 2002-12-14 -# 这个!!binary标签表明一个字符串实际上是一个二进制blob的base64编码表示。 +# 这个 !!binary 标签表明这个字符串实际上 +# 是一个用 base64 编码表示的二进制 blob。 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还有一个集合类型,它看起来像这样: +# YAML 还有一个集合类型,它看起来像这样: set: ? item1 ? item2 ? item3 -# 像Python一样,集合仅是有null数值的地图;上面的集合等价于: +# 像 Python 一样,集合仅是值为 null 的映射;上面的集合等价于: set2: item1: null item2: null item3: null ``` + +### 更多资源 + ++ [YAML official website](http://yaml.org/) ++ [Online YAML Validator](http://codebeautify.org/yaml-validator) diff --git a/zh-cn/zfs-cn.html.markdown b/zh-cn/zfs-cn.html.markdown new file mode 100644 index 00000000..fdf5277e --- /dev/null +++ b/zh-cn/zfs-cn.html.markdown @@ -0,0 +1,397 @@ +--- +category: tool +tool: zfs +contributors: + - ["sarlalian", "http://github.com/sarlalian"] +translators: + - ["Alan Cheng", "https://github.com/kedaio"] +filename: LearnZfs-cn.txt +lang: zh-cn +--- + +[ZFS](http://open-zfs.org/wiki/Main_Page) +是重新思考与储存相关技术的结果,它把传统的文件系统和卷管理器集成到一个工具当中. +ZFS不但有把它和传统存储系统分开来的特有术语,也有很多聚焦于可用性的功能。 + + +## ZFS概念 + +### 虚拟设备(Virtual Devices,VDEV) + +对于操作系统来说,VDEV和传统的RAID阵列卡所呈现的raid设备类似。VDEV有几种不同的类型,每种类型 +都有自己的优势,包括冗余和速度。一般来说,VDEV的可靠性和安全性比阵列卡要好。因此使用ZFS时不 +建议使用阵列卡。让ZFS直接管理磁盘。 + +VDEV的类型 +* stripe (条带。单个磁盘,没有冗余) +* mirror (镜像。支持n-way镜像) +* raidz + * raidz1 (一个奇偶校验磁盘, 类似于RAID 5) + * raidz2 (两个奇偶校验磁盘, 类似于RAID 6) + * raidz3 (三个奇偶校验磁盘, 没有类似RAID等级) +* disk (磁盘) +* file (文件。不推荐在生产环境中使用,因为中间又多了一层不必要的文件系统) + +数据会以条带方式存储于存储池中的所有VDEV上。因此一个存储池中的VDEV越多,IOPS就越高。 + +### storage pool (存储池) + +ZFS 使用存储池来作为底层存储提供者(VDEV)的抽象。这样可以把用户可见的文件系统和底层的物理磁盘 +布局分离开来。 + +### ZFS 数据集(Dataset) + +ZFS 数据集类似于传统的文件系统(译者注:或者说是目录),但是提供了更多的功能。ZFS的很多优势也是 +在这一层体现出来的。数据集支持 [Copy on Write](https://en.wikipedia.org/wiki/Copy-on-write) +快照, 配额, 压缩和重复消除(de-duplication). + + +### 限制 + +一个目录最多可包含 2^48个文件, 每个文件最大可以是16 exabytes. 一个存储池最大可包含256 zettabytes 、 +(2^78) 的空间, 可以条带化地分布于2^64 设备上. 单一主机最多可以创建2^64个存储池。这些限制可以说是相 +当大。 + + +## 命令 + +### 存储池 + +Actions: (存储池操作) +* List (列举) +* Status (查看状态) +* Destroy (删除) +* Get/Set properties (获取/设置属性) + +List zpools (列举存储池(也叫zpool)) + +```bash +# 创建一个raidz类型的存储池(名称为bucket) +$ zpool create bucket raidz1 gpt/zfs0 gpt/zfs1 gpt/zfs2 + +# 列出所有存储池 +$ zpool list +NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zroot 141G 106G 35.2G - 43% 75% 1.00x ONLINE - + +# 列出某一存储池的详细信息 +$ zpool list -v zroot +NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zroot 141G 106G 35.2G - 43% 75% 1.00x ONLINE - + gptid/c92a5ccf-a5bb-11e4-a77d-001b2172c655 141G 106G 35.2G - 43% 75% +``` + +Status of zpools (存储池状态) + +```bash +# 获取全部zpool状态信息 +$ zpool status + pool: zroot + state: ONLINE + scan: scrub repaired 0 in 2h51m with 0 errors on Thu Oct 1 07:08:31 2015 +config: + + NAME STATE READ WRITE CKSUM + zroot ONLINE 0 0 0 + gptid/c92a5ccf-a5bb-11e4-a77d-001b2172c655 ONLINE 0 0 0 + +errors: No known data errors + +# 用scrub来更正存储池错误信息 +$ zpool scrub zroot +$ zpool status -v zroot + pool: zroot + state: ONLINE + scan: scrub in progress since Thu Oct 15 16:59:14 2015 + 39.1M scanned out of 106G at 1.45M/s, 20h47m to go + 0 repaired, 0.04% done +config: + + NAME STATE READ WRITE CKSUM + zroot ONLINE 0 0 0 + gptid/c92a5ccf-a5bb-11e4-a77d-001b2172c655 ONLINE 0 0 0 + +errors: No known data errors +``` + +Properties of zpools (存储池属性) + +```bash + +# 获取某一存储池的全部属性。属性可能是系统提供,也可能是用户设置 +$ zpool get all zroot +NAME PROPERTY VALUE SOURCE +zroot size 141G - +zroot capacity 75% - +zroot altroot - default +zroot health ONLINE - +... + +# 设置存储池属性,下例这是设置comment(备注)属性 +$ zpool set comment="Storage of mah stuff" zroot +$ zpool get comment +NAME PROPERTY VALUE SOURCE +tank comment - default +zroot comment Storage of mah stuff local +``` + +Remove zpool (删除存储池) + +```bash +$ zpool destroy test +``` + + +### Datasets (数据集) + +Actions: (数据集相关操作) +* Create (创建) +* List (列举) +* Rename (重命名) +* Delete (删除) +* Get/Set properties (获取/设置属性) + +Create datasets + +```bash +# 创建数据集 +$ zfs create tank/root/data +$ mount | grep data +tank/root/data on /data (zfs, local, nfsv4acls) + +# 创建子数据集 +$ zfs create tank/root/data/stuff +$ mount | grep data +tank/root/data on /data (zfs, local, nfsv4acls) +tank/root/data/stuff on /data/stuff (zfs, local, nfsv4acls) + + +# 创建卷 +$ zfs create -V zroot/win_vm +$ zfs list zroot/win_vm +NAME USED AVAIL REFER MOUNTPOINT +tank/win_vm 4.13G 17.9G 64K - +``` + +List datasets (列举数据集) + +```bash +# 列出所有数据集 +$ zfs list +NAME USED AVAIL REFER MOUNTPOINT +zroot 106G 30.8G 144K none +zroot/ROOT 18.5G 30.8G 144K none +zroot/ROOT/10.1 8K 30.8G 9.63G / +zroot/ROOT/default 18.5G 30.8G 11.2G / +zroot/backup 5.23G 30.8G 144K none +zroot/home 288K 30.8G 144K none +... + +# 列举某一数据集的信息 +$ zfs list zroot/home +NAME USED AVAIL REFER MOUNTPOINT +zroot/home 288K 30.8G 144K none + +# 列出快照 +$ zfs list -t snapshot +zroot@daily-2015-10-15 0 - 144K - +zroot/ROOT@daily-2015-10-15 0 - 144K - +zroot/ROOT/default@daily-2015-10-15 0 - 24.2G - +zroot/tmp@daily-2015-10-15 124K - 708M - +zroot/usr@daily-2015-10-15 0 - 144K - +zroot/home@daily-2015-10-15 0 - 11.9G - +zroot/var@daily-2015-10-15 704K - 1.42G - +zroot/var/log@daily-2015-10-15 192K - 828K - +zroot/var/tmp@daily-2015-10-15 0 - 152K - +``` + +Rename datasets (重命名数据集) + +```bash +$ zfs rename tank/root/home tank/root/old_home +$ zfs rename tank/root/new_home tank/root/home +``` + +Delete dataset (删除数据集) + +```bash +# 数据集如果有快照则无法删除 +zfs destroy tank/root/home +``` + +Get / set properties of a dataset (获取/设置数据集属性) + +```bash +# 获取数据集全部属性 +$ zfs get all zroot/usr/home │157 # Create Volume +NAME PROPERTY VALUE SOURCE │158 $ zfs create -V zroot/win_vm +zroot/home type filesystem - │159 $ zfs list zroot/win_vm +zroot/home creation Mon Oct 20 14:44 2014 - │160 NAME USED AVAIL REFER MOUNTPOINT +zroot/home used 11.9G - │161 tank/win_vm 4.13G 17.9G 64K - +zroot/home available 94.1G - │162 ``` +zroot/home referenced 11.9G - │163 +zroot/home mounted yes - +... + +# 获取数据集属性 +$ zfs get compression zroot/usr/home +NAME PROPERTY VALUE SOURCE +zroot/home compression off default + +# 设置数据集属性(下例为设置压缩属性compression) +$ zfs set compression=gzip-9 mypool/lamb + +# 列举所有数据集的名称、配额和预留属性 +$ zfs list -o name,quota,reservation +NAME QUOTA RESERV +zroot none none +zroot/ROOT none none +zroot/ROOT/default none none +zroot/tmp none none +zroot/usr none none +zroot/home none none +zroot/var none none +... +``` + + +### Snapshots (快照) + +快照是ZFS 的一个非常重要的功能 + +* 快照占用的空间等于它和原始数据的差异量 +* 创建时间以秒计 +* 恢复时间和写入速度相同 +* 易于自动化 + +Actions: (快照相关操作) +* Create (创建) +* Delete (删除) +* Rename (重命名) +* Access snapshots (访问) +* Send / Receive (发送/接收) +* Clone (克隆。译者注:关于clone和快照的区别可参看[这里](http://docs.oracle.com/cd/E19253-01/819-5461/gbcxz/index.html)) + + +Create snapshots (创建快照) + +```bash +# 为单一数据集创建快照 +zfs snapshot tank/home/sarlalian@now + +# 为数据集及其子集创建快照 +$ zfs snapshot -r tank/home@now +$ zfs list -t snapshot +NAME USED AVAIL REFER MOUNTPOINT +tank/home@now 0 - 26K - +tank/home/sarlalian@now 0 - 259M - +tank/home/alice@now 0 - 156M - +tank/home/bob@now 0 - 156M - +... + +Destroy snapshots (删除快照) + +```bash +# 如何删除快照 +$ zfs destroy tank/home/sarlalian@now + +# 删除某一数据集及其子集的快照 +$ zfs destroy -r tank/home/sarlalian@now + +``` + +Renaming Snapshots (重命名) + +```bash +# 重命名快照 +$ zfs rename tank/home/sarlalian@now tank/home/sarlalian@today +$ zfs rename tank/home/sarlalian@now today + +# zfs rename -r tank/home@now @yesterday +``` + +Accessing snapshots (访问快照) + +```bash +# cd进入一个快照目录 +$ cd /home/.zfs/snapshot/ +``` + +Sending and Receiving + +```bash +# 备份快照到一个文件 +$ zfs send tank/home/sarlalian@now | gzip > backup_file.gz + +# 发送快照到另一个数据集 +$ zfs send tank/home/sarlalian@now | zfs recv backups/home/sarlalian + +# 发送快照到一个远程主机 +$ zfs send tank/home/sarlalian@now | ssh root@backup_server 'zfs recv tank/home/sarlalian' + +# 发送完整数据集及其快照到一个新主机 +$ zfs send -v -R tank/home@now | ssh root@backup_server 'zfs recv tank/home' +``` + +Cloneing Snapshots (克隆快照) + +```bash +# 克隆一个快照 +$ zfs clone tank/home/sarlalian@now tank/home/sarlalian_new + +# 提升克隆,让它不再依赖原始快照 +$ zfs promote tank/home/sarlalian_new +``` + +### 汇总 + +下面这个脚本使用了FreeBSD, jails和ZFS,来自动在一个mysql群集的热备主机上为一个mysq staging数据库 +创建一份纯净的拷贝。 + +```bash +#!/bin/sh + +echo "==== Stopping the staging database server ====" +jail -r staging + +echo "==== Cleaning up existing staging server and snapshot ====" +zfs destroy -r zroot/jails/staging +zfs destroy zroot/jails/slave@staging + +echo "==== Quiescing the slave database ====" +echo "FLUSH TABLES WITH READ LOCK;" | /usr/local/bin/mysql -u root -pmyrootpassword -h slave + +echo "==== Snapshotting the slave db filesystem as zroot/jails/slave@staging ====" +zfs snapshot zroot/jails/slave@staging + +echo "==== Starting the slave database server ====" +jail -c slave + +echo "==== Cloning the slave snapshot to the staging server ====" +zfs clone zroot/jails/slave@staging zroot/jails/staging + +echo "==== Installing the staging mysql config ====" +mv /jails/staging/usr/local/etc/my.cnf /jails/staging/usr/local/etc/my.cnf.slave +cp /jails/staging/usr/local/etc/my.cnf.staging /jails/staging/usr/local/etc/my.cnf + +echo "==== Setting up the staging rc.conf file ====" +mv /jails/staging/etc/rc.conf.local /jails/staging/etc/rc.conf.slave +mv /jails/staging/etc/rc.conf.staging /jails/staging/etc/rc.conf.local + +echo "==== Starting the staging db server ====" +jail -c staging + +echo "==== Makes the staging database not pull from the master ====" +echo "STOP SLAVE;" | /usr/local/bin/mysql -u root -pmyrootpassword -h staging +echo "RESET SLAVE;" | /usr/local/bin/mysql -u root -pmyrootpassword -h staging +``` + + +### 延伸阅读 + +* [BSDNow's Crash Course on ZFS](http://www.bsdnow.tv/tutorials/zfs) +* [FreeBSD Handbook on ZFS](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/zfs.html) +* [BSDNow's Crash Course on ZFS](http://www.bsdnow.tv/tutorials/zfs) +* [Oracle's Tuning Guide](http://www.oracle.com/technetwork/articles/servers-storage-admin/sto-recommended-zfs-settings-1951715.html) +* [OpenZFS Tuning Guide](http://open-zfs.org/wiki/Performance_tuning) +* [FreeBSD ZFS Tuning Guide](https://wiki.freebsd.org/ZFSTuningGuide) |