从0开发3D引擎(十二):使用领域驱动设计,从最小3D程序中提炼引擎(第三部分)
大家好,本文根据领域驱动设计的成果,实现了init API。
上一篇博文
从0开发3D引擎(十一):使用领域驱动设计,从最小3D程序中提炼引擎(第二部分)
下一篇博文
从0开发3D引擎(十三):使用领域驱动设计,从最小3D程序中提炼引擎(第四部分)
继续实现
实现“DirectorJsAPI.init”
实现“保存WebGL上下文”限界上下文
1、在src/api_layer/api/中加入DirectorJsAPI.re,实现API
DirectorJsAPI.re代码为:
let init = DirectorApService.init;
2、在src/infrastructure_layer/external/external_object/中加入Error.re,负责处理“js异常”这个外部对象
Error.re代码为:
//根据错误信息(string类型),创建并抛出“js异常”对象
let error = msg => Js.Exn.raiseError(msg);
//根据“js异常”对象,抛出它
let throwError: Js.Exn.t => unit = [%raw err => {|
throw err;
|}];
3、在src/application_layer/service/中加入DirectorApService.re,实现应用服务
DirectorApService.re代码为:
let init = contextConfigJsObj => {
CanvasCanvasEntity.getCanvas()
|> OptionContainerDoService.get
//OptionContainerDoService.get函数返回的是Result容器的包装值,需要调用ResultContainerVO.bind函数来处理容器内部的值
|> ResultContainerVO.bind(canvas => {
SetWebGLContextSetWebGLContextDoService.setGl(
contextConfigJsObj,
canvas,
)
})
//应用服务DirectorApService负责用抛出异常的方式处理Result错误
|> ResultContainerVO.handleFail(Error.throwError);
};
关于bind函数的使用,可以参考从0开发3D引擎(五):函数式编程及其在引擎中的应用->bind
4、修改CanvasCanvasEntity.re,实现getCanvas函数
CanvasCanvasEntity.re相关代码为:
let getCanvas = () => {
Repo.getCanvas();
};
5、把最小3D程序的WebGL1.re放到src/infrastructure_layer/external/library/中,保留所有的代码
WebGL1.re代码为:
open Js.Typed_array;
type webgl1Context;
type program;
type shader;
type buffer;
type attributeLocation = int;
type uniformLocation;
type bufferTarget =
| ArrayBuffer
| ElementArrayBuffer;
type usage =
| Static;
type contextConfigJsObj = {
.
"alpha": bool,
"depth": bool,
"stencil": bool,
"antialias": bool,
"premultipliedAlpha": bool,
"preserveDrawingBuffer": bool,
};
[@bs.send]
external getWebGL1Context:
('canvas, [@bs.as "webgl"] _, contextConfigJsObj) => webgl1Context =
"getContext";
[@bs.send.pipe: webgl1Context] external createProgram: program = "";
[@bs.send.pipe: webgl1Context] external useProgram: program => unit = "";
[@bs.send.pipe: webgl1Context] external linkProgram: program => unit = "";
[@bs.send.pipe: webgl1Context]
external shaderSource: (shader, string) => unit = "";
[@bs.send.pipe: webgl1Context] external compileShader: shader => unit = "";
[@bs.send.pipe: webgl1Context] external createShader: int => shader = "";
[@bs.get] external getVertexShader: webgl1Context => int = "VERTEX_SHADER";
[@bs.get] external getFragmentShader: webgl1Context => int = "FRAGMENT_SHADER";
[@bs.get] external getHighFloat: webgl1Context => int = "HIGH_FLOAT";
[@bs.get] external getMediumFloat: webgl1Context => int = "MEDIUM_FLOAT";
[@bs.send.pipe: webgl1Context]
external getShaderParameter: (shader, int) => bool = "";
[@bs.get] external getCompileStatus: webgl1Context => int = "COMPILE_STATUS";
[@bs.get] external getLinkStatus: webgl1Context => int = "LINK_STATUS";
[@bs.send.pipe: webgl1Context]
external getProgramParameter: (program, int) => bool = "";
[@bs.send.pipe: webgl1Context]
external getShaderInfoLog: shader => string = "";
[@bs.send.pipe: webgl1Context]
external getProgramInfoLog: program => string = "";
[@bs.send.pipe: webgl1Context]
external attachShader: (program, shader) => unit = "";
[@bs.send.pipe: webgl1Context]
external bindAttribLocation: (program, int, string) => unit = "";
[@bs.send.pipe: webgl1Context] external deleteShader: shader => unit = "";
[@bs.send.pipe: webgl1Context] external createBuffer: buffer = "";
[@bs.get]
external getArrayBuffer: webgl1Context => bufferTarget = "ARRAY_BUFFER";
[@bs.get]
external getElementArrayBuffer: webgl1Context => bufferTarget =
"ELEMENT_ARRAY_BUFFER";
[@bs.send.pipe: webgl1Context]
external bindBuffer: (bufferTarget, buffer) => unit = "";
[@bs.send.pipe: webgl1Context]
external bufferFloat32Data: (bufferTarget, Float32Array.t, usage) => unit =
"bufferData";
[@bs.send.pipe: webgl1Context]
external bufferUint16Data: (bufferTarget, Uint16Array.t, usage) => unit =
"bufferData";
[@bs.get] external getStaticDraw: webgl1Context => usage = "STATIC_DRAW";
[@bs.send.pipe: webgl1Context]
external getAttribLocation: (program, string) => attributeLocation = "";
[@bs.send.pipe: webgl1Context]
external getUniformLocation: (program, string) => Js.Null.t(uniformLocation) =
"";
[@bs.send.pipe: webgl1Context]
external vertexAttribPointer:
(attributeLocation, int, int, bool, int, int) => unit =
"";
[@bs.send.pipe: webgl1Context]
external enableVertexAttribArray: attributeLocation => unit = "";
"";
[@bs.send.pipe: webgl1Context]
external uniformMatrix4fv: (uniformLocation, bool, Float32Array.t) => unit =
"";
[@bs.send.pipe: webgl1Context]
external uniform1i: (uniformLocation, int) => unit = "";
[@bs.send.pipe: webgl1Context]
external uniform3f: (uniformLocation, float, float, float) => unit = "";
[@bs.send.pipe: webgl1Context]
external drawElements: (int, int, int, int) => unit = "";
[@bs.get] external getFloat: webgl1Context => int = "FLOAT";
[@bs.send.pipe: webgl1Context]
external clearColor: (float, float, float, float) => unit = "";
[@bs.send.pipe: webgl1Context] external clear: int => unit = "";
[@bs.get]
external getColorBufferBit: webgl1Context => int = "COLOR_BUFFER_BIT";
[@bs.get]
external getDepthBufferBit: webgl1Context => int = "DEPTH_BUFFER_BIT";
[@bs.get] external getDepthTest: webgl1Context => int = "DEPTH_TEST";
[@bs.send.pipe: webgl1Context] external enable: int => unit = "";
[@bs.get] external getTriangles: webgl1Context => int = "TRIANGLES";
[@bs.get] external getUnsignedShort: webgl1Context => int = "UNSIGNED_SHORT";
[@bs.get] external getCullFace: webgl1Context => int = "CULL_FACE";
[@bs.send.pipe: webgl1Context] external cullFace: int => unit = "";
[@bs.get] external getBack: webgl1Context => int = "BACK";
6、在src/domain_layer/domain/init/set_webgl_context/service/中加入SetWebGLContextSetWebGLContextDoService.re,创建领域服务SetWebGLContext
SetWebGLContextSetWebGLContextDoService.re代码为:
let setGl = (contextConfigJsObj, canvas): ResultContainerVO.t(unit, Js.Exn.t) => {
ContextContextEntity.setGl(contextConfigJsObj, canvas)
|> ResultContainerVO.succeed;
};
7、修改ContextContextEntity.re,实现setGl函数
ContextContextEntity.re相关代码为:
let setGl = (contextConfigJsObj, canvas) => {
ContextRepo.setGl(WebGL1.getWebGL1Context(canvas, contextConfigJsObj));
};
8、修改ContextPOType.re,定义Context PO的gl字段的数据类型
ContextPOType.re相关代码为:
type context = {
gl: option(WebGL1.webgl1Context),
...
};
9、修改ContextRepo.re,实现仓库对Context PO的gl字段的操作
ContextRepo.re代码为:
let getGl = gl => {
//将Option转换为Result
Repo.getContext().gl |> OptionContainerDoService.get;
};
let setGl = gl => {
Repo.setContext({...Repo.getContext(), gl: Some(gl)});
};
10、修改CreateRepo.re,实现创建Context PO的gl字段
CreateRepo.re相关代码为:
let create = () => {
...
context: {
gl: None,
...
},
};
实现“初始化所有Shader”限界上下文
1、重写DirectorApService.re
DirectorApService.re代码为:
let init = contextConfigJsObj => {
CanvasCanvasEntity.getCanvas()
|> ResultContainerVO.bind(canvas => {
SetWebGLContextSetWebGLContextDoService.setGl(
contextConfigJsObj,
canvas,
)
|> ResultContainerVO.bind(() => {InitShaderInitShaderDoService.init()})
})
|> ResultContainerVO.handleFail(Error.throwError);
};
2、加入值对象InitShader
在从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(第一部分)的“设计值对象InitShader”中,我们已经定义了值对象InitShader的类型,所以我们直接将设计转换为实现:
在src/domain_layer/domain/init/init_shader/value_object/中加入InitShaderInitShaderVO.re,创建值对象InitShader
InitShaderInitShaderVO.re代码为:
type singleInitShader = {
shaderId: string,
vs: string,
fs: string,
};
type t = list(singleInitShader);
3、在src/domain_layer/domain/shader/shader/value_object/中加入ProgramShaderVO.re,创建值对象Program,它的DO对应一个WebGL的program对象
ProgramShaderVO.re代码为:
type t =
| Program(WebGL1.program);
let create = program => Program(program);
let value = program =>
switch (program) {
| Program(value) => value
};
4、修改聚合根ShaderManager的DO
根据识别的引擎逻辑:
- 在初始化所有Shader时,创建每个Program
- 在渲染每个三角形时,根据Shader名称获得关联的Program
我们需要根据Shader id获得关联的Program,所以在ShaderManager DO中应该加入一个immutable hash map,它的key为Shader id,value为值对象Program的DO。
应该在领域视图的“容器”限界上下文中,加入值对象ImmutableHashMap、值对象MutableHashMap,其中ImmutableHashMap用于实现不可变的hash map,MutableHashMap用于实现可变的hash map。
现在来具体实现它们:
1)在src/domain_layer/domain/structure/container/value_object/中创建文件夹hash_map/
2)在hash_map/文件夹中加入ImmutableHashMapContainerVO.re、MutableHashMapContainerVO.re、HashMapContainer.re、HashMapContainerType.re
ImmutableHashMapContainerVO.re负责实现Immutable Hash Map;
MutableHashMapContainerVO.re负责实现Mutable Hash Map;
HashMapContainer.re从两者中提出的公共代码;
HashMapContainerType.re定义HashMap的类型。
因为HashMapContainer需要使用reduce来遍历数组,这个操作属于通用操作,应该作为领域服务,所以在领域视图的“容器”限界上下文中,加入领域服务Array。在src/domain_layer/domain/structure/container/service/中加入ArrayContainerDoService.re,创建领域服务Array。
相关代码如下:
ArrayContainterDoService.re
let reduceOneParam = (func, param, arr) => {
//此处为了优化,使用for循环和mutable变量来代替Array.reduce
let mutableParam = ref(param);
for (i in 0 to Js.Array.length(arr) - 1) {
mutableParam := func(. mutableParam^, Array.unsafe_get(arr, i));
};
mutableParam^;
};
HashMapContainerType.re
type t('key, 'value) = Js.Dict.t('value);
type t2('value) = t(string, 'value);
HashMapContainer.re
let createEmpty = (): HashMapContainerType.t2('a) => Js.Dict.empty();
let get = (key: string, map: HashMapContainerType.t2('a)) =>
Js.Dict.get(map, key);
let entries = (map: HashMapContainerType.t2('a)): array((Js.Dict.key, 'a)) =>
map |> Js.Dict.entries;
let _mutableSet = (key: string, value, map) => {
Js.Dict.set(map, key, value);
map;
};
let _createEmpty = (): Js.Dict.t('a) => Js.Dict.empty();
let copy = (map: HashMapContainerType.t2('a)): HashMapContainerType.t2('a) =>
map
|> entries
|> ArrayContainerDoService.reduceOneParam(
(. newMap, (key, value)) => newMap |> _mutableSet(key, value),
_createEmpty(),
);
ImmutableHashMapContainerVO.re
type t('key, 'value) = HashMapContainerType.t('key, 'value);
let createEmpty = HashMapContainer.createEmpty;
let set =
(key: string, value: 'a, map: HashMapContainerType.t2('a))
: HashMapContainerType.t2('a) => {
let newMap = map |> HashMapContainer.copy;
Js.Dict.set(newMap, key, value);
newMap;
};
let get = HashMapContainer.get;
MutableHashMap.re
type t('key, 'value) = HashMapContainerType.t('key, 'value);
let createEmpty = HashMapContainer.createEmpty;
let set = (key: string, value: 'a, map: HashMapContainerType.t2('a)) => {
Js.Dict.set(map, key, value);
map;
};
let get = HashMapContainer.get;
现在我们可以通过修改ShaderManagerShaderEntity.re来修改ShaderManager的DO,加入programMap字段
ShaderManagerShaderEntity.re相关代码为:
type t = {
...
programMap:
ImmutableHashMapContainerVO.t2(ShaderShaderEntity.t, ProgramShaderVO.t),
};
5、创建领域服务BuildInitShaderData,实现构造值对象InitShader
1)在src/domain_layer/domain/init/init_shader/service/中加入BuildInitShaderDataInitShaderDoService.re,创建领域服务BuildInitShaderData
BuildInitShaderDataInitShaderDoService.re代码为:
let build = () => {
ShaderManagerShaderEntity.getAllGLSL()
|> List.map(((shaderName, glsl)) => {
(
{
shaderId: ShaderShaderEntity.getId(shaderName),
vs: GLSLShaderVO.getVS(glsl),
fs: GLSLShaderVO.getFS(glsl),
}: InitShaderInitShaderVO.singleInitShader
)
});
};
2)修改GLSLShaderVO.re,实现getVS、getFS函数
GLSLShaderVO.re相关代码为:
let getVS = glsl =>
switch (glsl) {
| GLSL(vs, fs) => vs
};
let getFS = glsl =>
switch (glsl) {
| GLSL(vs, fs) => fs
};
3)修改ShaderManagerShaderEntity.re,加入getAllGLSL函数
ShaderManagerShaderEntity.re相关代码为:
let getAllGLSL = () => {
ShaderManagerRepo.getAllGLSL();
};
4)修改ShaderManagerRepo.re,加入getAllGLSL函数
ShaderManagerShaderEntity.re相关代码为:
let getAllGLSL = () => {
Repo.getShaderManager().glsls
|> List.map(((shaderId, (vs, fs))) => {
(ShaderShaderEntity.create(shaderId), GLSLShaderVO.create((vs, fs)))
});
};
6、在src/domain_layer/domain/init/init_shader/service/中加入InitShaderInitShaderDoService.re,创建领域服务InitShader
InitShaderInitShaderDoService.re代码为:
let init = (): ResultContainerVO.t(unit, Js.Exn.t) => {
ContextContextEntity.getGl()
|> ResultContainerVO.bind(gl => {
//从着色器DO数据中构建值对象InitShader
BuildInitShaderDataInitShaderDoService.build()
|> ResultContainerVO.tryCatch(initShaderData => {
initShaderData
|> List.iter(
(
{shaderId, vs, fs}: InitShaderInitShaderVO.singleInitShader,
) => {
let program = ContextContextEntity.createProgram(gl);
/* 注意:领域服务不应该直接依赖Repo
应该通过实体ContextContextEntity而不是ShaderManagerRepo来将program设置到ShaderManager PO的programMap中!
*/
ContextContextEntity.setProgram(shaderId, program);
ContextContextEntity.initShader(vs, fs, program, gl)
|> ignore;
//用于运行测试
Js.log((shaderId, vs, fs));
})
})
});
};
7、修改ContextContextEntity.re,实现相关函数
ContextContextEntity.re相关代码为:
let getGl = () => {
ContextRepo.getGl();
};
...
let createProgram = gl => gl |> WebGL1.createProgram;
let setProgram = (shaderId, program) => {
ShaderManagerRepo.setProgram(shaderId, program);
};
let _compileShader = (gl, glslSource, shader) => {
WebGL1.shaderSource(shader, glslSource, gl);
WebGL1.compileShader(shader, gl);
WebGL1.getShaderParameter(shader, WebGL1.getCompileStatus(gl), gl)
=== false
? {
let message = WebGL1.getShaderInfoLog(shader, gl);
//这里为了实现“从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(第一部分)”提出的“处理错误优化”,用“抛出异常”而不是Result来处理错误
Error.error(
{j|shader info log: $message
glsl source: $glslSource
|j},
);
}
: shader;
};
let _linkProgram = (program, gl) => {
WebGL1.linkProgram(program, gl);
WebGL1.getProgramParameter(program, WebGL1.getLinkStatus(gl), gl) === false
? {
let message = WebGL1.getProgramInfoLog(program, gl);
//这里为了实现“从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(第一部分)”提出的“处理错误优化”,用“抛出异常”而不是Result来处理错误
Error.error({j|link program error: $message|j});
}
: program;
};
let initShader = (vsSource: string, fsSource: string, program, gl) => {
let vs =
_compileShader(
gl,
vsSource,
WebGL1.createShader(WebGL1.getVertexShader(gl), gl),
);
let fs =
_compileShader(
gl,
fsSource,
WebGL1.createShader(WebGL1.getFragmentShader(gl), gl),
);
WebGL1.attachShader(program, vs, gl);
WebGL1.attachShader(program, fs, gl);
WebGL1.bindAttribLocation(program, 0, "a_position", gl);
_linkProgram(program, gl);
WebGL1.deleteShader(vs, gl);
WebGL1.deleteShader(fs, gl);
program;
};
8、修改ShaderManagerPOType.re,ShaderManager PO加入programMap字段
虽然programMap也是hash map,但不能直接使用领域层的值对象ImmutableHashMapContainerVO来定义它的类型!因为PO属于基础设施层,它不能依赖领域层!
因此,我们应该在基础设施层的“数据”中创建一个ImmutableHashMap.re模块,尽管它的类型和函数都与ImmutableHashMapContainerVO一样。
在src/infrastructure_layer/data/中创建文件夹structure/,在该文件夹中加入ImmutableHashMap.re。
为了方便,目前暂时直接用ImmutableHashMapContainerVO来实现ImmutableHashMap。
ImmutableHashMap.re代码为:
type t2('key, 'a) = ImmutableHashMapContainerVO.t2('key, 'a);
let createEmpty = ImmutableHashMapContainerVO.createEmpty;
let set = ImmutableHashMapContainerVO.set;
修改ShaderManagerPOType.re,ShaderManager PO加入programMap字段:
type shaderManager = {
...
programMap: ImmutableHashMap.t2(shaderId, WebGL1.program),
};
9、修改ShaderManagerRepo.re,实现setProgram函数
ShaderManagerRepo.re相关代码为:
let _getProgramMap = ({programMap}) => programMap;
let setProgram = (shaderId, program) => {
Repo.setShaderManager({
...Repo.getShaderManager(),
programMap:
_getProgramMap(Repo.getShaderManager())
//这里也使用基础设施层的“数据”的ImmutableHashMap,因为操作的是ShaderManager PO的programMap
|> ImmutableHashMap.set(shaderId, program),
});
};
10、修改CreateRepo.re,实现创建ShaderManager PO的programMap字段
CreateRepo.re相关代码为:
let create = () => {
...
shaderManager: {
...
programMap: ImmutableHashMap.createEmpty(),
},
};
实现用户代码并运行测试
1、在项目根目录上执行webpack命令,更新wd.js文件
yarn webpack
2、实现index.html相关代码
index.html代码为:
<script>
...
//准备webgl上下文的配置项
var contextConfig = {
"alpha": true,
"depth": true,
"stencil": false,
"antialias": true,
"premultipliedAlpha": true,
"preserveDrawingBuffer": false,
};
wd.Director.init(contextConfig);
</script>
3、运行测试
运行index.html页面
打开控制台,可以看到打印了两次数组,每次数组内容为[Shader名称, vs, fs],其中第一次的Shader名称为“shader2”,第二次为“shader1”
从0开发3D引擎(十二):使用领域驱动设计,从最小3D程序中提炼引擎(第三部分)的更多相关文章
- 从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(上)
目录 上一篇博文 下一篇博文 前置知识 回顾上文 最小3D程序完整代码地址 通用语言 将会在本文解决的不足之处 本文流程 解释本文使用的领域驱动设计的一些概念 本文的领域驱动设计选型 设计 引擎名 识 ...
- 从0开发3D引擎(十一):使用领域驱动设计,从最小3D程序中提炼引擎(第二部分)
目录 上一篇博文 本文流程 回顾上文 解释基本的操作 开始实现 准备 建立代码的文件夹结构,约定模块文件的命名规则 模块文件的命名原则 一级和二级文件夹 api_layer的文件夹 applicati ...
- iOS 11开发教程(十二)iOS11应用视图始祖——UIView
iOS 11开发教程(十二)iOS11应用视图始祖——UIView 在Swift中,NSObject是所有类的根类.同样在UIKit框架(UIKit框架为iOS应用程序提供界面对象和控制器)中,也存在 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明
紧接上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明>,这里专讲OAuth2.0. 理解OAuth2.0 首先我们通过一张图片来了解一下OAu ...
- 从0开发3D引擎(补充):介绍领域驱动设计
我们使用领域驱动设计(英文缩写为DDD)的方法来设计引擎,在引擎开发的过程中,领域模型会不断地演化. 本文介绍本系列使用的领域驱动设计思想的相关概念和知识点,给出了相关的资料. 上一篇博文 从0开发3 ...
- Python开发【第二十二篇】:Web框架之Django【进阶】
Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...
- 《C++游戏开发》笔记十二 战争迷雾:初步实现
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9475979 作者:七十一雾央 新浪微博:http:/ ...
- 网站开发进阶(四十二)巧用clear:both
网站开发进阶(四十二)巧用clear:both 前言 我们在制作网页中用div+css或者称xhtml+css都会遇到一些很诡异的情况,明明布局正确,但是整个画面却混乱起来了,有时候在IE6下看的很正 ...
- 【小梅哥FPGA进阶教程】第十二章 数字密码锁设计
十二.数字密码锁设计 本文由山东大学研友袁卓贡献,特此感谢 实验目的 实现数字密码锁设计,要求矩阵按键输出且数码管显示输入密码,密码输入正确与否均会有相应标志信号产生. 实验平台 芯航线FPGA核心板 ...
随机推荐
- vue项目打包部署elementUI的字体图标丢失问题
自己搭建的Vue项目,没有使用vue-cli,引入elementUI时,使用的是webpack直接打包工具,发现字体图标丢失你 记录一下解决办法: webpack module配置:(build目录下 ...
- php time()时间戳作为文件名产生文件同名的bug
/*time()函数生成的文件名可能是相同的,因为如果php运行的过程如果足够快,time()函数调用的足够频繁,那么有可能time()生成的时间戳会相同,因为时间戳是以秒为单位,所以如果足够频繁有可 ...
- css后续篇
5.盒模型 在css中,box model这一术语是用来设计和布局时使用的,在网页中显示一些方方正正的盒子,这种盒子就叫盒模型 盒模型有两种: 标准模型和IE模型(了解) 盒模型属性 width : ...
- mysql之存储过程(一)
今天开发一个需求,需要在一个旧表中增加一列并且对已经的表中记录初始化新列的值, 由于是一次性的工作,故写了个存储过程来代替代码程序初始化 创建及执行过程记录如下: MySQL [XXX_YYY]> ...
- Web Service概述 及 应用案例
Web Service的定义 W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计.Web Service服务通常被定义为一组模块化的API,它们可以通过网络进行调用 ...
- COMET探索系列二【Ajax轮询复用模型】
写在前面:Ajax轮询相信大家都信手拈来在用,可是有这么一个问题,如果一个网站中同时有好多个地方需要用到这种轮询呢?就拿我们网站来说,有一个未读消息数提醒.还有一个时实时加载最新说说.昨天又加了一个全 ...
- UITextView实现PlaceHolder的方式
实现UITextView实现PlaceHolder的方式的方式有两种,这两种方法的核心就是通过通知来添加和去除PlaceHolder:下面来介绍两种方法:个人比较喜欢第一种,看起来更加合理. 方法1: ...
- 2010提高组-C]关押罪犯(扩展域并查集
题:https://www.cometoj.com/problem/0073 #include<bits/stdc++.h> using namespace std; ; struct n ...
- python、anaconda、jupyter notebook、pycharm、spyder
说明: 1.anaconda把任何东西都当做包来管理. 2.anaconda本省集成了python和conda.spyder.numpy等. 3.pip只用于python,conda可用于多种语言. ...
- python语法基础-基础-控制语句
############### if条件控制语句 ############### # 以下实例 x 为 0-99 取一个数,y 为 0-199 取一个数,如果 x>y 则输出 x,如 ...