法务+编码味道+链式调用+Rust生命周期
本帖最后由 你有种再说一遍 于 2024-9-13 08:41 编辑法务+编码味道+链式调用+Rust生命周期
# 法务
有时候很讨厌几家科技大公司的函数名不一样,细查之后,发现它们多数是因为法务原因而避让.
之前Sum(甲骨文)公司把Google起诉了,理由是安卓用了JavaSE API,直接复制了0.4%的声明代码,后来裁决了API相同并不构成版权问题,Sum败诉.
美国是同时存在判例法和阐释指定法,在没有判例之前是不存在指导案例的,所以这就考验了对于律师和法官的演绎推理能力了.
此外还有一种叫净室开发的方式来绕过侵权问题,即提供相同功能,也不构成侵犯知识产权,但是可能没有专利权.
净室开发是第一组人员进行功能分析,然后告诉第二组人员进行开发,使得代码层面自然就不一样了.获取的方式当然要合法啦,而且获取方式居然可以是已有产品API说明书.
# 编码味道
有时候编码的味道是很重要的,
像avlisp就缺少很多味道,动不动就遍历啦,没有命名空间啦,跟写汇编和c语言似的,每个指令都记忆,关键是没有函数提示...
而c++/c#语言的class.Method()可以在.的位置进行提示然后自动补全,这样编辑器就很好做.
那么有没有为了IDE写一个语言呢?
真有,那就是Rust.大神Linus让写驱动层的过渡去Rust,内核层则不然.
Java很多地方都map map的,因为它是一个很重要的味道.
大家有没有想过,结构和map其实是一回事呢?
你想想class.A和struct.A和map["A"]它们不就是一个占用空间和寻址的区别而已嘛?本质上都是KeyValue.
那么寻址足够快,map是不是相当于一个动态的结构体了,这就是为什么Java要优化这个底层,采用位运算指令周期少,而取余的周期高.
class和struct:
http://bbs.mjtd.com/thread-191057-1-1.html
JDK1.8的hashmap:
http://bbs.mjtd.com/thread-190705-1-1.html
# 链式调用
Google三驾马车之一的MapReduce模式多出名,它在js代码示例如下:
```JavaScript
const array = ;
const result = array
.map(x => x * 2) //每个元素乘2
.filter(x => x > 2) //过滤,获取大于2的元素
.reduce((acc, x) => acc + x); //聚合,求和
```
c#利用linq实现,函数名有点不同:
```csharp
var array = new List<int> { 1, 2, 3, 4 };
int result = array
.Select(x => x * 2) //每个元素乘2
.Where(x => x > 2) //过滤,获取大于2的元素
.Aggregate(0,(acc,x)=>acc+x); //聚合,求和
```
js的reduce==c#linq的Aggregate,
但是推荐c#末尾直接用.Sum(),因为Sum/Average/Max/Min这些函数在net8.0自带SIMD.
注意不是Sum(x => x + 1)这种选择器模式,不然可能没有SIMD的(看了源码https://segmentfault.com/a/1190000042837291真没有,不知道存不存在通过编译器优化,然后函数内联之类,大概率不会,毕竟没那么智能)
所以会简化为:
```c#
var array = new List<int> { 1, 2, 3, 4 };
int result = array
.Select(x => x * 2) //每个元素乘2
.Where(x => x > 2) //过滤,获取大于2的元素
.Sum(); //全部求和
```
js和c#差异是:
在js的map和filter方法会立即创建新的数组,而c#linq仅仅是表达式树,所以就可以看得出来它们设计哲学的不同.
js占用运行内存高,并且中间对象多,大量的数组会进入GC回收,它强调是不可变性和纯函数.
c#在大数据上面比较快,它少了很多中间对象,不会引发cpu boom.java的stream也是如此.
编译器生成的是一系列条件语句进行链式判断,此处是伪代码示意:
```txt
c#生成表达式树示意:
public int MapReduce(int[] array) {
int num = 0;
for(int j = 0; j < array.Length; j++){
int x = array*2;
if(x > 2) { num += x}
}
return num;
}
js编译器生成示意:
public int MapReduce(int[] array) {
List<int> s1 = new();
for(int j = 0; j < array.Length; j++) {
s1.Add(array*2);
}
// s1将从动态数组转为不可变数组传递下去
List<int> s2 = new();
for(int k = 0; j < s1.Count; k++) {
if( s1>2 ){ s2.Add(s1); }
}
// s2将从动态数组转为不可变数组传递下去
int num = 0;
for(int m = 0; j < s2.Count; m++) {
num += s2;
}
return num;
}
```
你们想知道链式调用怎么做的吗?
```csharp
// 定义一个类
class Test {
int _a = 0;
public Test Add(int num) {
_a += num;
return this;//返回自身实现链式调用
}
public void Print() {
Console.WriteLine(_a);
}
}
// 调用:
var t = new Test();
t.Add(1).Add(2).Add(99).Print();
```
# Rust特性
## 引用和指针
C++结构体和类这样传参,写拷贝副本的.
C#倒是结构体拷贝副本,类不拷贝.
```cpp
class MyClass {
//..
};
void function(MyClass obj) {
// 'obj' 是 'MyClass' 对象的一个副本
}
```
所以需要这样:
1.0 通过引用传递Pass by Reference:
```cpp
void function(MyClass& obj) {
// 'obj' 是对原始对象的引用
}
// 调用:
MyClass myObject;
function(myObject); // 传引用
```
2.0 通过指针传递Pass by Pointer:
```cpp
void function(MyClass* obj) {
// 'obj' 是指向原始对象的指针
}
// 调用:
MyClass myObject;
function(&myObject); // 传指针
```
C/C++经常说,引用是指针的别名,那么你会感觉"既生瑜何生亮",如果总是感觉它们一样,那可能是接触面太少,领悟的规则不够多.
学习Rust可以让你深入了解引用和指针的区别.
Rust是一种"过作用域必然释放"的语言.
它诞生了所有权转移机制,无论如何你都要具备所有权的知识,其中最主要是:变量的不可变性/生命周期/借用规则.
通过生命周期标注对齐生命长度,生命周期是为了避免悬垂引用,从而释放内存.
编译器做巨量的工作在编码阶段就检查代码了,即使你不会也必须要会了才能写出来,不然编译不通过.
## 简单比较
在C#中生命周期是淡化的,只发生在大括号.
```csharp
public static void main1 () {
{ int a = 1; }
int b = a;//报错,无法访问
}
public static void main2 () {
string s = "string";
string r = "";
{ r = s; }
Print(r);//顺利赋值并打印
}
```
同样例子,所有权转移,这里会产生报错:
错误例子1:悬垂引用错误
```rust
fn main() {
let s = String::from("hello");
let r;
{
let t = &s;// 借用,不报错
r = t; // 引用赋值(非copy类型-数值类型),产生所有权转移
} // 悬垂引用: 超出作用域,丢弃了t,之后r就成为悬垂引用,相当于一个尸体,没有装入灵魂.
println!("Length of `r`: {}", r.len()); // 这里尝试使用r,将会导致编译错误.
}
```
怎么改才是对的呢?
```rust
// 方案1: 初始化后赋值
// 这样打印出来是对的,就是充满垃圾代码...并且得到一系列警告(不是错误).
fn main() {
let s = String::from("hello");
let ra = String::from("xxx"); // 初始化
let mut r = &ra; // 借用
{
let t = &s;// 借用
r = t; // 赋值,对s的引用
}
println!("Length of `r1`: {}", r.len());
}
// 方案2: 套一层函数返回引用,多了一层栈帧,但是可以通过内联进行消除.
fn main() {
let s = String::from("hello");
let r = get_reference(&s);
println!("Length of `r2`: {}", r.len());
}
# // 积极内联
// 拒绝内联是#
fn get_reference(s: &String) -> &String {
let t = s;
t
}
// 方案3: 通过克隆副本,多了内存开销.
fn main() {
let s = String::from("hello");
let r;
{
let t = &s;
r = t.clone();
}
println!("Length of `r3`: {}", r.len());
}
// 方案4: 可变引用
fn main() {
let mut s = String::from("hello");
{
let r = &mut s;// 可变借用
println!("Length of `s41`: {}", r.len());// 可以读取长度
r.push('k');// 可以改变长度,因为r是可变引用
}// r 离开作用域
println!("Length of `s42`: {}", s.len());// 可以读取长度
}
// 接着才是绕过Rust的安全检查机制:指针
// 方案5:
fn main() {
let s = String::from("hello");
let p: *const String = &s;
// 如果你尝试解引用裸指针,就需要使用 unsafe 块
unsafe {
println!("Length of `s5`: {}", (*p).len());
}
}
// 方案6:
使用std::mem::forget函数可以用来防止值的自动清理,这在某些情况下可以用来避免悬垂引用,但这种做法非常危险,因为它可能会导致内存泄漏.
use std::mem;
fn main() {
let s = String::from("hello");
let p = &s as *const String;
// 这里使用 unsafe 块来解引用裸指针,并且使用 forget 来防止 s 被自动清理
mem::forget(s);
unsafe {
println!("Length of `s6`: {}", (*p).len());
}
}
```
## 生命周期长度标注
例子1:
str是字面量,&它是引用类型,函数只有一个参数,那么编译器可以自动推导生命周期:
```rust
fn print_book_title(book: &str) {
println!("书名是:{}", book);
}
fn main() {
let book_title = "Rust程序设计语言";
print_book_title(book_title);
}
```
例子2:
函数有2+参数就不行,因为存在生命长度不一,不知道怎么释放,要用生命周期符号约束起来,包括返回值:
```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let string1 = String::from("long string");
{ // 短的作用域
let result = longest(string1.as_str(), "short");
println!("The longest string is {}", result);
}
}
```
例子3:
结构体字段的引用也要保证声明周期一致.
```rust
// 结构体,
// 约束两个字段生命周期为'a,表示共同长度
struct Book<'a> {
title: &'a str,
author: &'a str,
}
// 定义一个函数
// 参数接收Book实例和生命周期参数'a
// 返回字符串:书名和作者名,它不是引用所以不标注生命周期
fn describe_book<'a>(book: &'a Book<'a>) -> String {
// 末尾最后一行自动返回参数
format!("'{}' by {}", book.title, book.author)
}
fn main() {
// 栈帧字符串字面量,作为Book实例的字段.
let title = "标题";
let author = "作者";
// 栈帧创建一个Book实例
let book = Book { title: title, author: author };
// 调用函数并打印返回的描述
println!("{}", describe_book(&book));
}
```
这种例子太多了,大家可以自行学习
(完) 感谢惊惊大佬的分享,在整理整理可以出书了:victory: 跟大佬学习到了!
页:
[1]