blog's People
blog's Issues
【git commit 和 rebase】
【编辑器】
【json】
function tokenizeString(string) {
const tokens = []; // 储存标记的数组
let currentToken = ''; // 当前标记
let inString = false; // 当前位置是否在字符串中
for (const char of string) { // 遍历字符串的每个字符
if (char === '"') { // 检查当前字符是否为双引号
inString = !inString; // 如果是双引号,切换到另一种状态:打开或关闭字符串
}
// 如果在字符串中,所有字符都添加到当前标记中,包括逗号和冒号
if (inString) {
currentToken += char;
}
else if (char === ',' || char === '{' || char === '}') { // 如果当前字符是逗号或括号
if (currentToken !== '') { // 检查当前标记是否非空
tokens.push(currentToken); // 添加当前标记到标记列表中
currentToken = ''; // 重置当前标记
}
tokens.push(char); // 添加当前字符到标记列表中
}
else if (char !== ':' && char !== ' ') { // 如果当前字符是数字或字母
currentToken += char; // 添加当前字符到当前标记中
}
}
if (currentToken !== '') { // 循环结束后,检查当前标记是否非空
tokens.push(currentToken); // 添加当前标记到标记列表中
}
return tokens; // 返回标记列表
}
rpc
RPC
RPC(Remote Procedure Call,远程过程调用)是一种用于实现分布式系统中进程间通信的技术。它允许程序员调用远程计算机上的函数或方法,就像调用本地函数或方法一样。RPC框架隐藏了底层通信协议的细节,使得远程调用可以像本地调用一样简单和透明。
在RPC调用中,调用方(客户端)发送一个请求消息到远程服务器上的一个特定服务(服务端),该服务接收请求并返回一个响应消息。这些请求和响应消息通常使用轻量级的传输协议(如TCP或UDP)进行传输,以提高传输效率。
RPC框架通常由客户端和服务端两个组件组成,它们之间通过网络进行通信。客户端使用一个stub(代理)来调用服务端的方法,stub会把调用转换为网络消息并将其发送给服务端。服务端接收到请求后,解码消息并调用本地方法,将结果打包成响应消息并发送回客户端。
利用 python 实现,代码案例
以下是一个简单的使用Python实现的RPC示例,它使用了Python标准库中的xmlrpc
模块。
首先,在服务端上创建一个可以远程调用的函数:
# server.py
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler
# 定义远程调用函数
def add(x, y):
return x + y
# 创建一个RPC服务器并绑定端口
with SimpleXMLRPCServer(('localhost', 8000), requestHandler=SimpleXMLRPCRequestHandler) as server:
server.register_introspection_functions()
# 注册远程函数
server.register_function(add, 'add')
# 开始监听请求
server.serve_forever()
然后,在客户端上调用该函数:
# client.py
import xmlrpc.client
# 创建一个RPC代理
proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
# 调用远程函数
result = proxy.add(4, 5)
print("4 + 5 = %s" % str(result))
运行服务端程序后,在另一个命令行窗口中运行客户端程序,即可看到输出:
4 + 5 = 9
这个例子中,服务端使用SimpleXMLRPCServer
创建了一个RPC服务器,客户端使用ServerProxy
创建了一个RPC代理,并调用了服务端的add
函数。
流程
RPC 的基本流程,下面我再详细解释一下:
-
客户端和服务器之间建立 TCP 连接,该连接可以是按需连接或长连接。按需连接指的是每次远程调用都会建立一个新的连接,调用结束后立即断开连接;长连接指的是多个远程调用共享一个连接,连接在一段时间内不会关闭,以提高效率。
-
客户端通过某种方式获取服务器的地址,以及需要调用的方法的名称和参数信息。这个过程通常是由服务注册中心完成的,客户端向注册中心发送请求,获取对应的服务地址和方法信息。
-
客户端使用客户端 stub(代理)将需要调用的方法以及参数序列化成可以通过网络传输的格式(例如 JSON 或 XML),并将其发送给服务端。
-
服务端接收到请求后,使用服务器 stub 将接收到的数据反序列化成方法名称和参数,并进行本地调用。调用完成后,服务端再将返回值序列化并发送回客户端。
-
客户端接收到返回值后,使用客户端 stub 将其反序列化成可以使用的格式。
需要注意的是,在实际的 RPC 框架中,还会有很多细节需要处理,例如异常处理、协议版本兼容性等等。但是上述的流程是基本的框架,可以帮助我们更好地理解 RPC 的实现原理。
代码示例
以下是一个简单的示例代码,分为客户端和服务端两个部分。
在客户端上,我们需要实现一个 RPC 客户端(rpcclient.py
),一个用于劫持发送数据的客户端 stub(rpcstub.py
),以及一个用于与服务端建立连接的 TCP 客户端(tcpclient.py
)。客户端主文件为clientmain.py
。
rpcclient.py
:
import json
class RPCClient:
def __init__(self, stub):
self.stub = stub
def call(self, method, *args):
data = {'method': method, 'params': args}
data = json.dumps(data)
result = self.stub.send(data)
return json.loads(result)['result']
rpcstub.py
:
class RPCStub:
def __init__(self, client):
self.client = client
def __getattr__(self, name):
def method(*args):
return self.client.call(name, *args)
return method
tcpclient.py
:
import socket
class TCPClient:
def __init__(self, host, port):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.host, self.port))
def send(self, data):
self.socket.sendall(data.encode())
result = self.socket.recv(1024).decode()
return result
clientmain.py
:
from tcpclient import TCPClient
from rpcstub import RPCStub
from rpcclient import RPCClient
client = TCPClient('localhost', 8000)
stub = RPCStub(client)
rpc = RPCClient(stub)
result = rpc.call('add', 4, 5)
print("4 + 5 = %s" % result)
result = rpc.call('multiply', 4, 5)
print("4 * 5 = %s" % result)
在服务端上,我们需要实现一个 RPC 服务器(rpcserver.py
),一个用于劫持接收数据的服务端 stub(rpcstub.py
),以及一个用于与客户端建立连接的 TCP 服务器(tcpserver.py
)。服务端主文件为servermain.py
。
rpcserver.py
:
import json
class RPCServer:
def __init__(self, stub):
self.stub = stub
def handle_request(self, request):
data = json.loads(request)
method = getattr(self.stub, data['method'])
result = method(*data['params'])
result = json.dumps({'result': result})
return result
rpcstub.py
:
class RPCStub:
def __init__(self):
pass
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
tcpserver.py
:
import socket
class TCPServer:
def __init__(self, host, port):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((self.host, self.port))
self.socket.listen(1)
def accept(self):
conn, addr = self.socket.accept()
return conn
def recv(self, conn):
data = conn.recv(1024).decode()
这段示例代码是一个简单的 RPC 实现,其中包括客户端和服务端两个部分。
在 client
文件夹下,clientmain.py
是客户端的主文件,它通过调用 rpcclient.py
中的 RPCClient
类来实现与服务端的远程调用。RPCClient
类包含一个 rpcstub
属性,它是客户端的 rpc stub,用于将客户端发出的请求数据序列化并发送到服务端。同时,RPCClient
还包含一个 tcpclient
属性,它用于与服务端建立 TCP 连接,并通过这个连接来发送请求和接收响应。
在 server
文件夹下,servermain.py
是服务端的主文件,它通过调用 rpcserver.py
中的 RPCServer
类来实现服务端的远程调用。RPCServer
类包含一个 rpcstub
属性,它是服务端的 rpc stub,包含了客户端需要调用的方法。同时,RPCServer
还包含一个 tcpserver
属性,它用于监听来自客户端的连接请求,并通过这些连接来接收请求数据并处理请求。
jsondecode.py
文件中的 json_decode
函数则是服务端的解码函数,用于将从客户端接收到的序列化数据解析为对应的方法名和参数,并调用 rpcstub
中的相应方法。
整个 RPC 的流程如下:
- 客户端与服务端建立 TCP 连接
- 客户端调用
rpcstub
中的方法,并将需要传递给服务端的数据作为参数传递给这个方法 rpcstub
将数据序列化成 JSON 格式,并通过tcpclient
发送到服务端- 服务端接收到请求数据后,通过
jsondecode
解析出对应的方法名和参数,并调用rpcstub
中对应的方法 rpcstub
执行完对应的方法后,将返回值序列化成 JSON 格式,并通过tcpserver
发送回客户端- 客户端接收到服务端的返回值,并将其解析成对应的数据格式,以供下一步处理使用。
【实现 排序组件】
使用 react umi ts react hook, 中 Dropdown 进行二次封装,
实现 点击按钮,overlay 里面显示四列选择,创建时间,更新时间,正序,倒序
点击四个元素,可以多选,代码如何显示
以下是一个示例代码,用于实现您所描述的功能:
import React, { useState, useCallback } from 'react';
import { Dropdown, Menu } from 'antd';
import { ClickParam } from 'antd/lib/menu';
interface Column {
key: string;
name: string;
}
interface OrderMenuProps {
columns: Column[];
onSelect: (columns: Column[], orders: 'asc' | 'desc'[]) => void;
}
const OrderMenu: React.FC<OrderMenuProps> = ({ columns, onSelect }) => {
const [selectedColumns, setSelectedColumns] = useState<Column[]>([]);
const [orders, setOrders] = useState<'asc' | 'desc'[]>([]);
const handleSelect = useCallback(
({ key }: ClickParam) => {
const index = selectedColumns.findIndex((column) => column.key === key);
if (index > -1) {
setSelectedColumns((prevSelectedColumns) =>
prevSelectedColumns.filter((column) => column.key !== key)
);
setOrders((prevOrders) => prevOrders.filter((_, i) => i !== index));
} else {
const column = columns.find((column) => column.key === key);
if (column) {
setSelectedColumns((prevSelectedColumns) => [...prevSelectedColumns, column]);
setOrders((prevOrders) => [...prevOrders, 'asc']);
}
}
},
[columns, selectedColumns]
);
const handleMenuClick = useCallback(
({ key }: ClickParam) => {
if (key === 'sort') {
onSelect(selectedColumns, orders);
} else if (key === 'reset') {
setSelectedColumns([]);
setOrders([]);
}
},
[onSelect, selectedColumns, orders]
);
const menu = (
<Menu onClick={handleMenuClick}>
{columns.map((column) => (
<Menu.Item key={column.key} disabled={selectedColumns.includes(column)}>
{column.name}
</Menu.Item>
))}
<Menu.Divider />
<Menu.Item key="asc" disabled={!selectedColumns.length}>
正序
</Menu.Item>
<Menu.Item key="desc" disabled={!selectedColumns.length}>
倒序
</Menu.Item>
<Menu.Item key="sort" disabled={!selectedColumns.length}>
排序
</Menu.Item>
<Menu.Divider />
<Menu.Item key="reset" disabled={!selectedColumns.length}>
重置
</Menu.Item>
</Menu>
);
return (
<Dropdown overlay={menu} trigger={['click']}>
<a className="ant-dropdown-link" href="#">
点击按钮进行排序
</a>
</Dropdown>
);
};
export default OrderMenu;
在 OrderMenu
组件中,首先定义了 Column
类型,以表示每个列表的列信息。然后,在 OrderMenu
的属性中,我们接收了 columns
和 onSelect
两个参数。columns
是一个描述列表的列的数组,onSelect
是一个回调函数,用于在选择列和排序方式之后触发排序操作。
在组件的主体中,我们使用了 useState
来存储当前选择的列和排序方式,以及一个 useCallback
处理函数 handleSelect
,用于在用户选择或取消选择一个列时更新状态。在菜单项点击后,我们会调用 handleMenuClick
处理函数,该函数根据用户选择的操作来执行相应的操作,例如排序或重置所有选项。
最后,我们使用 Ant Design
的 Dropdown
和 Menu
组件来实现一个下拉选择菜单,当用户点击按钮时,它将显示可用于选择列表列和排序方式的所有选项。同时,我们还将 OrderMenu
组件导出为默认组件,以便在其他地方使用。
【画线】
Bresenham 算法的线段绘制方法
drawLine(p1, p2, color = Color.black()) {
// p1 p2 分别是起点和终点, Point 类型
// color Color
// 使用 drawPoint 函数来画线
let dx = p2.x - p1.x
let dy = p2.y - p1.y
if (Math.abs(dy) > Math.abs(dx)) {
let y = p1.y
while (y !== p2.y) {
let x = dx / dy * (y - p1.y) + p1.x
let point = Point.new(x, y)
this.drawPoint(point, color)
let i = dy > 0 ? 1 : -1
y += i
}
} else {
let x = p1.x
while (x !== p2.x) {
let y = dx == 0 ? p1.y : dy / dx * (x - p1.x) + p1.y
let point = Point.new(x, y)
this.drawPoint(point, color)
let i = dx > 0 ? 1 : -1
x += i
}
}
}
```js
drawLineDDA(p1, p2, color = Color.black()) {
let dx = p2.x - p1.x
let dy = p2.y - p1.y
let steps = Math.max(Math.abs(dx), Math.abs(dy))
let xIncrement = dx / steps
let yIncrement = dy / steps
let x = p1.x
let y = p1.y
for (let i = 0; i <= steps; i++) {
let point =Point.new(x, y)
this.drawPoint(point, color)
x += xIncrement
y += yIncrement
}
}
DDA 算法的思路是将线段分成若干个等长的小线段,然后逐个绘制这些小线段。这个算法的优点是实现简单,但是缺点是精度不高,容易出现锯齿状的效果。如果你想要更高精度的线段绘制,可以尝试中点画线算法。
中点画线算法是一种用于绘制线段的算法,它可以在不使用浮点数的情况下,实现高精度的线段绘制。下面是中点画线算法的具体实现:
drawLineMidpoint(p1, p2, color = Color.black()) {
let dx = p2.x - p1.x
let dy = p2.y - p1.y
let d = 2 * dy - dx
let y = p1.y
for (let x = p1.x; x <= p2.x; x++) {
let point = Point.new(x, y)
this.drawPoint(point, color)
if (d > 0) {
y++
d -= 2 * dx
}
d += 2 * dy
}
}
中点画线算法的思路是从线段的起点开始,逐个计算每个像素点的位置,并绘制该像素点。在计算每个像素点的位置时,算法会根据当前像素点的位置和线段的斜率,来判断下一个像素点的位置。具体来说,算法会计算出一个叫做决策参数的值,根据这个值的正负来判断下一个像素点的位置。如果决策参数大于 0,那么下一个像素点的 y 坐标会加 1,否则不变。
中点画线算法的优点是实现简单,且可以在不使用浮点数的情况下,实现高精度的线段绘制。缺点是在某些情况下,可能会出现锯齿状的效果。如果你想要更高质量的线段绘制,可以尝试其他算法,比如贝塞尔曲线或者样条曲线。
【编译原理】
编译程序
-
源程序:这是编程语言的原始代码,例如C、Python等。它是程序员编写的高级语言代码。
-
词法分析:在这个阶段,源程序被分解为词法单元(tokens),例如关键字、标识符、操作符等。词法分析器扫描源代码并根据语言规则将其划分为这些词法单元。
-
语法分析(AST):在这个阶段,词法单元被组织成语法结构。语法分析器使用语法规则将词法单元组合成语法树,也称为抽象语法树(AST)。AST表示了源代码的结构和语义信息。
-
中间语言:在某些编译器中,可以使用中间语言作为源代码和目标代码之间的中间表示。中间语言是一种抽象层级的表示,可以在此层级上进行优化和转换。根据你的描述,你使用了自定义的汇编语法作为中间语言。
-
汇编:在这个阶段,中间语言被翻译成目标机器的汇编代码。汇编器将中间语言指令转换为特定机器架构的汇编指令,这些指令是机器语言的一种表示形式。
-
目标代码:最后,汇编代码被汇编器转换成机器语言的二进制表示形式,形成目标代码。目标代码是可以在特定硬件上执行的机器指令序列。
需要注意的是,编译过程的具体实现可能因编译器和目标平台的不同而有所不同。上述流程描述了一般的编译过程,但在实际中可能会有更多的步骤和优化技术涉及。
以下示例是简化的,并用伪代码表示,以便更清楚地演示每个阶段的功能。
- 源程序:
int a = 1;
int b = 2;
int c = a + b;
- 词法分析:
Token: "int"
Token: "a"
Token: "="
Token: "1"
Token: ";"
Token: "int"
Token: "b"
Token: "="
Token: "2"
Token: ";"
Token: "int"
Token: "c"
Token: "="
Token: "a"
Token: "+"
Token: "b"
Token: ";"
- 语法分析(AST):
Program
├── Declaration: int a = 1
├── Declaration: int b = 2
└── Declaration: int c = Expression
├── Identifier: a
├── Operator: +
└── Identifier: b
- 中间语言(自定义汇编语法):
[set a 1]
[set b 2]
[set c [+ a b]]
- 汇编:
00000000 ; set指令
00010000 ; 寄存器a
00000001 ; 数字 1
00000000 ; set指令
00100000 ; 寄存器b
00000010 ; 数字 2
00000000 ; set指令
00110000 ; 寄存器c
00000001 ; 加法指令(用于将寄存器a和寄存器b相加)
00010000 ; 寄存器a
00100000 ; 寄存器b
- 目标代码:
0110011000000001 ; 将数字1存储到寄存器a
0110011000100010 ; 将数字2存储到寄存器b
0110011001000001 ; 将寄存器a和寄存器b相加,并将结果存储在寄存器c
这些示例代码展示了从源程序到目标代码的每个阶段的具体实现。请注意,中间语言和汇编代码示例是基于你之前提供的自定义汇编语法的解释。在实际编译器中,具体的实现细节和代码表示形式可能会有所不同。
【bilibili那个顶部的图片】
我看了是用了img + transform 做的 但是他做的好丝滑
看了代码 感觉都会写, 但是就是实现出来的版本没他们那么丝滑舒服
他那个背后自动旋转的风车
有两个 一大一小
看了半天才发现是鼠标放上去左右划能动
视频都可以控制风车的转动欸
这个风车是随着鼠标左右移动顺时针或者逆时针滚动的
改变video的current time
直接改播放时间好像就变成 顺时针或者逆时针了
面老说:
直接视频里面录一个顺时针 一个逆时针
外面的图层人物随鼠标移动而移动的 github以前也有,网上有 demo,或者问 gpt
景深就是后面图层移动慢 前面图层移动快
所以要整成不同的layer
计算鼠标坐标到图片中心点距离 ,同时也能获得鼠标移动方向 ,鼠标往左 各个图层向左位移
然后第一个图层 移动距离*1 第二个图层 0.8 第三个图层0.6
b站例子里面 最底的几个图层(叶子) 不位移 但根据计算出来的位移值执行对应操作:控制视频播放
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.