#每日一问# 马宝强为什么没声音了,丁真却越来越火?

回复

总结zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 3419 次浏览 • 2020-12-08 11:29 • 来自相关话题

#每日一问# 滴滴为什么要做青菜拼车、花小猪拼车?

回复

总结zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2690 次浏览 • 2020-12-08 11:13 • 来自相关话题

#每日一问# 为什么拼多多没有购物车?

回复

总结zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 3484 次浏览 • 2020-12-08 11:09 • 来自相关话题

#面试题集锦#实现单链表反转

面试zkbhj 发表了文章 • 0 个评论 • 2476 次浏览 • 2020-08-14 17:16 • 来自相关话题

反转一个链表

示例:输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
结构体定义struct ListNode {
int val;
struct ListNode *next;
};
 





 思路一

先对原链表做头删操作,再对新链表做头插
定义一个新head头指针,标记为newHead,将它初始为NULL,并非指向NULL,最后我们选择返回这个newHead指针作为新链表的头指针。
定义一个结点node作为"临时中转站",初始化与否并无大影响。进行循环遍历链表各个结点,判定head指针是否为空,即是否到达了原链表的结尾。如果不为空,说明还没有到达尾部。如果程序第一次运行就没有进入循环,说明传入了一个空链表,此时返回newHead新链表的头指针,同样返回NULL,这样处理也是合理的。以下开始逆序链表逻辑:在当前指针不为NULL时,先对原链表做头删操作,再对新链表做头插操作。即使用循环进行操作:让node指针指向传入函数链表的头指针head,两指针指向保持相同。然后让head指针指向它的next结点,此时旧链表已经完成了头删操作。第一个结点已经被"切割"下来。接下来要做的就是对新链表进行头插操作,使结点放入新链表。让node指针的next下一个结点指向新链表的头指针newHead,完成结点的链接,即头插入新的链表中。然后更新newHead指向为新链表的头结点。进行下一次循环。最终head指针指向了原链表的结尾,即为NULL,退出循环,此时新链表已经反转完毕,情况如图:最终返回新链表头指针newHead即可。





 struct ListNode *reverseList(struct ListNode* head) {
struct ListNode *newHead = NULL;
struct ListNode *node;
while (head != NULL) {
//1. 对之前的链表做头删
node = head;
head = head->next;

//2. 对新链表做头插
node->next = newHead;
newHead = node;
}
return newHead;
}
思路二





利用选择语句,找到空链表的情况,此情况返回NULL空指针,因为空链表不能反转,或者说反转之后还是一个空链表,返回空。利用三个指针p0"前指针"、p1“当前指针”、p2"后指针"来分批处理链表元素,p0置为NULL,它将作为链表的尾结点向前推进处理,p1指向旧链表的头指针head,p2指向旧链表的头指针的next结点。开始遍历链表,循环判定因子为p1,当它为空时到达链表尾部跳出循环。否则在表中执行循环内逻辑:将p1指向的当前结点的下一个结点指向p0,即前一个结点。此时p0为NULL,那么p1的下一个结点就为空了,它现在是最后一个结点。然后将p0指针指向p1,将p1指针指向p2,注意这两步不可以调换顺序,否则正确向后挪移一位。此时完成了三个指针的一轮更迭。判定p2指针是否为空,如果为空说明此时p2到达了链表结尾,当前指针p1的指向为最后一个结点,它的next即为空。如果不为空,将p2更新到下一个结点,进行下一次循环。下一次进行循环时,就会把截断结点链接到新链表的头部,同时更新三个指针。继续循环。循环终止条件为:p1指向了链表尾部的NULL,此时p1的前指针p0即指向了反转后的链表,它就是新链表的head头指针。此时返回p0即可。





 struct ListNode *reverseList(struct ListNode* head) {
if (head == NULL) {
return NULL;
}
struct ListNode *p0 = NULL;
struct ListNode *p1 = head;
struct ListNode *p2 = head->next;
while (p1 != NULL) {
p1->next = p0;

p0 = p1;
p1 = p2;
if (p2 != NULL) {
p2 = p2->next;
}
}
return p0;
}
参考文档:
https://blog.csdn.net/qq_42351880/article/details/88637387
 
  查看全部
反转一个链表

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

结构体定义
struct ListNode {
int val;
struct ListNode *next;
};

 

QQ截图20200814170600.jpg

 思路一

先对原链表做头删操作,再对新链表做头插
  1. 定义一个新head头指针,标记为newHead,将它初始为NULL,并非指向NULL,最后我们选择返回这个newHead指针作为新链表的头指针。

  1. 定义一个结点node作为"临时中转站",初始化与否并无大影响。
  2. 进行循环遍历链表各个结点,判定head指针是否为空,即是否到达了原链表的结尾。如果不为空,说明还没有到达尾部。如果程序第一次运行就没有进入循环,说明传入了一个空链表,此时返回newHead新链表的头指针,同样返回NULL,这样处理也是合理的。
  3. 以下开始逆序链表逻辑:在当前指针不为NULL时,先对原链表做头删操作,再对新链表做头插操作。即使用循环进行操作:
  4. 让node指针指向传入函数链表的头指针head,两指针指向保持相同。
  5. 然后让head指针指向它的next结点,此时旧链表已经完成了头删操作。第一个结点已经被"切割"下来。接下来要做的就是对新链表进行头插操作,使结点放入新链表。
  6. 让node指针的next下一个结点指向新链表的头指针newHead,完成结点的链接,即头插入新的链表中。然后更新newHead指向为新链表的头结点。进行下一次循环。
  7. 最终head指针指向了原链表的结尾,即为NULL,退出循环,此时新链表已经反转完毕,情况如图:
  8. 最终返回新链表头指针newHead即可。


QQ截图20200814170610.jpg

 
struct ListNode *reverseList(struct ListNode* head) {
struct ListNode *newHead = NULL;
struct ListNode *node;
while (head != NULL) {
//1. 对之前的链表做头删
node = head;
head = head->next;

//2. 对新链表做头插
node->next = newHead;
newHead = node;
}
return newHead;
}

思路二

QQ截图20200814171211.jpg

  1. 利用选择语句,找到空链表的情况,此情况返回NULL空指针,因为空链表不能反转,或者说反转之后还是一个空链表,返回空。
  2. 利用三个指针p0"前指针"、p1“当前指针”、p2"后指针"来分批处理链表元素,p0置为NULL,它将作为链表的尾结点向前推进处理,p1指向旧链表的头指针head,p2指向旧链表的头指针的next结点。
  3. 开始遍历链表,循环判定因子为p1,当它为空时到达链表尾部跳出循环。否则在表中执行循环内逻辑:将p1指向的当前结点的下一个结点指向p0,即前一个结点。此时p0为NULL,那么p1的下一个结点就为空了,它现在是最后一个结点。
  4. 然后将p0指针指向p1,将p1指针指向p2,注意这两步不可以调换顺序,否则正确向后挪移一位。此时完成了三个指针的一轮更迭。
  5. 判定p2指针是否为空,如果为空说明此时p2到达了链表结尾,当前指针p1的指向为最后一个结点,它的next即为空。如果不为空,将p2更新到下一个结点,进行下一次循环。
  6. 下一次进行循环时,就会把截断结点链接到新链表的头部,同时更新三个指针。继续循环。
  7. 循环终止条件为:p1指向了链表尾部的NULL,此时p1的前指针p0即指向了反转后的链表,它就是新链表的head头指针。此时返回p0即可。


QQ截图20200814171221.jpg

 
struct ListNode *reverseList(struct ListNode* head) {
if (head == NULL) {
return NULL;
}
struct ListNode *p0 = NULL;
struct ListNode *p1 = head;
struct ListNode *p2 = head->next;
while (p1 != NULL) {
p1->next = p0;

p0 = p1;
p1 = p2;
if (p2 != NULL) {
p2 = p2->next;
}
}
return p0;
}

参考文档:
https://blog.csdn.net/qq_42351880/article/details/88637387
 
 

#每日精进#2020年8月6日

总结zkbhj 发表了文章 • 0 个评论 • 2449 次浏览 • 2020-08-06 09:31 • 来自相关话题

【早读:《深入理解计算机系统》】

第二章 信息的表示和处理

表示代码int sum(int x, int y) {
return x + y;
}当我们在不同的机器上编译上面的程序得到的机器代码都不尽相同:

Linux32     55 89 e5 8b 45 0c 03 45 08 c9 c3
Windows  55 89 e5 8b 45 0c 03 45 08 5d c3
Sun           81 c3 e0 08 90 03 00 09


因此,二进制代码是不兼容的,无法在不同机器之间移植。

这得到一个计算机系统的基本概念:从机器的角度来看,程序仅仅只是字节序列,机器没有关于原始程序的任何信息。

布尔代数简介

计算机的核心都是围绕1和0来演化的。对于0和1的起源,要追溯到1850年前后乔治·布尔的工作,所以这个也叫布尔代数。即通过将逻辑值TRUE和FALSE编码为二进制1和0设计出的一种代数,以研究逻辑推理的基本原则。






上面分别列出了~(NOT)、&(AND)、|(OR)和^(EXCLUSIVE-OR)四种基本运算。

后来创立信息论领域的Claude Shannon首先建立了布尔代数和数字逻辑之间的联系。

将上述基础的布尔运算扩展到位向量运算。位向量就是固定长度为w、由0和1组成的串。

假设 w=4,a=[0110],b=[1100]。那么四种运算 a&b、a|b、a^b、~b 结果分别如下:





 
布尔运算&对|满足分配率:a&(b|c) = (a&b)|(a&c);反过来,|也满足对&的分配率,即:a|(b&c) = (a|b) & (a|c)。

位向量的一个有用应用就是表示有限集合,即用位向量来给集合进行编码。

C语言的一个有用特性就是支持按位布尔运算。|、&、~、^这些运算可以用到任何“整型”的数据类型上。void inplace_swap(int *x, int *y){
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}
上面这段代码,就是利用了两个事实来实现*x和*y所指向的变量值进行了交换操作。两个事实分别是:

异或运算是可交换和可结合的;

对于任意的a, a ^ a = 0;

所以上述程序的计算过程如下:

初始:*x = a    *y = b
第一步:*x = a   *y = a ^ b
第二步:*x = a ^ (a ^ b) = (a ^ a) ^ b = b   *y = a ^ b
第三部:*x = b   *y = b ^ ( a ^ b) = (b ^ b) ^ a = a


但是注意,这种方式和通常的交换两个数值的技术不一样,当移动一个值时,我们不需要第三个位置来临时存放另外一个值。这种交换方式并没有性能上的优势,它仅仅是一个智力游戏!

位级运算常见的用法就是实现掩码运算:掩码是一个位模式,表示从一个字中选出的位的集合。

比如对于掩码0xFF(最低的8位都是1)表示一个字的低位字节。x&0xFF会得到一个由x的最低有效字节组成的值。 
 
 
【英文中几点钟的说法o'clock是什么的缩写?】

o'clock = of the clock.

在14世纪以前,人类还没有发明出来时钟,都是通过一些其他途径来获取和感知时间,比如日晷、沙漏等。直到 14 世纪,现代意义上的时钟雏形才得以发明。当时的时钟会自己报时“说出”:It's 7 of the clock!后来,随着时钟的普及和大众化,人们开始将 of 的 f 和 the 一带而过,简略地读成了 o'clock。
 https://ask.zkbhj.com/?/article/370 查看全部

【早读:《深入理解计算机系统》】

第二章 信息的表示和处理

表示代码
int sum(int x, int y) {
return x + y;
}
当我们在不同的机器上编译上面的程序得到的机器代码都不尽相同:


Linux32     55 89 e5 8b 45 0c 03 45 08 c9 c3
Windows  55 89 e5 8b 45 0c 03 45 08 5d c3
Sun           81 c3 e0 08 90 03 00 09



因此,二进制代码是不兼容的,无法在不同机器之间移植。

这得到一个计算机系统的基本概念:从机器的角度来看,程序仅仅只是字节序列,机器没有关于原始程序的任何信息。

布尔代数简介

计算机的核心都是围绕1和0来演化的。对于0和1的起源,要追溯到1850年前后乔治·布尔的工作,所以这个也叫布尔代数。即通过将逻辑值TRUE和FALSE编码为二进制1和0设计出的一种代数,以研究逻辑推理的基本原则。

20200806092721.jpg


上面分别列出了~(NOT)、&(AND)、|(OR)和^(EXCLUSIVE-OR)四种基本运算。

后来创立信息论领域的Claude Shannon首先建立了布尔代数和数字逻辑之间的联系。

将上述基础的布尔运算扩展到位向量运算。位向量就是固定长度为w、由0和1组成的串。

假设 w=4,a=[0110],b=[1100]。那么四种运算 a&b、a|b、a^b、~b 结果分别如下:

20200806092735.jpg

 
布尔运算&对|满足分配率:a&(b|c) = (a&b)|(a&c);反过来,|也满足对&的分配率,即:a|(b&c) = (a|b) & (a|c)。

位向量的一个有用应用就是表示有限集合,即用位向量来给集合进行编码。

C语言的一个有用特性就是支持按位布尔运算。|、&、~、^这些运算可以用到任何“整型”的数据类型上。
void inplace_swap(int *x, int *y){
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}

上面这段代码,就是利用了两个事实来实现*x和*y所指向的变量值进行了交换操作。两个事实分别是:

异或运算是可交换和可结合的;

对于任意的a, a ^ a = 0;

所以上述程序的计算过程如下:


初始:*x = a    *y = b
第一步:*x = a   *y = a ^ b
第二步:*x = a ^ (a ^ b) = (a ^ a) ^ b = b   *y = a ^ b
第三部:*x = b   *y = b ^ ( a ^ b) = (b ^ b) ^ a = a



但是注意,这种方式和通常的交换两个数值的技术不一样,当移动一个值时,我们不需要第三个位置来临时存放另外一个值。这种交换方式并没有性能上的优势,它仅仅是一个智力游戏!

位级运算常见的用法就是实现掩码运算:掩码是一个位模式,表示从一个字中选出的位的集合。

比如对于掩码0xFF(最低的8位都是1)表示一个字的低位字节。x&0xFF会得到一个由x的最低有效字节组成的值。 
 
 
英文中几点钟的说法o'clock是什么的缩写?】


o'clock = of the clock.


在14世纪以前,人类还没有发明出来时钟,都是通过一些其他途径来获取和感知时间,比如日晷、沙漏等。直到 14 世纪,现代意义上的时钟雏形才得以发明。当时的时钟会自己报时“说出”:It's 7 of the clock!后来,随着时钟的普及和大众化,人们开始将 of 的 f 和 the 一带而过,简略地读成了 o'clock。
 https://ask.zkbhj.com/?/article/370

#每日精进#2020年8月5日

总结zkbhj 发表了文章 • 0 个评论 • 2640 次浏览 • 2020-08-05 11:20 • 来自相关话题

【早读:《深入理解计算机系统》】

第二章 信息的表示和处理

寻址和字节顺序

在几乎所有机器上,多字节对象被存储为连续的字节序列,对象的地址为最小的地址。

排列表示一个对象的字节有两个通用的规则:

小端法:在内存中按照从最低有效字节到最高有效字节的顺序存储对象;
大端法:在内存中按照从最高有效字节到最低有效字节的顺序存储对象;一旦选择了特定的操作系统,那么字节顺序也就固定下来。Android和IOS只能够运行于小端模式下。

两种方式没有谁好谁坏之分,对于那种字节排序的选择都是任意的。
大小端的说法来自于Jonathan Swift的《格利弗游记》一书,其中交战的两个派别无法就打开一个半熟的鸡蛋应该从哪一端打开达成一致意见。
不同的端模式下,会有以下影响:
1、通过网络在不同端模式的机器间传递数据时,发送和接收时,需要转换网络标准;
2、在检查机器级程序时,对数据的解读方式;
3、在编写规避正常的类型系统的程序时。​#include <stdio.h>

typedef unsigned char *byte_pointer;
//typedef char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%p\t0x%.2x\n", &start, start);
printf("\n");
}

void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}

void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}

void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}

​在这段程序中,“byte_pointer start”告诉编译器,应该把这个指针看成指向一个字节序列,而不是指向一个原始数据类型的对象。然后,这个指针会被看成是对象使用的最低字节地址。
这种强制类型转换不会改变真实的指针,它们只是告诉编译器以新的数据类型来看待被指向的数据。
使用ASCII码作为字符码的任何系统上都是将得到相同的结果,与字节顺序和字节大小规则无关。因而,文本数据比二进制数据具有更强的平台独立性。
 
【HTML页面里怎么实现代码包含?】
https://ask.zkbhj.com/?/question/387
 
【Yii2框架中如何区分不同的场景指定赋值字段和检验规则】
 
场景(scenario)

分析上面问题,会发现关键点是批量赋值(massive assignment)和数据校验(validate)两个方法。如果对不同的场景指定赋值字段和检验规则,问题就迎刃而解。

Yii中的scenario有 安全属性 和 活跃属性 两个概念。安全属性用在批量赋值的load方法,只有安全属性才能被赋值;活跃属性用在规则校验的validate方法,在活跃属性集中并且定义了校验规则的属性才会被校验。活跃属性和安全属性的关系是,安全属性是活跃属性的子集。

\yii\base\Model类定义了默认场景:SCENARIO_DEFAULT(值为default)。默认场景下,出现在rules方法中的属性既是活跃属性,又是安全属性(这句话基本正确,看后续解释)。为不同场景指定活跃属性、安全属性以及校验器,可以通过覆盖senarios或rules两个方法实现(几乎每个Model类都会重写rules方法,senarios用得少)。

rules

先看rules方法。默认的属性加校验器定义方式,让每个属性既是安全属性,也是活跃属性。如果想让某个属性不是安全属性(不能通过load批量赋值),在属性名前加感叹号!即可。例如student中的user_id字段:public function rules()
{
return [
["!user_od", "required"],
["!user_id", "integer"],
["!user_od", "unique"],
// other rules
];
}user_id是活跃属性,在写入数据库时会被校验。但它不是安全属性,不能通过load方法进行赋值,解决了安全隐患。

再看rules方法按场景区分校验器规则的做法:定义校验器时on属性指定规则在哪些场景下生效,except属性则排除一些场景(如果不指定on和except,规则对所有场景生效)。例如:public function rules()
{
return [
["password", "string", "length" => [8, 16], "on" => ["signup"]], // 仅在signup场景时才被验证
["status", "integer", "except" => ["signup"], // 除了signup场景,其他情况都校验
// other rules
];
}在原来基础上新增感叹号和on/except属性,非常简便的就定义了非安全属性以及分场景指定校验规则。

scenarios

另外一种更清晰定义安全属性和活跃属性的做法是重写scenarios方法。scenarios方法返回一个数组,数组的键是场景名称,值是活跃属性集合(包饭安全属性)。例如student表的可能实现如下:public function scenarios()
{
return [
self::SCENARIO_DEFAULT => ["!user_id", "grade", "class", xxxx],
"update" => ["grade", "class", xxxx],
];
}默认情形下(学生报名),年级、班级这些信息是安全属性,但user_id不是,只能在程序内部赋值,并在插入数据时被校验;在修改信息时,user_id不是活跃属性,既不能被批量赋值,也不需要校验(事实上它不应该改变)。

scenarios方法只能定义活跃属性和安全属性,无法定义校验规则,需要和rules配合使用。
https://www.cnblogs.com/yangxunwu1992/p/6669380.html

 
【关于未来几年国内经济形势的分析总结】】
 
疫情发生以来,国内外经济形势发生了重大变化。国际局势也从全球化走向了逆全球化的道路,美国通过各种手段(贸易战、“中国病毒”论、打压华为、打压Tik Tok、关闭领事馆、干涉中国内政等)不断打压中国,各大企业也在不断将企业撤出中国。
 
最近最热门的经济词汇就是经济内循环,意思是说,以后要以国内市场为主,国内生产出来的东西,主要在自己国内消化掉,重新转化为生产力。也就是走内需拉动经济增长的路子。这是在疫情发生以及国内外形式发生如此变化之后的无奈之举。
 
我们过去拉动经济的三驾马车是外贸,基建和房地产,内需一向很薄弱,这几年内需份额有所提高,但还不足以成为拉动经济增长的主要动力。
 
最主要体现在居民收入不足,杠杆过高,中产被绑在房地产的战车上面等。
 
从微观层面看,中国消费结构呈现两边高中间低的M字型结构(健康的结构应该是相反的,两边低中间高,中产成为消费的主力)。所以中国目前的情况就是有钱人并不会受到高房价的影响而降低消费,反而还因此提高了消费能力,还在消费升级。然后相对经济收入低的人群消费能力本就低,地摊经济很火就说明这个问题。中产,由于购房等压力,从中端消费跌落到低端消费。
 
所以,国内要实现以经济内循环为主,国际循环为辅的国内国际双循环的新发展格局,有以下几种办法:
1、提高居民购买力
主要就是降房价和提高居民收入,提高居民收入很难,现在经济太差,没有需求就没有工作岗位,居民收入不下降已经很不错,但房价是有可能缓慢下降的

2、发展房地产。
过去十几年就是这么干的,但现在房价太高,已经到了伤害经济的底部,已经不能继续走拉抬房价发展经济的老路。

3、让股市走出慢牛。
目前看也相对可行,上层也一直在强调要通过资本市场服务实体经济,这也是我一向看好A股走牛的重要原因。
 
4、发展高科技和人民币国际化。
 
讲通俗一点,就是:

经济内循环其实就是要过苦日子的另一个代名词。也可以简单理解为好好干活,但不要想着能挣很多钱。在未来很长一段时间里面,物价会相对比较便宜。

 
https://zhuanlan.zhihu.com/p/165347415
 
【Go语言核心36讲:第12节 使用函数的正确姿势(1)】
 
在 Go 语言中,函数可是一等的(first-class)公民,函数类型也是一等的数据类型(函数类型属于引用类型,它的零值为nil)。
这意味着函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等。“函数是一等的公民”是函数式编程(functional programming)的重要特征。Go 语言在语言层面支持了函数式编程。

package main

import "fmt"

type Printer func(contents string) (n int, err error)

func printToStd(contents string) (bytesNum int, err error) {
return fmt.Println(contents)
}

func main() {
var p Printer
p = printToStd
p("something")
}函数的签名其实就是函数的参数列表和结果列表的统称,它定义了可用来鉴别不同函数的那些特征,同时也定义了我们与函数交互的方式。

各个参数和结果的名称不能算作函数签名的一部分,对于结果声明,名字也可以没有。且函数的名称也不算函数签名的一部分,只是个标识符而已。

高阶函数
 

1. 接受其他的函数作为参数传入;
2. 把其他的函数作为结果返回。

只要满足了其中任意一个特点,我们就可以说这个函数是一个高阶函数。
 
卫述语句
 
卫述语句是指被用来检查关键的先决条件的合法性,并在检查未通过的情况下立即终止当前代码块执行的语句。在 Go 语言中,if 语句常被作为卫述语句。
if op == nil {
return 0, errors.New("invalid operation")
} 查看全部
【早读:《深入理解计算机系统》】

第二章 信息的表示和处理

寻址和字节顺序

在几乎所有机器上,多字节对象被存储为连续的字节序列,对象的地址为最小的地址。

排列表示一个对象的字节有两个通用的规则:


小端法:在内存中按照从最低有效字节到最高有效字节的顺序存储对象;
大端法:在内存中按照从最高有效字节到最低有效字节的顺序存储对象;一旦选择了特定的操作系统,那么字节顺序也就固定下来。Android和IOS只能够运行于小端模式下。


两种方式没有谁好谁坏之分,对于那种字节排序的选择都是任意的。
大小端的说法来自于Jonathan Swift的《格利弗游记》一书,其中交战的两个派别无法就打开一个半熟的鸡蛋应该从哪一端打开达成一致意见。
不同的端模式下,会有以下影响:
1、通过网络在不同端模式的机器间传递数据时,发送和接收时,需要转换网络标准;
2、在检查机器级程序时,对数据的解读方式;
3、在编写规避正常的类型系统的程序时。
​#include <stdio.h>

typedef unsigned char *byte_pointer;
//typedef char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%p\t0x%.2x\n", &start, start);
printf("\n");
}

void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}

void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}

void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}

​在这段程序中,“byte_pointer start”告诉编译器,应该把这个指针看成指向一个字节序列,而不是指向一个原始数据类型的对象。然后,这个指针会被看成是对象使用的最低字节地址。
这种强制类型转换不会改变真实的指针,它们只是告诉编译器以新的数据类型来看待被指向的数据。
使用ASCII码作为字符码的任何系统上都是将得到相同的结果,与字节顺序和字节大小规则无关。因而,文本数据比二进制数据具有更强的平台独立性。
 
【HTML页面里怎么实现代码包含?】
https://ask.zkbhj.com/?/question/387
 
【Yii2框架中如何区分不同的场景指定赋值字段和检验规则】
 
场景(scenario)

分析上面问题,会发现关键点是批量赋值(massive assignment)和数据校验(validate)两个方法。如果对不同的场景指定赋值字段和检验规则,问题就迎刃而解。

Yii中的scenario有 安全属性 和 活跃属性 两个概念。安全属性用在批量赋值的load方法,只有安全属性才能被赋值;活跃属性用在规则校验的validate方法,在活跃属性集中并且定义了校验规则的属性才会被校验。活跃属性和安全属性的关系是,安全属性是活跃属性的子集。

\yii\base\Model类定义了默认场景:SCENARIO_DEFAULT(值为default)。默认场景下,出现在rules方法中的属性既是活跃属性,又是安全属性(这句话基本正确,看后续解释)。为不同场景指定活跃属性、安全属性以及校验器,可以通过覆盖senarios或rules两个方法实现(几乎每个Model类都会重写rules方法,senarios用得少)。

rules

先看rules方法。默认的属性加校验器定义方式,让每个属性既是安全属性,也是活跃属性。如果想让某个属性不是安全属性(不能通过load批量赋值),在属性名前加感叹号!即可。例如student中的user_id字段:
public function rules()
{
return [
["!user_od", "required"],
["!user_id", "integer"],
["!user_od", "unique"],
// other rules
];
}
user_id是活跃属性,在写入数据库时会被校验。但它不是安全属性,不能通过load方法进行赋值,解决了安全隐患。

再看rules方法按场景区分校验器规则的做法:定义校验器时on属性指定规则在哪些场景下生效,except属性则排除一些场景(如果不指定on和except,规则对所有场景生效)。例如:
public function rules()
{
return [
["password", "string", "length" => [8, 16], "on" => ["signup"]], // 仅在signup场景时才被验证
["status", "integer", "except" => ["signup"], // 除了signup场景,其他情况都校验
// other rules
];
}
在原来基础上新增感叹号和on/except属性,非常简便的就定义了非安全属性以及分场景指定校验规则。

scenarios

另外一种更清晰定义安全属性和活跃属性的做法是重写scenarios方法。scenarios方法返回一个数组,数组的键是场景名称,值是活跃属性集合(包饭安全属性)。例如student表的可能实现如下:
public function scenarios()
{
return [
self::SCENARIO_DEFAULT => ["!user_id", "grade", "class", xxxx],
"update" => ["grade", "class", xxxx],
];
}
默认情形下(学生报名),年级、班级这些信息是安全属性,但user_id不是,只能在程序内部赋值,并在插入数据时被校验;在修改信息时,user_id不是活跃属性,既不能被批量赋值,也不需要校验(事实上它不应该改变)。

scenarios方法只能定义活跃属性和安全属性,无法定义校验规则,需要和rules配合使用。
https://www.cnblogs.com/yangxunwu1992/p/6669380.html

 
【关于未来几年国内经济形势的分析总结】】
 
疫情发生以来,国内外经济形势发生了重大变化。国际局势也从全球化走向了逆全球化的道路,美国通过各种手段(贸易战、“中国病毒”论、打压华为、打压Tik Tok、关闭领事馆、干涉中国内政等)不断打压中国,各大企业也在不断将企业撤出中国。
 
最近最热门的经济词汇就是经济内循环,意思是说,以后要以国内市场为主,国内生产出来的东西,主要在自己国内消化掉,重新转化为生产力。也就是走内需拉动经济增长的路子。这是在疫情发生以及国内外形式发生如此变化之后的无奈之举。
 
我们过去拉动经济的三驾马车是外贸,基建和房地产,内需一向很薄弱,这几年内需份额有所提高,但还不足以成为拉动经济增长的主要动力。
 
最主要体现在居民收入不足杠杆过高中产被绑在房地产的战车上面等。
 
从微观层面看,中国消费结构呈现两边高中间低的M字型结构(健康的结构应该是相反的,两边低中间高,中产成为消费的主力)。所以中国目前的情况就是有钱人并不会受到高房价的影响而降低消费,反而还因此提高了消费能力,还在消费升级。然后相对经济收入低的人群消费能力本就低,地摊经济很火就说明这个问题。中产,由于购房等压力,从中端消费跌落到低端消费。
 
所以,国内要实现以经济内循环为主,国际循环为辅的国内国际双循环的新发展格局,有以下几种办法:
1、提高居民购买力
主要就是降房价和提高居民收入,提高居民收入很难,现在经济太差,没有需求就没有工作岗位,居民收入不下降已经很不错,但房价是有可能缓慢下降的

2、发展房地产。
过去十几年就是这么干的,但现在房价太高,已经到了伤害经济的底部,已经不能继续走拉抬房价发展经济的老路。

3、让股市走出慢牛。
目前看也相对可行,上层也一直在强调要通过资本市场服务实体经济,这也是我一向看好A股走牛的重要原因。
 
4、发展高科技和人民币国际化。
 
讲通俗一点,就是:


经济内循环其实就是要过苦日子的另一个代名词。也可以简单理解为好好干活,但不要想着能挣很多钱。在未来很长一段时间里面,物价会相对比较便宜。


 
https://zhuanlan.zhihu.com/p/165347415
 
【Go语言核心36讲:第12节 使用函数的正确姿势(1)】
 
在 Go 语言中,函数可是一等的(first-class)公民,函数类型也是一等的数据类型(函数类型属于引用类型,它的零值为nil)。
这意味着函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等。“函数是一等的公民”是函数式编程(functional programming)的重要特征。Go 语言在语言层面支持了函数式编程。

package main

import "fmt"

type Printer func(contents string) (n int, err error)

func printToStd(contents string) (bytesNum int, err error) {
return fmt.Println(contents)
}

func main() {
var p Printer
p = printToStd
p("something")
}
函数的签名其实就是函数的参数列表和结果列表的统称,它定义了可用来鉴别不同函数的那些特征,同时也定义了我们与函数交互的方式。


各个参数和结果的名称不能算作函数签名的一部分,对于结果声明,名字也可以没有。且函数的名称也不算函数签名的一部分,只是个标识符而已。


高阶函数
 


1. 接受其他的函数作为参数传入;
2. 把其他的函数作为结果返回。


只要满足了其中任意一个特点,我们就可以说这个函数是一个高阶函数。
 
卫述语句
 
卫述语句是指被用来检查关键的先决条件的合法性,并在检查未通过的情况下立即终止当前代码块执行的语句。在 Go 语言中,if 语句常被作为卫述语句。
if op == nil { 
return 0, errors.New("invalid operation")
}

#每日精进#2020年8月4日

总结zkbhj 发表了文章 • 0 个评论 • 2666 次浏览 • 2020-08-04 14:51 • 来自相关话题

【早读:《深入理解计算机系统》】
 
第二章 信息的表示和处理

大多数计算机使用8位的块,或字节,作为最小的可寻址内存单位。

机器级程序将内存视为一个非常大的字节数组,称为虚拟内存。

内存中每个字节由一个唯一的数字来标识,称为它的地址。所有可能的地址的集合就称为虚拟地址空间。

所以,这个虚拟地址空间只是一个展现给机器级程序的概念性映像。实际上,它将DRAM、闪存、磁盘存储器等和操作系统软件结合起来,封装了复杂性,为程序提供一个看上去统一的字节数组。


C语言中的一个指针的值,都是某个存储块的第一个字节的虚拟地址。每个程序对象可以简单地视为一个字节块,而程序本身就是一个字节序列。

十六进制表示法

由于二进制和十进制对于描述位模式来说都非常不方便:二进制太冗长,十进制和位模式的互相转化很麻烦,替代的方法就是引入16进制。

以0x或者0X开头,0~9、A~F,不区分大小写且大小写不敏感。

重要的是二进制、十进制和十六进制之间的互相转换方法,详细的可以进入凯冰科技知识共享中心搜索相关问题或文章查看。

对于x=2的n次方这个公式,转换十六进制,可以转化为n=i+4j,然后得到的十六进制就是:0x + 2的i次方 + j个0,比如512,是2的9次方,9=1+4*2,所以十六进制就是0x200。

字数据大小

每台计算机都有一个字长,指明指针数据的标称大小。字长决定虚拟地址空间的最大大小。32位字长限制的虚拟地址空间位4千兆字节(约4GB),而现在比较普及的64位字长的虚拟空间位16EB。

大多数64位机器也可以运行32位机器编译的程序,这是一种向后兼容。//该编译后的程序可以在32或64位机器上运行
linux> gcc -m32 prog.c

//该编译后,只能在64位机器上运行
linux> gcc -m64 prog.c我们将程序称为32位程序或64位程序,区别在于该程序是如何编译的,而不是其运行的机器类型。

ISO C99引入了确定大小的数据类型,int32_t 和int64_t,其数据大小是固定的,分别为4个字节和8个字节。使用确切大小的整数类型是程序员准确控制数据表示的最佳途径。

程序员应该力图使他们的程序可以在不同的机器和编译器上可移植,可移植的一方面就是说程序对不同数据类型的确切大小不敏感。

比如许多程序员假设一个声明为int类型的程序对象能被用来存储一个指针,这在大多数32位的机器上能够正常工作,但是在一台64位的机器上却会导致问题。所以,1980到2010年期间(32位机器是主流)编写的程序,之后64位机器陆续普及之后,迁移过来的程序就暴露出来许多隐藏的对字长的依赖性问题,导致错误。

【垂直行业如电商如何衡量搜索引擎的优劣】

在电商行业中,无论是2B还是2C,最终的业务目的就是交易成单,众所周知搜索服务旨在让消费者能够更快的定位到自己想要的产品。

一般电商搜索的核心是搜索精度和搜索广度,精度就是搜索的精确性,广度就是搜索结果的范围,其关键结果肯定是“为用户找到想要的商品”,但过于追求搜索的精确度就会导致出现搜索的结果比较少或结果为0的情况,用户搜不到商品势必会引发流失,因此在搜索服务里面还可以做的就是给用户提供一些相关性搜索结果。那么搜索做的好不好,其实就是在搜索精度和搜索广度二者之间做一个比较好的平衡点。

搜索过程中遇到的问题:

1.随机性发现的Bad case
2.KPI或者OKR考核
3.业务方诉求

核心指标
 
“搜索PV”:指访问搜索页面的次数;“搜索UV”:访问过搜索结果页的用户数;“无结果率”:空结果PV/搜索PV,无结果率越低,代表客户搜索需求解决情况越好;“TOP5 PV—CTR”:指该query search结果中,排在前五位的item有被点击的搜索PV/该query搜索PV该指标能一定程度反应排序效果;“人均搜索PV”:搜索PV/搜索UV;该指标的含义比较复杂,一方面人均pv大的话可能代表用户对搜索比较感兴趣,但另一方面人均pv大也可能代表搜索召回的结果较差,导致用户无法使用较少的点击找到满足需求的结果;“有点击搜索PV占比”:有点击搜索PV/搜索PV数;“PV-CTR”:搜索结果页item点击数/搜索PV数;“UV-CTR”:点击的uv / 曝光的uv;“Item-CTR”:搜索结果页item点击数/搜索结果页item总曝光PV数;
 
搜索技术等级分类





 
https://developer.aliyun.com/article/769492
 
  查看全部

【早读:《深入理解计算机系统》】
 
第二章 信息的表示和处理

大多数计算机使用8位的块,或字节,作为最小的可寻址内存单位。

机器级程序将内存视为一个非常大的字节数组,称为虚拟内存。

内存中每个字节由一个唯一的数字来标识,称为它的地址。所有可能的地址的集合就称为虚拟地址空间。

所以,这个虚拟地址空间只是一个展现给机器级程序的概念性映像。实际上,它将DRAM、闪存、磁盘存储器等和操作系统软件结合起来,封装了复杂性,为程序提供一个看上去统一的字节数组。


C语言中的一个指针的值,都是某个存储块的第一个字节的虚拟地址。每个程序对象可以简单地视为一个字节块,而程序本身就是一个字节序列。

十六进制表示法

由于二进制和十进制对于描述位模式来说都非常不方便:二进制太冗长,十进制和位模式的互相转化很麻烦,替代的方法就是引入16进制。

以0x或者0X开头,0~9、A~F,不区分大小写且大小写不敏感。

重要的是二进制、十进制和十六进制之间的互相转换方法,详细的可以进入凯冰科技知识共享中心搜索相关问题或文章查看。

对于x=2的n次方这个公式,转换十六进制,可以转化为n=i+4j,然后得到的十六进制就是:0x + 2的i次方 + j个0,比如512,是2的9次方,9=1+4*2,所以十六进制就是0x200。

字数据大小

每台计算机都有一个字长,指明指针数据的标称大小。字长决定虚拟地址空间的最大大小。32位字长限制的虚拟地址空间位4千兆字节(约4GB),而现在比较普及的64位字长的虚拟空间位16EB。

大多数64位机器也可以运行32位机器编译的程序,这是一种向后兼容。
//该编译后的程序可以在32或64位机器上运行
linux> gcc -m32 prog.c

//该编译后,只能在64位机器上运行
linux> gcc -m64 prog.c
我们将程序称为32位程序或64位程序,区别在于该程序是如何编译的,而不是其运行的机器类型。

ISO C99引入了确定大小的数据类型,int32_t 和int64_t,其数据大小是固定的,分别为4个字节和8个字节。使用确切大小的整数类型是程序员准确控制数据表示的最佳途径。

程序员应该力图使他们的程序可以在不同的机器和编译器上可移植,可移植的一方面就是说程序对不同数据类型的确切大小不敏感。

比如许多程序员假设一个声明为int类型的程序对象能被用来存储一个指针,这在大多数32位的机器上能够正常工作,但是在一台64位的机器上却会导致问题。所以,1980到2010年期间(32位机器是主流)编写的程序,之后64位机器陆续普及之后,迁移过来的程序就暴露出来许多隐藏的对字长的依赖性问题,导致错误。

【垂直行业如电商如何衡量搜索引擎的优劣】

在电商行业中,无论是2B还是2C,最终的业务目的就是交易成单,众所周知搜索服务旨在让消费者能够更快的定位到自己想要的产品。

一般电商搜索的核心是搜索精度和搜索广度,精度就是搜索的精确性,广度就是搜索结果的范围,其关键结果肯定是“为用户找到想要的商品”,但过于追求搜索的精确度就会导致出现搜索的结果比较少或结果为0的情况,用户搜不到商品势必会引发流失,因此在搜索服务里面还可以做的就是给用户提供一些相关性搜索结果。那么搜索做的好不好,其实就是在搜索精度和搜索广度二者之间做一个比较好的平衡点。

搜索过程中遇到的问题:


1.随机性发现的Bad case
2.KPI或者OKR考核
3.业务方诉求


核心指标
 
  • “搜索PV”:指访问搜索页面的次数;
  • “搜索UV”:访问过搜索结果页的用户数;
  • “无结果率”:空结果PV/搜索PV,无结果率越低,代表客户搜索需求解决情况越好;
  • “TOP5 PV—CTR”:指该query search结果中,排在前五位的item有被点击的搜索PV/该query搜索PV该指标能一定程度反应排序效果;
  • “人均搜索PV”:搜索PV/搜索UV;该指标的含义比较复杂,一方面人均pv大的话可能代表用户对搜索比较感兴趣,但另一方面人均pv大也可能代表搜索召回的结果较差,导致用户无法使用较少的点击找到满足需求的结果;
  • “有点击搜索PV占比”:有点击搜索PV/搜索PV数;
  • “PV-CTR”:搜索结果页item点击数/搜索PV数;
  • “UV-CTR”:点击的uv / 曝光的uv;
  • “Item-CTR”:搜索结果页item点击数/搜索结果页item总曝光PV数;

 
搜索技术等级分类

13cfe86b5b2f4aacba814f4fb2f080e5.png

 
https://developer.aliyun.com/article/769492
 
 

#每日精进#2020年8月3日

总结zkbhj 发表了文章 • 0 个评论 • 2388 次浏览 • 2020-08-03 20:45 • 来自相关话题

【早读:《深入理解计算机系统》】

第二章 信息的表示和处理

现代计算机存储和处理信息以二值信号表示。二值信号能够很容易的被表示、存储和传输,且用二值信号进行存储和执行计算的电子电路非常简单可靠。

三种重要的数字表示:
 

无符号编码:基于传统的二进制表示法,表示大于或者等于0的数字;
补码编码:表示有符号整数的最常见方式;
浮点数编码:表示实数的科学计数法的以2为基数的版本;


计算机的表示法是以有限数量的位来对一个数字进行编码,所以一旦超出界限,某些运算就会溢出,导致令人吃惊的后果。

整数的计算机运算满足人们所熟知的真正整数运算的许多性质。但是浮点数不一样。

整数的表示虽然只能编码一个相对较小的数值范围,但是这种表示是精确的;

浮点数虽然可以编码一个比较大的数值范围,但是这种标示只是近似的;

通过如下命令,可以在gcc编译C程序时指定C语言版本:
linux> gcc -std=c11 zkbhj.c
//其他版本参数
//GNU 89 无,-std=gnu89
//ANSI ,ISO C90 -ansi,-std=c89
//ISO C99 -std=c99
//ISO C11 -std=c11
【Go核心36讲:第11节 通道的高级玩法】

单向通道

所谓单向通道就是,只能发不能收,或者只能收不能发的通道。

声明一个只能发(向通道发送)不能收(从通道接收),容量为1的单向通道:
var uselessChan = make(chan<- int, 1)声明一个只能收(从通道接收)不能发(向通道发送),容量为1的单向通道:
var uselessChan = make(<-chan int, 1)
与发送操作和接收操作对应,这里的“发”和“收”都是站在操作通道的代码的角度上说的。

单向通道有什么应用价值?

概括地说,单向通道最主要的用途就是约束其他代码的行为。
//参数定义中就约束 ch只能进行发送操作,不能接收
//可以限制方法函数内对参数的操作行为做限定
func SendInt(ch chan<- int) {
ch <- rand.Intn(1000)
}//这段接口声明中,就约定了所以要实现这个接口的实现类型
//都约定了这些方法的参数类型
type Notifier interface {
SendInt(ch chan<- int)
}在实际调用的时候,传递一个双向通道即可,因为Go 语言在这种情况下会自动地把双向通道转换为函数所需的单向通道。

一种专门为了操作通道而存在的语句:select语句

select语句只能与通道联用,它一般由若干个分支组成。每次执行这种语句的时候,一般只有一个分支中的代码会被运行。分支分为两种,一种叫做候选分支,另一种叫做默认分支。每个case表达式中都只能包含操作通道的表达式。
select {
case <-intChannels[0]:
fmt.Println("The first candidate case is selected.")
case <-intChannels[1]:
fmt.Println("The second candidate case is selected.")
case elem := <-intChannels[2]: fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
default: fmt.Println("No candidate case is selected!")
}select语句只能对其中的每一个case表达式各求值一次。所以,如果我们想连续或定时地操作其中的通道的话,就往往需要通过在for语句中嵌入select语句的方式实现。但这时要注意,简单地在select语句的分支中使用break语句,只能结束当前的select语句的执行,而并不会对外层的for语句产生作用。这种错误的用法可能会让这个for语句无休止地运行下去。

select语句的分支选择规则总结:

1、对于每一个case表达式,都至少会包含一个代表发送操作的发送表达式或者一个代表接收操作的接收表达式;
2、select语句包含的候选分支中的case表达式都会在该语句执行开始时先被求值,并且求值的顺序是依从代码编写的顺序从上到下的;
3、对于每一个case表达式,如果其中的发送表达式或者接收表达式在被求值时,相应的操作正处于阻塞状态,那么对该case表达式的求值就是不成功的;
4、仅当select语句中的所有case表达式都被求值完毕后,它才会开始选择候选分支。这时候,它只会挑选满足选择条件的候选分支执行。所有的都不满足,执行default;
5、如果select语句发现同时有多个候选分支满足选择条件,那么它就会用一种伪随机的算法在这些分支中选择一个并执行;

 
【关于730政治局会议的总结】

15大要点(内循环 + 持久战)
 
​中国发展仍处于战略机遇期从持久战角度认识中长期问题以国内大循环为主题建立中长期协调机制牢牢把握扩大内需这个战略基点确保宏观政策落地见效保持货币供应量合理增长毫不放松抓好常态化疫情防控扩大最终消费加快新基建以新型城镇化带动投资和消费产业链补短板和锻长板从严打击证券违法活动住房不炒缓解疫情对年轻人就业影响

我们遇到的很多问题是中长期的,必须从持久战的角度加以认识

“在泡沫中狂欢的日志不多了,做好潮水退却后的准备,是每个国家,每个人都要面对的现实”

——吴晓灵 前央行副行长 查看全部
【早读:《深入理解计算机系统》】

第二章 信息的表示和处理

现代计算机存储和处理信息以二值信号表示。二值信号能够很容易的被表示、存储和传输,且用二值信号进行存储和执行计算的电子电路非常简单可靠。

三种重要的数字表示:
 


无符号编码:基于传统的二进制表示法,表示大于或者等于0的数字;
补码编码:表示有符号整数的最常见方式;
浮点数编码:表示实数的科学计数法的以2为基数的版本;



计算机的表示法是以有限数量的位来对一个数字进行编码,所以一旦超出界限,某些运算就会溢出,导致令人吃惊的后果。

整数的计算机运算满足人们所熟知的真正整数运算的许多性质。但是浮点数不一样。

整数的表示虽然只能编码一个相对较小的数值范围,但是这种表示是精确的;

浮点数虽然可以编码一个比较大的数值范围,但是这种标示只是近似的;

通过如下命令,可以在gcc编译C程序时指定C语言版本:
linux> gcc -std=c11 zkbhj.c
//其他版本参数
//GNU 89 无,-std=gnu89
//ANSI ,ISO C90 -ansi,-std=c89
//ISO C99 -std=c99
//ISO C11 -std=c11

【Go核心36讲:第11节 通道的高级玩法】

单向通道

所谓单向通道就是,只能发不能收,或者只能收不能发的通道。

声明一个只能发(向通道发送)不能收(从通道接收),容量为1的单向通道:
var uselessChan = make(chan<- int, 1)
声明一个只能收(从通道接收)不能发(向通道发送),容量为1的单向通道:
var uselessChan = make(<-chan int, 1)
与发送操作和接收操作对应,这里的“发”和“收”都是站在操作通道的代码的角度上说的。

单向通道有什么应用价值?

概括地说,单向通道最主要的用途就是约束其他代码的行为。
//参数定义中就约束 ch只能进行发送操作,不能接收
//可以限制方法函数内对参数的操作行为做限定
func SendInt(ch chan<- int) {
ch <- rand.Intn(1000)
}
//这段接口声明中,就约定了所以要实现这个接口的实现类型
//都约定了这些方法的参数类型
type Notifier interface {
SendInt(ch chan<- int)
}
在实际调用的时候,传递一个双向通道即可,因为Go 语言在这种情况下会自动地把双向通道转换为函数所需的单向通道。

一种专门为了操作通道而存在的语句:select语句

select语句只能与通道联用,它一般由若干个分支组成。每次执行这种语句的时候,一般只有一个分支中的代码会被运行。分支分为两种,一种叫做候选分支,另一种叫做默认分支。每个case表达式中都只能包含操作通道的表达式。
select {
case <-intChannels[0]:
fmt.Println("The first candidate case is selected.")
case <-intChannels[1]:
fmt.Println("The second candidate case is selected.")
case elem := <-intChannels[2]: fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
default: fmt.Println("No candidate case is selected!")
}
select语句只能对其中的每一个case表达式各求值一次。所以,如果我们想连续或定时地操作其中的通道的话,就往往需要通过在for语句中嵌入select语句的方式实现。但这时要注意,简单地在select语句的分支中使用break语句,只能结束当前的select语句的执行,而并不会对外层的for语句产生作用。这种错误的用法可能会让这个for语句无休止地运行下去。

select语句的分支选择规则总结:


1、对于每一个case表达式,都至少会包含一个代表发送操作的发送表达式或者一个代表接收操作的接收表达式;
2、select语句包含的候选分支中的case表达式都会在该语句执行开始时先被求值,并且求值的顺序是依从代码编写的顺序从上到下的;
3、对于每一个case表达式,如果其中的发送表达式或者接收表达式在被求值时,相应的操作正处于阻塞状态,那么对该case表达式的求值就是不成功的;
4、仅当select语句中的所有case表达式都被求值完毕后,它才会开始选择候选分支。这时候,它只会挑选满足选择条件的候选分支执行。所有的都不满足,执行default;
5、如果select语句发现同时有多个候选分支满足选择条件,那么它就会用一种伪随机的算法在这些分支中选择一个并执行;


 
【关于730政治局会议的总结】

15大要点(内循环 + 持久战)
 
  1. ​中国发展仍处于战略机遇期
  2. 从持久战角度认识中长期问题
  3. 以国内大循环为主题
  4. 建立中长期协调机制
  5. 牢牢把握扩大内需这个战略基点
  6. 确保宏观政策落地见效
  7. 保持货币供应量合理增长
  8. 毫不放松抓好常态化疫情防控
  9. 扩大最终消费
  10. 加快新基建
  11. 以新型城镇化带动投资和消费
  12. 产业链补短板和锻长板
  13. 从严打击证券违法活动
  14. 住房不炒
  15. 缓解疫情对年轻人就业影响


我们遇到的很多问题是中长期的,必须从持久战的角度加以认识


“在泡沫中狂欢的日志不多了,做好潮水退却后的准备,是每个国家,每个人都要面对的现实”

——吴晓灵 前央行副行长


#每日精进#2020年08月02日

总结zkbhj 发表了文章 • 0 个评论 • 2490 次浏览 • 2020-08-02 19:46 • 来自相关话题

【午读:《深入理解计算机系统》】

第一章 计算机系统漫游

现代系统之间利用网络通信,和其他系统连接在一起。从一个单独的系统来看,网络可以视为一个I/O设备。

系统不仅仅只是硬件,而是硬件和软件互相交织的结合体,他们之间共同协作已达到运行应用程序的最终目的。

Amdahl(安达尔定律)定律

主要思想是:当对系统的某个部分加速时,其对系统整体性能的影响取决于该部分的重要性和加速程度。
主要观点是:想要显著加速整个系统,必须提升全系统中相当大的部分的速度。

 
Amdahl定律描述了改善任何过程的一般原则。

并发和并行

整个计算机发展历史中,我们一直在做两件事:一是让计算机做得更多,二是让计算机运行的更快。
并发:是一个通用概念,指一个同时具有多个活动的系统;
并行:指的是用并发来使一个系统运行得更快。

进程级并发
并发构建在进程整个抽象上,就能够设计出同时有多个程序执行的系统。
单处理器系统:只有一个处理器的系统;
多处理器系统:一个由单操作系统内核控制的多处理器组成的系统。
超线程,有时称为同时多线程,是一项允许一个CPU执行多个控制流的技术。

指令级并行
CPU可以同时执行多条指令的属性称为指令级并行。
超标量处理器:处理器能够达到比一个时钟周期一条指令更快的执行速率。

计算机系统中抽象的重要性
计算机系统提供的一些抽象,它提供不同层次的抽象表示来隐藏实际实现的复杂性。
比如上一节中:文件是对I/O设备的抽象,虚拟内存是对主存和磁盘的抽象,进程则是对一个正在运行的程序的抽象(处理器、主存和I/O的抽象)。
虚拟机,则提供对整个计算机的抽象。

第一章总结
计算机系统是由硬件和系统软件组成的,它们共同协作以运行应用程序。计算机内部的信息被表示为一组组的位,它们根据上下文有不同的解释方式。程序被其他程序翻译成不同的形式,开始是ASCII文本,然后被编译器和链接器翻译成二进制可执行文件。

处理器读取并解释存储在主存里的二进制指令。因为计算机花了大把时间用于存储器、I/O设备和CPU寄存器之间复制数据,所以讲系统中的存储设备划分成层次结构——CPU寄存器在顶部,接着是多层的硬件高速缓存存储器、DRAM主存和磁盘存储器。

在层次模型中,位于更高层的存储设备比低层的存储设备要更快,单位比特造价也更高。层次结构中较高层次存储设备可以作为较低层次存储设备的高速缓存。通过理解和运用这种存储层次结构的知识,程序员可以优化C程序的性能。

操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象:

(1)文件是对I/O设备的抽象

(2)虚拟存储器是对主存和I/O设备的抽象

(3)进程是对处理器、主存和I/O设备的抽象。

另外,虚拟机提供了对整个计算机的抽象。

最后,网络提供了计算机系统之间通信的手段。从特殊系统的角度来看,网络就是一种I/O设备。
 

一周的早读,完成了《深入理解计算机系统》这本又厚又重的计算机最底层原理书籍第一章的阅读和理解。
好好利用碎片时间,以及找到自己的高效时间段及学习方式很重要。
积少成多,积流成河!
明天开启第二章:程序结构和执行!


PS广而告之时间:

正在筹划一个作为技术人员角度的个人分享网站,以很程序员的视角,将学习过的一门语言、一本技术书籍,以“手册”的形式输出出来。
根据能量守恒定律,有输入就要有输出,既然自己有这样的渠道和能力,打算把体系学习过的一些内容,沉淀成一本本技术手册分享出来,可以给更多有同样需求的人提供一些有意义的帮助和指导。
还是凯冰科技10多年来坚持的一句slogan:
代码改变世界,技术改变生活
Code changes the world, technology changes life
doc.zkbhj.com
第一本就是这本《深入理解计算机系统》。嘿嘿~

【关于如何在美团里继续使用支付宝支付的方法】

额。。由于一些你懂的的原因,大部分美团用户已经无法再美团APP上用支付宝进行支付了。这里暂时不评论这件事情的对与错好与坏,只是从技术的角度,帮你找到了一个怎么继续在美团里使用支付宝支付的方法,操作步骤如下:

1、打开美团APP,点击进入个人中心页面,点击右上角的在线客服,进入客服聊天界面;

2、输入问题:支付宝无法使用;

3、在返回的结果里选择:放弃优惠,恢复支付宝。






经过上面的几步“骚操作”,就可以继续在美团里使用支付宝进行支付了!
 
​【Go语言核心36讲:第10节 通道的基本操作 】

☆ 通道(Channel)

通道是Go 语言最有特色的数据类型,是不同Goroutine之间通信的“桥梁”。
 

Don’t communicate by sharing memory; share memory by communicating. (不要通过共享内存来通信,而应该通过通信来共享内存。)


通道类型的值是 Go 语言自带的、唯一一个可以满足并发安全性的类型。

当容量为0时,我们可以称通道为非缓冲通道,也就是不带缓冲的通道。而当容量大于0时,我们可以称为缓冲通道,也就是带有缓冲的通道。

一个通道相当于一个先进先出(FIFO)的队列。也就是说,通道中的各个元素值都是严格地按照发送的顺序排列的,先被发送通道的元素值一定会先被接收。元素值的发送和接收都需要用到操作符<-。我们也可以叫它接送操作符。一个左尖括号紧接着一个减号形象地代表了元素值的传输方向。

☆ 对通道的发送和接收操作都有哪些基本的特性?

1、对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。

元素值从外界进入通道时会被复制。更具体地说,进入通道的并不是在接收操作符右边的那个元素值,而是它的副本。另一方面,元素值从通道进入外界时会被移动。这个移动操作实际上包含了两步,第一步是生成正在通道中的这个元素值的副本,并准备给到接收方,第二步是删除在通道中的这个元素值。

2、发送操作和接收操作中对元素值的处理都是不可分割的。

这里的“不可分割”的意思是,它们处理元素值时都是一气呵成的,绝不会被打断。

发送时,“复制元素值”和“放置副本到通道内部”这两个步骤不会被打断;

接收时,“复制通道内元素值”“放置副本到接收方”“删掉原值”三个步骤不会被打断。

3、发送操作在完全完成之前会被阻塞。接收操作也是如此。

以上各步骤执行期间,其他操作都会被阻塞。如此阻塞代码其实就是为了实现操作的互斥和元素值的完整。

☆ 发送操作和接收操作在什么时候可能被长时间的阻塞?

有缓冲的,如果通道已满,对它的所有发送操作都会被阻塞,直到通道中有元素值被接收走。如果通道已空,对它的所有接收操作都会被阻塞,直到通道中有新的元素值出现。所有被阻塞的goroutine都是按FIFO的策略执行的。

非缓冲通道,情况要简单一些。无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。由此可见,非缓冲通道是在用同步的方式传递数据。

对于值为nil的通道,不论它的具体类型是什么,对它的发送操作和接收操作都会永久地处于阻塞状态。它们所属的 goroutine 中的任何代码,都不再会被执行。所以我们一定不要忘记初始化通道!

☆ 发送操作和接收操作在什么时候会引发 panic?

通道一旦关闭,再对它进行发送操作,就会引发 panic。

如果我们试图关闭一个已经关闭了的通道,也会引发 panic。

通过接收表达式的第二个结果值,可以来判断通道是否关闭,但是可能有延时的。即如果通道已经关闭,但还有值未被取出,则这个时候,返回的仍然是true!

所以最佳实践告诉我们,千万不要让接收方关闭通道,而应当让发送方做这件事。 查看全部

【午读:《深入理解计算机系统》】

第一章 计算机系统漫游

现代系统之间利用网络通信,和其他系统连接在一起。从一个单独的系统来看,网络可以视为一个I/O设备。

系统不仅仅只是硬件,而是硬件和软件互相交织的结合体,他们之间共同协作已达到运行应用程序的最终目的。

Amdahl(安达尔定律)定律


主要思想是:当对系统的某个部分加速时,其对系统整体性能的影响取决于该部分的重要性和加速程度。
主要观点是:想要显著加速整个系统,必须提升全系统中相当大的部分的速度。


 
Amdahl定律描述了改善任何过程的一般原则。

并发和并行

整个计算机发展历史中,我们一直在做两件事:一是让计算机做得更多,二是让计算机运行的更快。
并发:是一个通用概念,指一个同时具有多个活动的系统;
并行:指的是用并发来使一个系统运行得更快。

进程级并发
并发构建在进程整个抽象上,就能够设计出同时有多个程序执行的系统。
单处理器系统:只有一个处理器的系统;
多处理器系统:一个由单操作系统内核控制的多处理器组成的系统。
超线程,有时称为同时多线程,是一项允许一个CPU执行多个控制流的技术。

指令级并行
CPU可以同时执行多条指令的属性称为指令级并行。
超标量处理器:处理器能够达到比一个时钟周期一条指令更快的执行速率。

计算机系统中抽象的重要性
计算机系统提供的一些抽象,它提供不同层次的抽象表示来隐藏实际实现的复杂性。
比如上一节中:文件是对I/O设备的抽象,虚拟内存是对主存和磁盘的抽象,进程则是对一个正在运行的程序的抽象(处理器、主存和I/O的抽象)。
虚拟机,则提供对整个计算机的抽象。

第一章总结
计算机系统是由硬件和系统软件组成的,它们共同协作以运行应用程序。计算机内部的信息被表示为一组组的位,它们根据上下文有不同的解释方式。程序被其他程序翻译成不同的形式,开始是ASCII文本,然后被编译器和链接器翻译成二进制可执行文件。

处理器读取并解释存储在主存里的二进制指令。因为计算机花了大把时间用于存储器、I/O设备和CPU寄存器之间复制数据,所以讲系统中的存储设备划分成层次结构——CPU寄存器在顶部,接着是多层的硬件高速缓存存储器、DRAM主存和磁盘存储器。

在层次模型中,位于更高层的存储设备比低层的存储设备要更快,单位比特造价也更高。层次结构中较高层次存储设备可以作为较低层次存储设备的高速缓存。通过理解和运用这种存储层次结构的知识,程序员可以优化C程序的性能。

操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象:

(1)文件是对I/O设备的抽象

(2)虚拟存储器是对主存和I/O设备的抽象

(3)进程是对处理器、主存和I/O设备的抽象。

另外,虚拟机提供了对整个计算机的抽象。

最后,网络提供了计算机系统之间通信的手段。从特殊系统的角度来看,网络就是一种I/O设备。
 


一周的早读,完成了《深入理解计算机系统》这本又厚又重的计算机最底层原理书籍第一章的阅读和理解。
好好利用碎片时间,以及找到自己的高效时间段及学习方式很重要。
积少成多,积流成河!
明天开启第二章:程序结构和执行!



PS广而告之时间

正在筹划一个作为技术人员角度的个人分享网站,以很程序员的视角,将学习过的一门语言、一本技术书籍,以“手册”的形式输出出来。
根据能量守恒定律,有输入就要有输出,既然自己有这样的渠道和能力,打算把体系学习过的一些内容,沉淀成一本本技术手册分享出来,可以给更多有同样需求的人提供一些有意义的帮助和指导。
还是凯冰科技10多年来坚持的一句slogan:
代码改变世界,技术改变生活
Code changes the world, technology changes life
doc.zkbhj.com
第一本就是这本《深入理解计算机系统》。嘿嘿~

【关于如何在美团里继续使用支付宝支付的方法】

额。。由于一些你懂的的原因,大部分美团用户已经无法再美团APP上用支付宝进行支付了。这里暂时不评论这件事情的对与错好与坏,只是从技术的角度,帮你找到了一个怎么继续在美团里使用支付宝支付的方法,操作步骤如下:


1、打开美团APP,点击进入个人中心页面,点击右上角的在线客服,进入客服聊天界面;

2、输入问题:支付宝无法使用;

3、在返回的结果里选择:放弃优惠,恢复支付宝。



WechatIMG147.jpeg

经过上面的几步“骚操作”,就可以继续在美团里使用支付宝进行支付了!
 
​【Go语言核心36讲:第10节 通道的基本操作 】

☆ 通道(Channel)

通道是Go 语言最有特色的数据类型,是不同Goroutine之间通信的“桥梁”。
 


Don’t communicate by sharing memory; share memory by communicating. (不要通过共享内存来通信,而应该通过通信来共享内存。)



通道类型的值是 Go 语言自带的、唯一一个可以满足并发安全性的类型。

当容量为0时,我们可以称通道为非缓冲通道,也就是不带缓冲的通道。而当容量大于0时,我们可以称为缓冲通道,也就是带有缓冲的通道。

一个通道相当于一个先进先出(FIFO)的队列。也就是说,通道中的各个元素值都是严格地按照发送的顺序排列的,先被发送通道的元素值一定会先被接收。元素值的发送和接收都需要用到操作符<-。我们也可以叫它接送操作符。一个左尖括号紧接着一个减号形象地代表了元素值的传输方向。

☆ 对通道的发送和接收操作都有哪些基本的特性?

1、对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。

元素值从外界进入通道时会被复制。更具体地说,进入通道的并不是在接收操作符右边的那个元素值,而是它的副本。另一方面,元素值从通道进入外界时会被移动。这个移动操作实际上包含了两步,第一步是生成正在通道中的这个元素值的副本,并准备给到接收方,第二步是删除在通道中的这个元素值。

2、发送操作和接收操作中对元素值的处理都是不可分割的。

这里的“不可分割”的意思是,它们处理元素值时都是一气呵成的,绝不会被打断。

发送时,“复制元素值”和“放置副本到通道内部”这两个步骤不会被打断;

接收时,“复制通道内元素值”“放置副本到接收方”“删掉原值”三个步骤不会被打断。

3、发送操作在完全完成之前会被阻塞。接收操作也是如此。

以上各步骤执行期间,其他操作都会被阻塞。如此阻塞代码其实就是为了实现操作的互斥和元素值的完整。

☆ 发送操作和接收操作在什么时候可能被长时间的阻塞?

有缓冲的,如果通道已满,对它的所有发送操作都会被阻塞,直到通道中有元素值被接收走。如果通道已空,对它的所有接收操作都会被阻塞,直到通道中有新的元素值出现。所有被阻塞的goroutine都是按FIFO的策略执行的。

非缓冲通道,情况要简单一些。无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。由此可见,非缓冲通道是在用同步的方式传递数据。

对于值为nil的通道,不论它的具体类型是什么,对它的发送操作和接收操作都会永久地处于阻塞状态。它们所属的 goroutine 中的任何代码,都不再会被执行。所以我们一定不要忘记初始化通道!

☆ 发送操作和接收操作在什么时候会引发 panic?

通道一旦关闭,再对它进行发送操作,就会引发 panic。

如果我们试图关闭一个已经关闭了的通道,也会引发 panic。

通过接收表达式的第二个结果值,可以来判断通道是否关闭,但是可能有延时的。即如果通道已经关闭,但还有值未被取出,则这个时候,返回的仍然是true!

所以最佳实践告诉我们,千万不要让接收方关闭通道,而应当让发送方做这件事。

#每日精进#2020年8月1日

总结zkbhj 发表了文章 • 0 个评论 • 1884 次浏览 • 2020-08-01 16:48 • 来自相关话题

 【《无证之罪》紫金陈,阅读总结】
这本书也是在刷完了网剧版(秦昊、邓家佳主演,《无证之罪》8.2分 )之后来读的原著。电视剧版还是做了很大的改动的,整个作品作为侦探推理小说来讲还是可圈可点的,一名技术精尖的法医犯罪,可以把所有证据消灭掉;一个辞职了刑警队长的大学数学教授,可以把高次方程的理论代入到案件的推导中。最后的结局说实话竟然是团灭,真的没有想到。怎么说,个人不是很喜欢这个结局。喜欢推理的同学,可以一看。
 
https://ask.zkbhj.com/?/article/351
 
【Elasticsearch全文检索的实现原理简介】
 
全文搜索原理:倒排索引
 
大家都知道,在数据库中直接全表查询的时间复杂度是o(n),如果对索引列进行查询,其时间复杂度为o(logn),如果数据以key-value形式存储,查询时间复杂度将降为o(1)。那么在全文搜索中我们直接建立从查询词到文档的映射是不是也就获得了o(1)的查询性能?这种词汇到文档的映射被称之为倒排索引。 
索引、类型与文档
索引:含相同属性的文档集合。相当于关系型数据库中的一个database类型:索引可以定义一个或者多个类型,文档必须属于一个类型,其相当于关系型数据库中的表,是通过mapping定义的。mapping中主要包括字段名、字段数据类型和字段索引类型这3个方面的定义,相当于关系型数据库中的schema。(类型在ES7.0中已被废弃)文档:可以被索引的基本数据单位,也是全文搜索中被搜索的对象,可以对应一个网页,一篇txt文档或者一个商品。 相当于关系型数据库中的表中的一行记录。
 
分片

有时候一个索引的数据量非常大,甚至超出了单机的存储能力,这个时候需要对索引分片存储,分别存到不同机器上。

备份

为了防止节点故障到时索引分片丢失,一般会对分片进行备份。备份除了可以保障数据安全性,还可以分担搜索的压力。

ES创建索引默认5个分片,1个备份,分片只能在创建索引的时候指定而备份可以后期动态修改。 
查询
 //指定属性查询
{
"query":{
"match":{
"name":"一页书"
}
}
}

//范围查询
{
"query":{
"range":{
"age{
"gte":18,
"lte":60
}
}
}
}

//多个条件联合查询
{
"query":{
“bool”: {
"must":[
{
"match":{
"name":"一页书"
}
},
{
"filter":{
"term"{
"age":18
}
}
}
]
}
}
}match、macth_phrase、multi_match与term的区别
match对会先对query进行分词,只要文档里面包含一个query中一个词就会被搜出来;macth_phrase也会对query进行分词,但一个文档必须包含query里面所有的词才会被搜出来,可以通过slop参数降低这种约束;
multi_match对多个字段同时进行匹配;term表示完全匹配,不对query进行分词,直接去匹配索引。
{
"query": {
"match_phrase": {
"content" : {
"query" : "我的宝马多少马力",
"slop" : 1
}
}
}
}https://zhuanlan.zhihu.com/p/94181307
 
【中文分词及结巴分词原理】
 
昨天在跟同事讨论我们搜索系统在做实体识别的时候,具体的实现原理是什么,当时对比的是ES是如何做分词的,想当然的理解成结巴也是用的词典树来实现的,今天查了一下,其实不是词典树,而是有向无环图(DAG)。
 
自如的NLP部分(实体识别)用的技术方案是结巴分词。所以,今天详细深入了解下一下中文分词及结巴分词相关的内容。详细见:
https://ask.zkbhj.com/?/article/364
 
  查看全部

 【《无证之罪》紫金陈,阅读总结】
这本书也是在刷完了网剧版(秦昊、邓家佳主演,《无证之罪》8.2分 )之后来读的原著。电视剧版还是做了很大的改动的,整个作品作为侦探推理小说来讲还是可圈可点的,一名技术精尖的法医犯罪,可以把所有证据消灭掉;一个辞职了刑警队长的大学数学教授,可以把高次方程的理论代入到案件的推导中。最后的结局说实话竟然是团灭,真的没有想到。怎么说,个人不是很喜欢这个结局。喜欢推理的同学,可以一看。
 
https://ask.zkbhj.com/?/article/351
 
【Elasticsearch全文检索的实现原理简介】
 
全文搜索原理:倒排索引
 
大家都知道,在数据库中直接全表查询的时间复杂度是o(n),如果对索引列进行查询,其时间复杂度为o(logn),如果数据以key-value形式存储,查询时间复杂度将降为o(1)。那么在全文搜索中我们直接建立从查询词到文档的映射是不是也就获得了o(1)的查询性能?这种词汇到文档的映射被称之为倒排索引。 
索引、类型与文档
  • 索引:含相同属性的文档集合。相当于关系型数据库中的一个database
  • 类型:索引可以定义一个或者多个类型,文档必须属于一个类型,其相当于关系型数据库中的表,是通过mapping定义的。mapping中主要包括字段名、字段数据类型和字段索引类型这3个方面的定义,相当于关系型数据库中的schema。(类型在ES7.0中已被废弃)
  • 文档:可以被索引的基本数据单位,也是全文搜索中被搜索的对象,可以对应一个网页,一篇txt文档或者一个商品。 相当于关系型数据库中的表中的一行记录。

 
分片

有时候一个索引的数据量非常大,甚至超出了单机的存储能力,这个时候需要对索引分片存储,分别存到不同机器上。

备份

为了防止节点故障到时索引分片丢失,一般会对分片进行备份。备份除了可以保障数据安全性,还可以分担搜索的压力。

ES创建索引默认5个分片,1个备份,分片只能在创建索引的时候指定而备份可以后期动态修改。 
查询
 
//指定属性查询
{
"query":{
"match":{
"name":"一页书"
}
}
}

//范围查询
{
"query":{
"range":{
"age{
"gte":18,
"lte":60
}
}
}
}

//多个条件联合查询
{
"query":{
“bool”: {
"must":[
{
"match":{
"name":"一页书"
}
},
{
"filter":{
"term"{
"age":18
}
}
}
]
}
}
}
match、macth_phrase、multi_match与term的区别
  • match对会先对query进行分词,只要文档里面包含一个query中一个词就会被搜出来;
  • macth_phrase也会对query进行分词,但一个文档必须包含query里面所有的词才会被搜出来,可以通过slop参数降低这种约束;

  • multi_match对多个字段同时进行匹配;
  • term表示完全匹配,不对query进行分词,直接去匹配索引。

{
"query": {
"match_phrase": {
"content" : {
"query" : "我的宝马多少马力",
"slop" : 1
}
}
}
}
https://zhuanlan.zhihu.com/p/94181307
 
【中文分词及结巴分词原理】
 
昨天在跟同事讨论我们搜索系统在做实体识别的时候,具体的实现原理是什么,当时对比的是ES是如何做分词的,想当然的理解成结巴也是用的词典树来实现的,今天查了一下,其实不是词典树,而是有向无环图(DAG)。
 
自如的NLP部分(实体识别)用的技术方案是结巴分词。所以,今天详细深入了解下一下中文分词及结巴分词相关的内容。详细见:
https://ask.zkbhj.com/?/article/364