要清楚在区块链上开发DApp的架构[x][][][][][]
 
DApp是去中心化的应用
 
  • 基于智能合约
  • 去中心化的游戏规则
  • 代币激励
 
 
DApp前端也是使用H5, 使用Javascript
后端不是服务器,而是合约语言
DApp的本质是智能合约
 
DApp的前端是给智能合约一个友好的界面
 
智能合约是部署在区块链上的
 
DApp应该是社区去运营,而不是单一公司运营
 
原生代币 coin , 合约代币 token
 
通过钱包转账, 例如 MetaMask
 
不是所有的共识都要达成全球共识
 
核心架构组件
 
第一层是NervosCKB
    common Knowledge Base, POW , 存放通用数据
第二层 AppChain
    企业应用链,局部共识, 各种源头, 开源, 1.5w TPS
第一层和第二层的关系
    AppChain 上的部分数据写入CKB, 部分代币抵押到CKB
 
 
 
万云 免费部署DApp
 
 
一个运行在区块链上的程序,不管是怎样的,都叫智能合约.
区块链提供智能合约所需的信任
 
图灵完备 (以太坊)
 
Solidity也可以运行在Nervos之上
 
EVM 以太坊的虚拟机
 
比特币比喻为区块链上的数据库
以太坊比喻为区块链上的虚拟机 (EVM)
 
以太坊运行代码要使用Gas
Gas租用系统资源
 
 
各种库,调用合约的各种功能:
Web3.js(以太坊)  Nervos.js(Nervos)
 
 
Solidity 合约语言入门
 
Solidity可以运行在EVM上
 
 
 
https://cryptozombies.io/zh   solidity合约语言的w3cschool
 
一份名为HelloWorld的空合约如下:
contract HelloWorld{
 
}
 
版本指令 : 所有的Solidity源码都必须冠以"version pragma"标明Solidity编译器的版本, 以避免将来新的编译器可能破坏你的代码.
 
例如 pragma solidity ^0.4.19;  (当前Solidity的最新版本是0.4.19)
 
综上,下面就是一个最基本的合约 - 每次建立一个新的项目时的第一段代码:
pragma solidity ^0.4.19;
 
contract HelloWorld {
 
}
 
状态变量和整数
状态变量是被永久地保存在合约中, 也就是说它们被写入以太币区块链中, 想象成写入一个数据库.
 
例子:
contract Example{
    //这个无符号证书将会永久地被保存在区块链中
    uint myUnsignedInteger = 100;
}
 
上面例子中, 定义 myUnsignedInteger 为 uint 类型, 并赋值100.
 
无符号整数 : uint
 
uint无符号数据类型, 指其值不能是负数, 对于有符号的整数存在名为 int 的数据类型.
 
注: Solidity中, uint实际上是uint256代名词, 一个256位的无符号整数, 你也可以定义位数少的 uints :
uint8, uint16, uint32, 等... 但一般来讲你愿意使用简单的uint, 除非在某些特殊情况下.之后会讲.
 
 
结构体:
有时需要更复杂的数据类型, Solidity提供了结构体:
struct Person{
    uint age;
    string name;
}
结构体允许你生成一个更复杂的数据类型, 它有多个属性.
 
注: 我们刚刚引进了一个新类型, string , 字符串用于保存任意长度的 UTF-8 编码数据, 
如 string greeting = "Hello World!"
 
使用结构体
pragma solidity ^0.4.19;
 
contract ZombieFactory {
 
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
 
struct Zombie {
string name;
uint dna;
}
 
}
 
数组:
如果你想建立一个集合,可以用数组这样的数据类型, Solidity支持两种数组: 静态数组 和 动态数组.
 
//固定长度为2的静态数组:
uint[2] fixedArray;
// 固定长度为5的string类型的静态数组:
string[5] stringArray;
//动态数组, 长度不固定, 可以动态添加元素:
uint[] dynamicArray;
也可以建立一个结构体类型数组, 例如:
Person[] people;  //这是动态数组, 可以不断添加元素
 
记住: 状态变量被永久保存在区块链中. 所以在你的合约中创建动态数组来保存成结构的数据是非常有意义的.
 
公共数组
你可以定义public数组, Solidity会自动创建getter方法:
Person[] public people;
 
其他的合约可以从这个开放public数组读取数据(但不能写入数据), 所以这在合约中是一个有用的保存公共数据的模式.
 
 
pragma solidity ^0.4.19;  //版本
 
contract ZombieFactory {  //创建合约
    
    uint dnaDigits = 16;   //无符号整型
    uint dnaModulus = 10 ** dnaDigits;  //10^dnaDigits 幂运算
 
    struct Zombie {   //数据构造器
        string name;
        uint dna;
    }
 
    Zombie[] public zombies; //Zombie类型数组 公开
}
 
 
定义函数
 
在Solidity中函数定义的句法如下:
function eatHamburgers(string _name, uint _amount) {
 
}
注意这里是function 而不是 contract 也不是 struct 
 
这是一个名为eatHamburgers的函数, 它接受两个参数,参数类型在参数名前面.
 
注: 习惯上函数里的变量都是以 _ 开头 (但不是硬性规定) 以区别全局变量. 整个教程沿用这个习惯.
 
我们的函数定义如下:
eatHamburgers("jack",100);
 
function函数创建Zombie, 并将其添加到zombies数组中.
 
其他示例:
Person satoshi = Person(172,"Satoshi");
 
people.push(satoshi);
 
你也可以两步并一步, 用一行代码更简洁:
people.push(Person(16,"Satoshi"));
 
 
注: array.push() 在数组的尾部加入新元素, 所以元素在数组中的顺序就是我们添加的顺序, 如:
uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// numbers is now equal to [5,10,15] 
 
私有/公共函数
 
Solidity定义的函数的属性默认为公共. 这就意味着任何一方(或其他合约)都可以调用你合约里的函数.
 
显然, 不是任何时候都需要这样, 而且这样的合约易于受到攻击. 所以将自己的函数定义为 私有 是一个好的编程习惯, 只有当你需要外部世界调用它时才将它设置为公共.
 
如何定义私有函数?
uint[] numbers;
 
function _addToArray(uint _number) private {
    numbers.push(_number);
}
这意味着只有我们合约中的其它函数才能够调用这个函数, 给numbers数组添加新成员.
可以看到, 在函数名后面使用private关键即可. 和函数的参数类似, 私有函数名字用 _ 开始 (非硬性)
这与public的数组类似,访问范围类型都写在后面.
 
函数的更多属性
返回值:
想要函数返回一个数值, 按如下定义:
string greeting = "What's up boy?";
 
function sayHello() public returns (string) {
    return greeting;
}
 
Solidity里, 函数的定义里可包含返回值的数据类型 ( 如本例中 string).
 
函数的修饰符
上面的函数实际上没有改变Solidity里的状态, 即, 它没有改变任何值或者写任何东西.
 
这种情况下我们可以把函数定义为 view , 意味着它智能读取数据不能更改数据:
 
function sayHello() public view returns (string) {
}
view类似java中的void
 
Solidity还支持pure函数, 表明这个函数甚至都不访问应用里的数据, 例如:
function _multiply(uint a, uint b) private pure returns (uint) {
    return a*b;
}
意思是没有访问其它作用于的数据.只是自己函数参数的数据,可以加pure
 
注: 可能很难记住任何时把函数标记为 pure/view .幸运的是 , Solidity 编辑器会给出提示, 提醒你使用这些修饰符.
 
返回函数:
function _generateRandomDna(string _str) private view returns (uint) {
// 这里开始
}
 
Keccak256 和 类型转换
Ethereum内部有一个散列函数 keccak256 , 它用了SHA3版本, 一个散列函数基本上就是把一个字符串转换为一个256位的16进制的数字, 
字符串的一个微小变化会引起散列数据极大变化.
 
这在Ethereum 中有很多应用, 但是现在我们只是用它造一个伪随机数.
 
例子:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5 
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9 
keccak256("aaaac");
 
显而易见,输入字符串只改变了一个字母, 输入就已经天壤之别了.
注: 在区块链中安全地产生一个随机数是一个很难的问题, 本例的方法不安全, 但是在这个Zombie DNA算法里不是那么重要
 
类型转换
有时你需要变换数据类型. 例如:
uint8 a = 5;
uint b = 6;
//将会抛出错误, 因为a*b 返回uint, 而不是uint8;
uint8 c = a * b;
//我们需要将b转换为uint8;
uint8 c = a * uint8(b);
 
上面, a * b 返回类型是 uint , 但是当我们尝试用 uint8 类型接收时, 就会造成潜在的错误.
如果把它的数据类型转换为 uint8 , 就可以了, 编译器也不会出错.
 
function _generateRandomDna(string _str) private view returns (uint) {
// 这里开始
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
 
 
放在一起
写一个公共函数把所有的部件连接起来:
function createRandomZombie(string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
 
事件
 
我们的合约几乎就要完成了! 让我们加上一个事件.
事件是合约和区块链通讯的一种机制, 你的前端应用"监听"某些事件, 并做出反应.
//这里建立事件
event IntegersAdded(uint x, uint y, uint result);
 
function add(uint _x, uint _y) public {
    uint result = _x + _y;
        //触发事件, 通知app
        IntegersAdded(_x , _y , result);
        return result;
}
 
你的 app 前端可以监听这个事件. JavaScript实现如下:
YourContract.IntegersAdded(function(error,result)){
    //doSomething
}
 
 
array.push()返回的是数组的当前长度.
要获取某个元素的下标index , 则应该在获取的 array.push()返回的uint值上-1
 
// 这里建立事件
event NewZombie(uint zombieId,string name,uint dna);
 
function _createZombie(string _name, uint _dna) private {
 uint id = zombies.push(Zombie(_name, _dna)) - 1;
 // 这里触发事件
 NewZombie(id,_name,_dna);
}
 
 
Web3.js
Solidity合约完工了, 现在写一段Javascript前端代码来调用这个合约.
以太坊的JavaScript库, 名为Web3.js
 
在后面的课程里,我们会进一步地教你如何安装一个合约,如何设置Web3.js。 但是现在我们通过一段代码来了解 Web3.js 是如何和我们发布的合约交互的吧。
 
如果下面的代码你不能全都理解,不用担心.
//下面是调用合约的方式:
var abi = /* abi是由编译器生成的 */
var ZombieFactoryContract = web3.eth.contract(abi);
var contractAddress = /* 发布之后在以太坊上生成的合约地址 */
var ZombieFactory = ZombieFactoryContract.at(contractAddress);
 
// `ZombieFactory` 能访问公共的函数以及事件
 
//某个监听文本输入的监听器:
$("#ourButton").click(createRandomZombie);
 
function createRandomZombie(){
    var name = $("#nameInput").val();
    ZombieFactory.createRandomZombie(name);
}
 
 
//监听 `NewZombie` 事件, 并且更新UI
var event = ZombieFactory.NewZombie(function(error,result){
    if(error){
        return;
    }
    generateZombie(result.zombieId, result.name, result.dna);
})
 
//获取Zombie的dna, 更新图像
function generateZombie(id, name, dna){
    let dnaStr = String(dna);
    //如果dna少于16位, 在它前面用0补上.
    while (dnaStr.length<16){
        dnaStr = "0" + dnaStr;
    }
 
    let zombieDetails = {
        //前两位数构成头部, 我们可能有7种头部, 所以 % 7
        //得到的数在0-6, 再加上1, 数的范围变成1-7
        //通过这样计算:
        headChoice: dnaStr.substring(0,2) % 7 + 1,
        //我们得到的图片名称从 head1.png 到 head7.png
    
        //接下来的两位数构成眼睛, 眼睛变化就对11取模:
        eyeChoice: dnaStr.substring(2,4) % 11 +1;
        //再接下来的两位数构成一幅, 衣服变化就对6取模:
        shirtChoice: dnaStr.substring(4,6) % 6 + 1;
        //最后6位控制颜色, 用css选择器: hue-rotate来更新
        //360度:
        skinColorChoice: parseInt(dnaStr.substring(6,8) / 100 * 360),
        eyeColorChoice: parseInt(dnaStr.substring(8,10) / 100 * 360),
        clothesColorChoice: parseInt(dnaStr.substring(10,12) /100 * 360),
        zombieName: name,
        zombieDescription: "A Level 1 CryptoZombie",
    }
    return zombieDetails;
}
 
我们的JavaScript所做的就是获取由zombieDetails产生的数据,并且利用浏览器里的JavaScript神奇功能(我们用Vue.js),置换出
图像以及使用CSS过滤器. 在后面课程,可以看到全部代码.
 
单独保存图片时,有个有意思的事情,会把单独僵尸的眼睛给保存下来,也就是说确实是不同的图片部件组合起来的僵尸哦!
 
 
第二课:
僵尸放入区块链数据库 组合生成新的僵尸 (例如将其名称相加,取他们的值)
 
映射(Mapping) 和 地址(Address)
 
Addresses (地址)
以太坊区块链由 _account_ (账户)组成, 你可以把它想象成银行账户, 一个账户余额是 _以太_ (在以太坊区块链上使用的币种) , 你可以和其它账户之间支付和接受以太币, 就像你的银行账户可以电汇资金到其它银行账户一样.
 
每个账户都有一个"地址", 你可以把它想象成银行账号, 这是账户唯一的标识符, 它看起来长这样:
0x0cE446255506E92DF41614C46F1d6df9Cc969183
(这是CryptoZombies团队的地址)
我们将在后面介绍地址的细节. 现在只需要了解地址属于特定用户(或智能合约)的.
所以我们可以指定"地址"作为僵尸主人的ID. 当用户通过与我们的应用程序交互来创建新的僵尸时,新僵尸的所有权被设置到调用者的以太坊地址下.
 
Mapping (映射)
第1课时,我们看到了 _结构体_ 和 _数组_ . _映射_ 是另一种在Solidity中存储有组织数据的方法.
映射是这样定义的:
//对于金融应用程序,将用户的余额保存在一个uint类型的变量中:
mapping(address => uint) public accountBalance;
//或者可以用来通过userId 存储/查找的用户名
mapping(uint => string) userIdToName;
 
映射本质上是存储和查找数据所用的键值对, 在第一个例子中, 键是一个address , 值是一个 uint , 
在第二个例子中, 键是一个uint, 值是一个string. (左键右值)
 
Msg.sender
现在有了一套映射来记录僵尸的所有权了, 我们可以修改 _createZombie 方法来运用它们.
为了这一点,我们要用到 msg.sender
 
msg.sender
在Solidity中,有一些全局变量可以被所有函数调用, 其中一个就是 msg.sender , 它指的是当前调用者(或智能合约)的 address .
 
注意: 在Solidity中, 功能执行始终需要从外部调用者开始 . 一个合约只会在区块链上什么也不做, 除非有人调用其中的函数. 所以msg.sender总是存在的.
 
以下是使用msg.sender来更新mapping的例子:
mapping (address => uint) favoriteNumber;
 
function setMyNumber(uint _myNumber) public {
    //更新我们的 `favoriteNumber` 映射来将 `_myNumber` 存储在 `msg.sender` 名下
    favoriteNumber[msg.sender] = _myNumber;
    //存储数据至映射的方法和将数据存储在数组相似
}
 
function whatIsMyNumber() public view returns (uint) {
    //拿到存储在调用者地址名下的值
    //若调用者还没调用 setMyNumber , 则值为 `0`
    return favoriteNumber[msg.sender];
}
 
这里例子中,任何人都可以调用 setMyNumber 在我们合约中存下一个 uint 并且与他们的地址相绑定.
然后, 他们调用 whatIsMyNumber 就会返回他们存储的 uint.
使用 msg.sender 很安全, 因为它具有以太坊区块链的安全保障 -- 除非窃取与以太坊地址相关联的私钥,否则是没有办法修改其他人的数据的.
 
Require
怎样限定每个玩家只调用一次函数呢?
答案是使用 require , require 使得函数在执行过程中, 当不满足某些条件时 抛出错误, 并停止执行:
 
function sayHiToVitalik(string _name) public returns (string) {
    //比较 _name 是否等于 "Vitalik" , 如果不成立, 抛出异常并终止程序
    //(敲黑板: Solidity 并不支持原生的字符串比较, 我们只能通过比较两个字符串的 keccak256 哈希值来进行判断)
    require(keccak256(_name) == keccak256("Vitalik"));
    //如果返回true, 运行如下语句
    return "Hi!";
}
 
如果你这样调用函数 sayHiToVitalik("Vitalik") ,它会返回 Hi! ,而如果调用的时候使用了其它参数,
它则会抛出错误并停止执行.
 
因此,在调用一个函数之前, 用require验证前置条件是非常有必要的.
 
我们使用了 require 来确保这个函数只有在每个用户第一次调用它的时候执行, 用以创建初始僵尸.
  1. 在createRandomZombie的前面放置 require 语句, 使得函数先检查 ownerZombieCount[msg.sender] 的值为0 , 不然就抛出一个错误.
 
注意: 在Solidity中, 关键字放置的顺序并不重要 
  • 虽然参数的两个位置是等效的, 但是,由于我们的答案检查器比较呆板, 它只能认定其中一个为正确答案.
  • 于是在这里, 我们就约定把 ownerZombieCount[msg.sender]放前面吧.
 
这里的就是说,先检查这个计数器是否是0. 这样的话就可以验证这个方法只执行几次
function createRandomZombie(string _name) public {
    require(ownerZombieCount[msg.sender] == 0);
    uint randDna = _generateRandomDna(_name);
    _createZombie(_name, randDna);
}
 
继承(Inheritance)
当代码过于冗长的时候, 最好将代码和逻辑分拆到多个不同的合约中, 以便于管理.
有个让Solidity的代码易于管理的功能, 就是合约 inheritance (继承):
contract Doge {
    function catchphrase() public returns (string) {
        return "So Wow CryptoDoge";
    }
}
 
contract BabyDoge is Doge {
    function anotherCatchphrase() public returns (string) {
        return "Such Moon BabyDoge";
    }
}
 
由于BabyDoge是从Doge那里 inherits (继承) 过来的. 这意味着当你编译和部署了BabyDoge,
它将可以访问 catchphrase() 和 anotherCatchphrase() 和其它我们在 Doge中定义的其它公共函数.
 
这可以用于逻辑继承 (比如表达子类的时候, Cat是一种Animal). 但也可以简单地将类似的逻辑组合到不同的合约中以组织代码.
 
回顾:
pragma solidity ^0.5.10;
 
 
contract ZombieFactory {
 
 
    event NewZombie(uint zombieId, string name, uint dna);  //事件
 
 
    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;
 
 
    struct Zombie {      //构造器 数据
        string name;
        uint dna;
    }
 
 
    Zombie[] public zombies;  //数组 类型为[]前的
 
 
    mapping (uint => address) public zombieToOwner;    //映射  键值对形式
    mapping (address => uint) ownerZombieCount;
 
 
    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;  
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }
 
 
    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }
 
 
    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }
 
 
}
 
 
引入(Import)
在Solidity中, 当你有多个文件并且想把一个文件导入另一个文件时, 可以使用import语句:
import "./someothercontract.sol";
 
contract NewContract is SomeOtherContract{
 
}
 
这样当我们在合约(contract) 目录下有一个名为 someothercontract.sol 的文件
( ./ 就是同一目录的意思) , 它就会被编译器导入.
 
Storage 与 Memory
在Solidity中, 有两个地方可以存储变量 -- storage 或 memory .
Storage 变量是指永久存储在区块链中的变量, Memory 变量则是临时的, 当外部函数对某合约调用完成时,
内存型变量即被移除, 你可以把它想象成存储在你电脑的硬盘或是RAM中数据的关系.
 
状态变量(在函数之外声明的变量).
 
然而也有一些情况下, 你需要手动声明存储类型, 主要用于处理函数内的 _结构体_ 和 _数组_ 时:
contract SandwichFactory {
    struct Sandwich {
        string name;
        string status;
    }
 
    Sandwich[] sandwiches;
 
    function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index]; // ^ 看上去很直接,不过 Solidity 将会给出警告 // 告诉你应该明确在这里定义 `storage` 或者 `memory`。 // 所以你应该明确定义 `storage`:
        Sandwich storage mySandwich = sandwiches[_index];
// ...这样 `mySandwich` 是指向 `sandwiches[_index]`的指针 // 在存储里,另外...
        mySandwich.status = "Eaten!";
// ...这将永久把 `sandwiches[_index]` 变为区块链上的存储 // 如果你只想要一个副本,可以使用`memory`:
 
    }
 
}
 
 
 
 
 
 
 
 
 
 
 
 

杂乱的Solidity - 2019-7-13的更多相关文章

  1. 每日一练ACM 2019.04.13

    2019.04.13 第1002题:A+B Proble Ⅱ Problem DescriptionI have a very simple problem for you. Given two in ...

  2. Java 9 ← 2017,2019 Java → 13 ,都发生了什么?

    距离 2019 年结束,只剩下 35 天了.你做好准备迎接 2020 年了吗? 一到年底,人就特别容易陷入回忆和比较之中,比如说这几天, 的对比挑战就火了! 这个话题登上了微博的热搜榜,也刷爆了朋友圈 ...

  3. Java 9 ← 2017,2019 → Java 13,来看看Java两年来的变化

    距离 2019 年结束,只剩下 33 天了.你做好准备迎接 2020 年了吗? 一到年底,人就特别容易陷入回忆和比较之中,比如说这几天的对比挑战就火了! 这个话题登上了微博的热搜榜,也刷爆了朋友圈, ...

  4. 2019 IEEEXtreme 13.0 Impact Factor 影响因子

    Impact Factor 题目大意 顾名思义,求影响因子.有 n 行 json 数据,第一行为期刊信息,后面为出版商出版的文章信息.   输入输出 点击查看详细 输入: 6 {"publi ...

  5. 2019.04.13 python基础

    第一节    主要讲python背景  没什么要注意的  了解记住概念就好 python官网  python.org  自带shell  可以运行python代码 在IDLE中怎么运行代码 新建文本  ...

  6. 2019 IEEEXtreme 13.0 题解记录

    比赛时间 2019.10.19 8:00 - 2019.10.20 8:00 比赛网站 https://csacademy.com/ieeextreme13 // 连续24小时做题真的是极限体验 // ...

  7. python第二周。2019.4.13

    1, 我绘制大蟒蛇就是..保存也对着呢,你要是打开文件的话,先闪个黑屏,再闪个白屏..自动退出,然后啥都没了. 我觉得是我代码编错了...再来一遍! 这次到可以,但是这个大蟒蛇好像没有回头... 刚才 ...

  8. 2019.2.13 SW

  9. 2019.01.13 bzoj4538: [Hnoi2016]网络(树链剖分)

    传送门 树链剖分一眼题. 题意简述: 给定一棵树,有三种操作: 加入一条路径 删除一条已加入的路径 询问不过一个点x的路径的最大值. 思路: 直接树链剖分维护答案. 因为询问的事不过点xxx的最大值, ...

随机推荐

  1. git 学习笔记 --Bug分支

    软件开发中,bug就像家常便饭一样.有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除. 当你接到一个修复一 ...

  2. 【leetcode-91 动态规划】 解码方法

    一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 给定一个只包含数字的非空字符串,请计算解码方法的总数. 示例 1 ...

  3. 超全、超详的Spring Boot配置讲解笔记

    springboot默认加载配置 SpringBoot使用两种全局的配置文件,全局配置文件可以对一些默认配置进行修改. application.properties application.yml 这 ...

  4. Quartz基础调度框架-第一篇控制台

    Quartz基础调度框架 Quartz核心的概念:scheduler任务调度.Job任务.Trigger触发器.JobDetail任务细节 结构 Conf 在这个基本结构里 是用来存放配置 publi ...

  5. C# vb .net实现玻璃桌子效果滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的玻璃桌子效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第 ...

  6. 用HTML、CSS、JS制作圆形进度条(无动画效果)

    逻辑 1.首先有一个圆:蓝色的纯净的圆,效果: 2.再来两个半圆,左边一个,右边一个将此蓝色的圆盖住,效果: 此时将右半圆旋转60°,就会漏出底圆,效果:   然后我们再用一个比底圆小的圆去覆盖这个大 ...

  7. tf.tile()函数的用法

    y = tf.tile(tf.range(2, dtype=tf.int32)[:, tf.newaxis], [2,3]) # tf.tile(input,[a,b]) 输入数据,按照对应维度将矩阵 ...

  8. 【MySQL】数据库中间件Atlas

    1.介绍 Atlas 是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目.它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改 ...

  9. 【转载】C#中Convert.ToInt32方法将字符串转换为Int32类型

    在C#编程过程中,可以使用Convert.ToInt32方法将字符串或者其他可转换为数字的对象变量转换为ToInt32类型,Convert.ToInt32方法有多个重载方法,最常使用的一个方法将字符串 ...

  10. 修复使用sub和sup时的行间距问题

    sub和sup元素会轻微地增大行高. 幸好,用一点CSS就可以修复这个问题. 来自Nicolas Gallagher和Jonathan Neal的normalize.css(http://necola ...