diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b228509..9b027607 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -337,6 +337,7 @@ jobs: Steve Hartwell 南宫乘风 砹小翼 +h7ml Alex LufsX baoer @@ -349,7 +350,6 @@ jobs: 秋 雨落 Blossom 萌新杰少 -h7ml chaos YuRuiH Willxup diff --git a/README.md b/README.md index 2eb52f98..3b8d61da 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Quick Reference [Dockerfile](./docs/dockerfile.md) [Django](./docs/django.md) [Elixir](./docs/elixir.md) +[Erlang](./docs/erlang.md) [Flask](./docs/flask.md) [FastAPI](./docs/fastapi.md) [Flutter](./docs/flutter.md) @@ -77,7 +78,7 @@ Quick Reference [Scala](./docs/scala.md) [Swift](./docs/swift.md) [SwiftUI](./docs/swiftui.md) -[Spring Boot](./docs/springboot.md) +[Spring Boot](./docs/springboot.md) [Lua](./docs/lua.md) [Pytorch](./docs/pytorch.md) @@ -100,6 +101,7 @@ Quick Reference [JavaScript](./docs/javascript.md) [jQuery](./docs/jquery.md) [Next.js](./docs/nextjs.md) +[RxJS](./docs/rxjs.md) [React](./docs/react.md) [React Router](./docs/reactrouter.md) [React Native](./docs/react-native.md) @@ -284,6 +286,7 @@ Quick Reference Steve Hartwell 南宫乘风 砹小翼 +h7ml Alex LufsX baoer @@ -296,7 +299,6 @@ Quick Reference 秋 雨落 Blossom 萌新杰少 -h7ml chaos YuRuiH Willxup diff --git a/assets/erlang.svg b/assets/erlang.svg new file mode 100644 index 00000000..a4b941c2 --- /dev/null +++ b/assets/erlang.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/rxjs.svg b/assets/rxjs.svg new file mode 100644 index 00000000..8cde0f51 --- /dev/null +++ b/assets/rxjs.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/erlang.md b/docs/erlang.md new file mode 100644 index 00000000..26c473f7 --- /dev/null +++ b/docs/erlang.md @@ -0,0 +1,450 @@ +Erlang 备忘清单 +=== + +[Erlang](https://www.erlang.org/) 是一种用于构建并发、分布式和容错系统的编程语言。以下是一些常用的命令和操作。 + +入门 +--- + +### 启动 Erlang Shell + +```shell +erl +``` + +### 编译代码 + + +```shell +# 在 Erlang Shell 中编译 +c(module). +# 在命令行中编译 +erlc module.erl +``` + +### 运行代码 + + +```shell +# 在 Erlang Shell 中运行 +module:function(). +# 从命令行运行 +erl -noshell -s module function -s init stop +``` + +### 退出 Erlang Shell + +```shell +q(). +``` + +代码结构 +--- + +### 模块定义 + +```erlang +-module(module_name). +-export([function_name/arity, ...]). + +function_name(Args) -> + % Function body. + Result. +``` + +### 导出函数 + +```erlang +-export([function1/0, function2/1]). +``` + +### 注释 + +```erlang +% 单行注释 +% 这是一个注释 +``` + +### 变量 + +```erlang +VarName = Value. % 变量名必须以大写字母开头 +Age = 25. +Name = "Alice". +``` + +数据类型 +--- + +### 原子 + +```erlang +atom. % 例子:atom, 'Atom with spaces' +``` + +### 数字 + +```erlang +123. % 整数 +3.14. % 浮点数 +``` + +### 布尔值 + +```erlang +true. +false. +``` + +### 字符串 + +```erlang +"Hello, World!". +``` + +### 元组 + +```erlang +{ok, "Success"}. +``` + +### 列表 + +```erlang +[1, 2, 3]. +[H|T] = [1, 2, 3]. % H = 1, T = [2, 3] +``` + +### 字典 (Map) + +```erlang +#{key1 => value1, key2 => value2}. +``` + +控制结构 +--- + +### 条件语句 + +```erlang +if + Condition1 -> Expression1; + Condition2 -> Expression2; + true -> DefaultExpression +end. +``` + +### case 表达式 + +```erlang +case Expression of + Pattern1 -> Expression1; + Pattern2 -> Expression2; + _ -> DefaultExpression +end. +``` + +### 函数定义 + +```erlang +% 无参函数 +my_function() -> + ok. + +% 有参函数 +add(A, B) -> + A + B. +``` + +列表操作 +--- + +### 列表生成 + +```erlang +% 生成 1 到 10 的列表 +[ X || X <- lists:seq(1, 10)]. + +% 生成 1 到 10 中的偶数 +[ X || X <- lists:seq(1, 10), X rem 2 == 0]. +``` + +并发 +--- + +### 启动进程 + +```erlang +spawn(Module, Function, Args). + +% 示例 +Pid = spawn(fun() -> io:format("Hello from process~n") end). +``` + +### 发送消息 + +```erlang +Pid ! Message. + +% 示例 +Pid ! {hello, self()}. +``` + +### 接收消息 + +```erlang +receive + Pattern1 -> Expression1; + Pattern2 -> Expression2; + after Timeout -> TimeoutExpression +end. +``` + +### 模式匹配 + +```erlang +{ok, Value} = {ok, 42}. +``` + +常用内置函数 (BIFs) +--- + +### 列表操作 + +```erlang +lists:append(List1, List2). +lists:map(Function, List). +lists:filter(Function, List). +lists:foldl(Function, Acc, List). +``` + +### 元组操作 + +```erlang +element(N, Tuple). +setelement(N, Tuple, Value). +tuple_size(Tuple). +``` + +### 字符串操作 + +```erlang +string:len(String). +string:concat(String1, String2). +string:tokens(String, Delimiters). +``` + +### 文件操作 + +```erlang +file:read_file(Filename). +file:write_file(Filename, Data). +file:delete(Filename). +``` + +### 列表操作 + +```erlang +lists:map(fun(X) -> X * 2 end, [1, 2, 3]). +lists:filter(fun(X) -> X rem 2 == 0 end, [1, 2, 3, 4]). +``` + +### 字符串操作 + +```erlang +string:len("Hello"). +string:upper("hello"). +``` + +### 文件操作 + +```erlang +{ok, File} = file:open("test.txt", [write]). +file:write(File, "Hello, file!"). +file:close(File). +``` + +### 示例:简单的服务器 + +```erlang +-module(server). +-export([start/0, loop/0]). + +start() -> + spawn(fun loop/0). + +loop() -> + receive + {echo, Msg} -> + io:format("Echo: ~p~n", [Msg]), + loop(); + stop -> + io:format("Server stopping~n"), + ok; + _ -> + io:format("Unknown message~n"), + loop() + end. +``` + +并发编程 +--- + +### 创建进程 + +```erlang +Pid = spawn(Module, Function, Args). +``` + +### 发送消息 + +```erlang +Pid ! Message. +``` + +### 接收消息 + +```erlang +receive + Pattern1 -> Actions1; + Pattern2 -> Actions2; + ... +end. +``` + +### 链接进程 + +```erlang +link(Pid). +unlink(Pid). +``` + +### 监控进程 + +```erlang +MonitorRef = erlang:monitor(process, Pid). +erlang:demonitor(MonitorRef). +``` + +错误处理 +--- + +### 捕获异常 + +```erlang +try Expression of + Pattern -> Result +catch + Class:Reason -> Handler +end. +``` + +### 常见异常类型 + +- `throw` +- `error` +- `exit` + +### 错误处理 + +```erlang +try Expression of + Pattern -> Result +catch + Type:Reason -> ErrorHandlingExpression +end. +``` + +分布式编程 +--- + +### 启动分布式节点 + +```shell +erl -name nodename@hostname -setcookie Cookie +``` + +### 连接节点 + +```erlang +net_adm:ping(Node). +``` + +### 发送消息到远程节点 + +```erlang +{remote_process, 'remote_node@host'} ! Message. +``` + +OTP 框架 +--- + +### 定义 GenServer + +```erlang +-module(my_gen_server). +-behaviour(gen_server). + +-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + {ok, #state{}}. + +handle_call(Request, From, State) -> + {reply, Reply, State}. + +handle_cast(Msg, State) -> + {noreply, State}. + +handle_info(Info, State) -> + {noreply, State}. + +terminate(Reason, State) -> + ok. + +code_change(OldVsn, State, Extra) -> + {ok, State}. +``` + +### 使用 GenServer + +```erlang +gen_server:start_link({local, Name}, Module, Args, Options). +gen_server:call(ServerRef, Request). +gen_server:cast(ServerRef, Msg). +``` + +测试 +--- + +### 编写 EUnit 测试 + +```erlang +-module(module_name_tests). +-include_lib("eunit/include/eunit.hrl"). + +simple_test() -> + ?assertEqual(Expected, Actual). + +complex_test_() -> + [ + {"Test case 1", ?_assertEqual(Expected1, Actual1)}, + {"Test case 2", ?_assertEqual(Expected2, Actual2)} + ]. +``` + +### 运行 EUnit 测试 + +```shell +# 在命令行中运行 +erl -eval "eunit:test(module_name)" -s init stop +``` + +另见 +--- + +- [Erlang 官方文档](https://www.erlang.org/docs) +- [Erlang 编程书籍](https://www.erlang.org/books) diff --git a/docs/rxjs.md b/docs/rxjs.md new file mode 100644 index 00000000..d19ec882 --- /dev/null +++ b/docs/rxjs.md @@ -0,0 +1,419 @@ +RxJS 备忘清单 +=== + +[RxJS](https://rxjs.dev/)(Reactive Extensions for JavaScript)是一个强大的库,用于处理异步事件和数据流。以下是 RxJS 的一些关键概念、操作符和方法的总结。 + +## 入门 + +### 安装 RxJS + +```bash +npm install rxjs +``` + +### 清晰 + + +RxJS 的强大之处在于它使用纯函数生成值的能力。这意味着您的代码更不容易出错。通常情况下,您会创建一个不纯的函数,而代码的其他部分可能会弄乱您的状态。 + +```js +let count = 0; +document.addEventListener('click', () => { + console.log(`Clicked ${++count} times`) +}); +``` + +使用 RxJS 可以隔离状态。 + +```js +import { fromEvent, scan } from 'rxjs'; + +fromEvent(document, 'click') + .pipe(scan((count) => count + 1, 0)) + .subscribe((count) => { + console.log(`Clicked ${count} times`) + }); +``` + +扫描操作符的工作原理与数组的 `reduce` 类似。它接受一个暴露给回调函数的值。回调的返回值将成为下次回调运行时公开的下一个值。 + +### 流 + + +RxJS 拥有一整套运算符,可以帮助您控制事件如何流经您的可观察对象。这是使用纯 JavaScript 每秒最多允许一次点击的方式: + +```js +let count = 0; +let rate = 1000; +let lastClick = Date.now() - rate; +document.addEventListener('click', () => { + if (Date.now() - lastClick >= rate) { + console.log(`Clicked ${++count}times`); + lastClick = Date.now(); + } +}); +``` + +使用 RxJS: + +```js +import { fromEvent, throttleTime, scan + } from 'rxjs'; + +fromEvent(document, 'click') + .pipe(throttleTime(1000), + scan((count) => count + 1, 0) + ) + .subscribe((count) => { + console.log(`Clicked ${count} times`) + }); +``` + +### 第一个示例 + +通常情况下,您需要注册事件监听器。 + +```js +document.addEventListener('click', () => { + console.log('Clicked!') +}); +``` + +使用 [RxJS](https://rxjs.dev/),您可以创建一个可观察对象。 + +```js +import { fromEvent } from 'rxjs'; + +fromEvent(document, 'click') + .subscribe(() => { + console.log('Clicked!') + }); +``` + +### 导入所需的 Observable 和操作符 + + +```javascript +import { Observable, of, from, interval, fromEvent } from 'rxjs'; + +import { map, filter, switchMap, mergeMap, catchError, debounceTime, + distinctUntilChanged, take, tap, concatMap, delay, retryWhen, scan, + combineLatest, concat, merge, forkJoin, withLatestFrom, startWith, reduce +} from 'rxjs/operators'; +``` + +## 创建 Observable + +### of + +```javascript +const obs = of(1, 2, 3); +obs.subscribe(console.log); +// 输出: 1 2 3 +``` + +创建一个立即发送指定值并完成的 Observable + +### from + +```javascript +const obs = from([1, 2, 3]); +obs.subscribe(console.log); +// 输出: 1 2 3 +``` + +从 Promise、数组、可迭代对象创建 Observable + +### interval + +```javascript +const obs = interval(1000); +obs.subscribe(console.log); +// 每秒输出一次递增的数字 +``` + +创建一个定时发送递增整数的 Observable + +### fromEvent + + +```javascript +const button = document.querySelector('button'); +const obs = fromEvent(button, 'click'); +obs.subscribe(event => { + console.log('Button clicked!', event) +}); +``` + +从 DOM 事件创建 Observable + +## 操作符 + +### map + +```javascript +const obs = of(1, 2, 3).pipe( + map(x => x * 2) +); +obs.subscribe(console.log); +// 输出: 2 4 6 +``` + +对 Observable 发出的每个值应用一个函数 + +### filter + +```javascript +const obs = of(1, 2, 3).pipe( + filter(x => x % 2 === 0) +); +obs.subscribe(console.log); +// 输出: 2 +``` + +过滤 Observable 发出的值 + +### switchMap + +```javascript +const obs = interval(1000).pipe( + switchMap(() => of('Hello')) +); + +obs.subscribe(console.log); +// 每秒输出一次 "Hello" +``` + +将 Observable 每个值映射成 Observable 并订阅,前一个订阅将被取消 + +### mergeMap + +```javascript +const obs = interval(1000).pipe( + mergeMap(() => of('Hello')) +); + +obs.subscribe(console.log); +// 每秒输出一次 "Hello" +``` + +类似 switchMap,但允许多个内部 Observable 并发执行 + +### catchError + +```javascript +const obs = of(1, 2, 3).pipe( + map(x => { + if (x === 2) throw 'Error!'; + return x; + }), + catchError(err => of('发现一个错误:'+err)) +); +obs.subscribe(console.log); +// 输出: 1 发现一个错误:Error! +``` + +捕获 Observable 链中的错误 + +### debounceTime + +```javascript +const obs = fromEvent(document,'mousemove') + .pipe(debounceTime(300)); + +obs.subscribe(event => { + console.log('Mouse moved!', event) +}); +``` + +延迟处理,直到源 Observable 停止发出数据一定时间 + +### distinctUntilChanged + +```javascript +const obs = of(1, 1, 2, 2, 3, 3).pipe( + distinctUntilChanged() +); +obs.subscribe(console.log); +// 输出: 1 2 3 +``` + +忽略连续重复的值 + +### take + +```javascript +const obs = interval(1000).pipe( + take(3) +); +obs.subscribe(console.log); +// 输出: 0 1 2 +``` + +只发出前 n 个值 + +## 组合操作符 + +### combineLatest + +```javascript +const obs1 = interval(1000); +const obs2 = of('A', 'B', 'C'); +const combined = combineLatest( + [obs1, obs2] +); + +combined.subscribe(console.log); +// 每秒输出一次两个 observables 的最新值 +``` + +当两个 Observable 都发出新的值时,发出它们的组合 + +### concat + +```javascript +const obs1 = of(1, 2, 3); +const obs2 = of(4, 5, 6); +const combined = concat(obs1, obs2); +combined.subscribe(console.log); +// 输出: 1 2 3 4 5 6 +``` + +按顺序连接多个 Observable + +### merge + +```javascript +const obs1 = interval(1000).pipe( + map(x => 'A' + x) +); +const obs2 = interval(500).pipe( + map(x => 'B' + x) +); +const combined = merge(obs1, obs2); +combined.subscribe(console.log); +// 每秒输出 "A" 和 "B" 开头的递增数字 +``` + +将多个 Observable 合并为一个 + +### forkJoin + +```javascript +const obs1 = of(1, 2, 3); +const obs2 = of('A', 'B', 'C'); +const combined = forkJoin([obs1, obs2]); +combined.subscribe(console.log); +// 输出: [3, 'C'] +``` + +等待所有 Observable 完成,然后发出它们的最后一个值的数组 + +## 错误处理 + +### retryWhen + + +```javascript +const obs = throwError('出了些问题!') + .pipe( + retryWhen(errors => + errors.pipe(delayWhen(() => interval(1000))) + ) + ); +obs.subscribe(console.log, console.error); +// 输出: 出了些问题! (每秒重试一次) +``` + +在 Observable 发出错误时重试 + +## 实用操作符 + +### tap + +```javascript +const obs = of(1, 2, 3).pipe( + tap(x => console.log(`Before: ${x}`)), + map(x => x * 2), + tap(x => console.log(`After: ${x}`)) +); +obs.subscribe(); +// 输出: Before: 1, After: 2, Before: 2, +// After: 4, Before: 3, After: 6 +``` + +用于记录、测量或执行副作用操作 + +### startWith + +```javascript +const obs = of(1, 2, 3).pipe( + startWith(0) +); +obs.subscribe(console.log); +// 输出: 0 1 2 3 +``` + +在 `Observable` 序列前添加值 + +### scan + +```javascript +const obs = of(1, 2, 3).pipe( + scan((acc, value) => acc + value, 0) +); +obs.subscribe(console.log); +// 输出: 1 3 6 +``` + +对 `Observable` 发出的每个值应用累加器函数 + +### reduce + + +```javascript +const obs = of(1, 2, 3).pipe(reduce((acc, value) => acc + value, 0)); +obs.subscribe(console.log); +// 输出: 6 +``` + +对 `Observable` 发出的值进行累加 + +### delay + +```javascript +const obs = of('Hello').pipe(delay(2000)); +obs.subscribe(console.log); +// 输出: 'Hello' (延迟2秒) +``` + +延迟 `Observable` 发出数据的时间 + +调度器 +--- + +### 调度器说明 + +调度器(Scheduler)控制着 RxJS 操作的执行时机。常见的调度器有: + +- `asyncScheduler`:异步执行任务 +- `queueScheduler`:按队列顺序执行任务 +- `animationFrameScheduler`:在浏览器的下一次重绘前执行任务 + +### 示例 + + +```javascript +const obs = of(1, 2, 3).pipe(observeOn(asyncScheduler)); + +console.log('Before subscribe'); +obs.subscribe(console.log); +console.log('After subscribe'); +// 输出: Before subscribe, After subscribe, 1, 2, 3 +``` + +## 另见 + +- [RxJS 官方文档](https://rxjs.dev/) +- [RxJS 学习资源](https://rxjs.dev/guide/overview) +- [RxJS 操作符参考](https://rxjs.dev/guide/operators)