0%

diesel使用的一些小知识点

最近因为使用diesel,大致看了diesel的文档同时结合自己两天内写代码的经验,总结出下面几条要点,作为参考。严格来说,这些知识点比较琐碎不能够成为一篇文章,但是想到可以为自己以后使用diesel作参考还是写出来。

  1. 设置好.env之后 diesel setup 是创建数据库的

  2. diesel migration generate create_posts 最后一个参数是创建migration的文件夹名的 它会生成一个带时间的migration文件 里面有个up和down的sql up 里面负责创建数据库表的 down 里面负责撤回车操作的

  3. up 和 down 里面的sql要自己创建

  4. diesel migration run 执行migration里面的up.sql的操作的 确切的说就是建表 5. diesel migration redo 执行migration里面的down.sql的操作 确切的拉说是删除表

  5. 注意了 setup是会创建数据库和直接运行migration run中的建表数据的 但是如果数据库存在是不会运行的。如果在 migration中添加,后续这个也是不会运行的

  6. 这一步进行之后schema文件也会生成。这个文件是可以改的 ,但是不建议改

  7. 如果升级数据库,请使用新的migration文件

  8. 注意使用extern crate diesel 和#[marco use],不然schema下的东西又是找不到的

    以上的条目中需要补充的还有如下

    本来以为在rust2018版本以后,就完全不使用extern crate了。但是事实证明还是需要extern crate diesel和#[marco_use]的,不然会有很多东西找不到

    使用diesel尽量引入prelude::* 虽然我不喜欢这样引入,但是自己精确引入会很麻烦

    一定要注意schema文件

如果使用只是使用diesel查询和写入如何简化代码

这个问题会显的很奇怪,因为作为一个ORM,本来就是辅助我们进行增删改查的。其实我们看了diesel的官方指引之后就会觉得这个问题很合理。重新表述如下

根据diesel的官方指引,我们是用建立连接,建立数据库,设计表这几个步骤一路走下来的。实际上,大多数时候,数据表并不是我们建立的。我们只需要使用diesel的库,连接数据库CRUD即可。这种情况下,如何简化操作呢?

根据我的尝试大致需要如下

  1. 引入库,这个是必须的

  2. 设置sql路径,这个是必须的

  3. 无需diesel setup因为我们不需要建立数据库,数据库是存在的

  4. 无需diesle migration run 因为表也不是我们建立的。

  5. 没有建表,没运行diesel migration run,则diesel不会为我们自动建立schema.rs文件,也不会在schema.rs中生成table宏的语法。所以我们需要手动去建立schema.rs的文件,src/schema.rs。就是必须建立的在src路径下面。这样才不会出错。然后我们在schema.rs中手动写入 table!宏。这样才可以正常使用。

  6. 以上的步骤是可以正常使用的,也是文件最少的情况。还有下面的情况补充

    如果我们运行了diesel set 如果数据库存在,是不会建立数据库的。会产生一个migrations文件。一个和Cargo.toml同级别的diesel.toml文件,里面配置了schema.rs文件的位置。 所以上一步中如果不运行diesel set,需要我们手动把schema文件放在指定的位置。

    同时,因为我们 不建表,所以不运行diesel migration run。schema里面是空的,为了正确使用orm的方便特性,我们得去手动补全这个文件,自己写table!宏。当然这个很容易。看example即可

    一些零碎的知识,其他的正常使用参考文档即可。这个算是在文档之外的补充备查!

阅读全文 »

占星与星座计算 morinus house system

在系列前三节,已经基本介绍了和占星有关的模型,基本的分宫制的原理和占星以及天文中所出现的术语,这一节将带大家进行一次实际的占星计算。本文仅涉及星盘的计算,不涉及其他。

本文的重点是星盘的计算。本文将采用莫林分宫制morinus house system!至于为什么选择莫林分宫制,是因为莫林分宫制可以找到具体的数学计算公式,便于计算。且莫林分宫制在高纬度地区受到的扭曲小。至于分宫制的基本原理,可以查看上一节分宫制基本原理。

对于“morinus house system”是有专门的计算方法,通过一系列的加减乘除算出一个人出生时的MC、Asc以及每个宫头所在得度数,就可以排出一个近乎完整的星图了。

ASC:东升点是在诞生时刻在诞生地点的东面地平与黄道交接的一点

MC:天顶

我们在计算的时候需要以下参数

  1. 当事人的出生位置,需要从具体地点转化成经纬度
  2. 出生时间,需要转化为恒星时
  3. 黄赤交角,这个是固定值。关于黄赤交角可以看第一节天球部分。23°27′
  4. 以及如下公式

MC = arctan(tan(RAMC) / cos(e))

RAMC为上中天的赤经度数

阅读全文 »

占星与星座计算 黄道十二宫与分宫原理

在占星与星座计算的第一篇文章中,尽可能详细的介绍了天球的构建过程原理和相关的概念。这一节我来介绍何为黄道12宫以及分工制的基本原理,不涉及具体分宫制。

黄道带

上一节的天球示意图中,我们已经标注出来了黄道,就是假定地球不动,太阳绕地球运动所形成的面,就是黄道。因为地球自转和地球公转是有一定角度偏差的。所以黄道和赤道是有一个23.5度的夹角的。

黄道带(或是黄道十二宫)的概念起源于巴比伦占星术,巴比伦人注意到了与太阳同时升起的星星,在黎明之前,可以观察到靠近太阳位置的星星升起,这些星星以一个似乎规则的圆周来回运动。他们将这些星星分为十二组,并给其命名。希腊人从巴比伦人那里继承了这一习惯,才有了黄道十二宫,这个词来源于希腊文zodion,意为“小动物”。

具体的黄道带的概念如下

黄道带(希腊语:ζῳδιακός, zōdiakos),是天文学的名词,指的是在黄道上的星座组成的环带,不仅是太阳每年在天球上所行经的路径,月球和行星的路径也大略都在黄道的附近,因此也全部都在黄道带的星座内。 在占星术,黄道带被人为划分为十二个随中气点移动(与实际星座位置不一致)的均等区域,各自都有符号。 因此,黄道带是一个天球坐标系统,或是更具体的说是一个黄道坐标系统,以黄道做为纬度的基准平面(原点),并且以太阳在春分时的位置作为经度的原点

黄道12宫的顺序如下

白羊宫(Aries, ♈︎);
金牛宫(Taurus, ♉︎);
双子宫(Gemini, ♊︎);
巨蟹宫(Cancer, ♋︎);
狮子宫(Leo, ♌︎);
处女宫(Virgo, ♍︎);
天秤宫(Libra, ♎︎);
天蝎宫(Scorpio, ♏︎);
射手宫(Sagittarius, ♐︎);
摩羯宫(Capricornus, ♑︎);
水瓶宫(Aquarius, ♒︎);
双鱼宫(Pisces, ♓︎)。

古人观察星空,发现天体分作两类:一类固定在天球上,组成各个星座,形成一幅永恒的天空背景,称之为恒星;另一类天体在黄道附近运行,不断穿过黄道上的十二个星座(或中国的二十八宿),称之为行星。这些行星包括七颗(七曜),分别是阴阳——太阳和太阴(月球),以及五行——金木水火土五个肉眼可见的经典行星。太阳在黄道上一年运行一圈,太阴在黄道上一个月运行一圈。一定要注意,占星中的行星和实际的行星概念并不一致,这毕竟是古代天文学。由于整体一环为360度,所以每个星座占据了360/12 = 30 度。

阅读全文 »

占星与星座计算 天球

最近因为某些原因,有了一段空余的时间。当你空闲便有了一些奇思妙想。因为很多年前特别喜欢一个关于星座的故事,所以利用这一段空余时间写一个系列文章讲占星中的一个小知识。为了不使文章引起争议,特此声明:本人完全相信星座!不解释。文章的前面系列介绍和占星有关的天文基本概念,后面具体根据人的出生时间,地理位置(经纬度)来计算人的星座。可能读了这一系列的文章,大家可以更好的理解为什么星座归类于“占星学”,这样一个听起来非常正式的名字。另外因为不是撰写论文,本文不会详细列出各类依据。太累!

天球概念

天球示意图

以上图片为完整的天球示意图

和地球概念相对,古代天文学家提出了天球的概念。天球是一个想想的球体,他也是旋转的。理论上具有无限大的半径且和地心是同心的。如图所示:和地球有赤道和南北极的概念对应,天球也有对应的天球赤道和天球南北极。

古代天文学家认为我们和星体是等距离的,尽管这并不正确。因为古代缺乏有效的天文观测手段。星球不是靠我们肉眼就可以判断出距离的。古代科学家克服了这种距离上的因素,使用角度来确定星球在天空中的位置。在当时观测条件受限的情况下,不失为一种很有效的天文抽象系统。

地球会围绕地轴自转,相对应于地球不动的参照系下。天球也会围着天极旋转,24小时不停。这就形成了古代天文学家的眼中的昼夜交替原理。太阳,卫星,形星都是东升西落的,这种称为天球的周日实视运动。地球因为有其公转,一颗横行总是比前一天提前(约4min)上升。

和地球对应的概念一样,天球也有自己的南北半球,对应也有南北回归线,南北极

北回归线:北回归线是太阳在北半球能够垂直射到的离赤道最远的位置点,将该点之纬度线便叫“北回归线”,在公元2018年,其位置约在北纬23°26′11.3″ (或 23.43648°)。相反,南纬23°26′11.3″ (或 23.43648°)则为“南回归线”。

阅读全文 »

比特币pow难度验证

何为POW

pow的全称为proof of work,即工作量证明。简单的解释为“做了多少工作”。抛开区块链的背景,pow就是对自己做了多少工作的一种说明:比如做了学习了50个小时的汽车驾驶。而他人很容易验证这个结果:你可能50个小时之后拿到了一本驾照。别人就知道你确实在学习驾驶上使用了50个小时。

Block Header

在区块链的世界里,pow的数据可以体现在区块链的区块头中。当然一般来说,讲解POW的难度离不开挖矿问题。本文因为主要讨论方向的问题,不展开讲挖矿,主要从区块头入手。在阅读下面的内容之前,默认读者已经有了如下前置知识

  1. 区块链常识
  2. 比特币基本概念
  3. 挖矿基本概念

抛开前置知识之后,我们来看区块头的数据结构。

https://en.bitcoin.it/wiki/Protocol_documentation#Block_Headers

可以直接参考以上链接,当然可以可以直接查看比特币的源码,我们现在把数据列出来。

1
2
3
4
5
6
7
8
struct header_structure {      // BYTES   NAME
uint32_t nVersion; // 4 version
uint8_t hashPrevBlock[32]; // 32 previous block header hash
uint8_t hashMerkleRoot[32]; // 32 merkle root hash
uint32_t nTime; // 4 time
uint32_t nBits; // 4 target
uint32_t nNonce; // 4 nonce
};
阅读全文 »

拜占庭将军问题简述

拜占庭的简述

本文将用于介绍著名的拜占庭将军问题。在介绍拜占庭将军问题之前,先简单说一下拜占庭。拜占庭帝国,在古代西欧也被称之为东罗马帝国。是一个位于欧亚交界处的封建君主制国家,其领土包括现在的欧洲南部,西亚和北非。公元四世纪左右,罗马帝国开始分列为罗马东部和罗马西部,这两个国家都被视为罗马正统,大概到公元十世纪,罗马西部陷落,随着神圣罗马帝国的建立,罗马东部失去了罗马这个单词的独占权,开始被称之为东罗马帝国。大概在16世纪之后,开始出现了拜占庭帝国的说法。值得一提的是,拜占庭帝国的首府君士坦丁堡被第四次十字军东征攻陷,从此一蹶不振。1453年5月29日,君士坦丁堡被强大的奥斯曼帝国攻陷,末代皇帝君士坦丁十一世战死。东罗马帝国就此终结。当然,此文不是介绍东罗马帝国历史的文章,在此仅简述拜占庭帝国的历史。

拜占庭将军问题是什么

拜占庭将军问题和拜占庭的历史其实并无关联,拜占庭将军问题也不是历史上真实存在的问题,他是著名计算机大神兰伯特于1982年提出的。拜占庭问题描述的是如下的场景

假设有一座城堡,拜占庭帝国想攻陷这座城堡。所以派出了很多支军队,因为通讯条件很落后,军队和军队之间必须经过信使来传递作战命令。城堡十分坚固,可以抵御一两只军队的进攻。只有所有军队同时进攻,才可以攻陷城堡。为了保证作战命令的统一,提出一个办法,投票。超过半数投票决定作战命令。比如:决定明天早上进攻,如果有半数的军队同意这个作战计划。则在明日早晨开始一起进攻。反之,如果一大半人都不同意明天早上进攻,则明早就不进攻。

现在存在的问题是:军队中有叛徒。叛徒会随意调整作战命令

我们现在用图一来表示这种困境,在图示中,我们用黄色的小人代表正常的部队,粉色的小人代表判叛徒。A表示进攻Attack,R代表拒绝Reject。因为正直的的部队会忠实的执行命令,所以我们把A和R标记在他们身上。

如图所示,身上标记A的三位将军表示他们决定“明早发动进攻”,身上标记R的三位将军决定明早不应该发动进攻,现在叛徒至关重要。叛徒的这一票将决定明早是否决定进攻。这时候叛徒给三位决定进攻的将军传递的消息是明早发动进攻,给三位不进攻的将军发送的消息是明早不发动进攻。所以三位决定进攻的将军知道了,有4票发动进攻的,那么大多数人决定进攻那我明早就发动攻势。三位不进攻的将军则知道,有四票不发动进攻的,那我明早不进攻。所以拜占庭难度就产生了,这些部队有的进攻有的撤退,战斗就失败了。

阅读全文 »

之前已经在x86的mac电脑上编译过substrate,按照官方指南上的操作就可以正常编译。但是在新款m1电脑上并没有编译通过,现在重新尝试在m1上编译substrate。

主要的准备过程参考如下文章

https://zhuanlan.zhihu.com/p/337224781

不过参考文章写于2020年12月16日,到现在(2021年3月10日)有部分状况已经发生了变化。针对和文章中不一样的状况稍作说明。

RUST

rust环境现在可以直接支持m1。所以使用rustup脚本可以直接安装rust,不需要额外设置。安装完成之后使用

rustup show

查看toolchain。则会发现是以aarch64开头的,原来的x86下面的tool-chain是

stable-x86_64-apple-darwin (default)

注意差别。

阅读全文 »

Sqlite自增字段

起因:在使用数据库存储从区块链网络上取来的block_header时,block_header本身并不带自身的高度信息。不过取来的数据是经过筛选的,按照数据库存储的顺序就可以代表blockheader的高度。所以在数据库中增加一个id primiry key autoincrement 的自增主键。每次从数据库查询时获取id值来代表区块的高度。但后来重构,id被TEXT类型的逐渐替代,所以这个功能无法正常实现。所以产生以下疑问

在已经有TEXT类型的主键后,sqlite可以拥有别的自增字段吗?

不可以!在sqlite的文档FAQ中第一个问题(文末给出参考链接)就是关于如何设置自增字段的。在sqlite中自增约束AUTOINCREMENT只可以跟在PRIMARY KEY后面。把AUTOINCREMENT放在主键以外的地方是不可以的。或者再明确一点,要想在sqlite中拥有一个自增字段必须这样写

1
id INTEGER PRIMARY KEY AUTOINCREMENT,

要求id的类型必须是INTEGER。每次插入数据库的时候,不要插入id的数据,数据库会自动为我们的主键id实现自增。之前提到的情况:id已经是TEXT PRIMARY KEY的状态下,无法再拥有另一个自增字段了。

题外:在sqlite以外的数据库中是可以的。以mysql为例,mysql的AUTOINCREMENT是可以加在主键之外的地方的。一个表中,只允许有一个自增字段,而且在mysql中需要给主键以外的字段实现自增,必须给该字段加上unique约束。

注意点:

  1. 在设定id INTEGER PRIMARY KEY,不加AUTOINCREMENT,只要不指定插入id的数据,id字段也可以实现自增。

在已经有TEXT类型的主键后,一定要有一个自增的字段来记录当前所在的行数怎么办?

阅读全文 »

多线程原语CondVar

多线程下的原语,除了我们常用的锁,还有另外一类用于同步的原语叫做“屏障”,“条件变量”(在rust或者cpp中)。在其他语言中也有类似的概念,叫做栅栏,闭锁,屏障,信号量等。他们具有相同的意义。

在介绍条件变量之前,先介绍屏障(Barrier)。屏障相当于一堵带门的墙,使用wait方法,在某个点阻塞全部进入临界区的线程。条件变量(Condition Variable)和屏障的语义类似,但它不是阻塞全部线程,而是在满足某些特定条件之前阻塞某一个得到互斥锁的线程。

单纯讲条件变量的意义并不直观。换种描述

条件变量可以在我们达到某种条件之前阻塞线程,我们利用此特性可以对线程进行同步。或者说做到按照某种条件,在多个线程中达到按照特定顺序执行的目的。

为此我们设计如下下面流程。为此流程写一段代码,来体会条件变量的作用

我们启动三个线程,t1,t2,t3。分别执行任务T1,T2,T3。现在要求:T2必须等待T1和T3完成之后再执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use parking_lot::{Mutex, Condvar};
use std::sync::Arc;
use std::thread;
use std::thread::sleep;
use std::time::Duration;


pub fn main() {
let pair = Arc::new((Mutex::new(0),
Condvar::new()));
let pair2 = pair.clone();
let pair3 = pair.clone();


let t1 = thread::Builder::new()
.name("T1".to_string())
.spawn(move ||
{
sleep(Duration::from_secs(4));
println!("I'm working in T1, step 1");
let &(ref lock, ref cvar) = &*pair2;
let mut started = lock.lock();
*started += 2;
cvar.notify_one();
}
)
.unwrap();

let t2 = thread::Builder::new()
.name("T2".to_string())
.spawn(move ||
{
println!("I'm working in T2, start");
let &(ref lock, ref cvar) = &*pair;
let mut notify = lock.lock();

while *notify < 5 {
cvar.wait(&mut notify);
}
println!("I'm working in T2, final");
}
)
.unwrap();

let t3 = thread::Builder::new()
.name("T3".to_string())
.spawn(move ||
{
sleep(Duration::from_secs(3));
println!("I'm working in T3, step 2");
let &(ref lock, ref cvar) = &*pair3;
let mut started = lock.lock();
*started += 3;
cvar.notify_one();
}
)
.unwrap();

t1.join().unwrap();
t2.join().unwrap();
t3.join().unwrap();
}

以上代码可以在 这个链接 下在playground运行。

上面的代码需要注意的点如下

阅读全文 »