#Kotlin 是好事,你看那 Deprecated 本来就应该换了(而且 Kotlin 的
至于 Null safety (Nullability) 更是 Kotlin 比 #Java 化腐朽为神奇的地方(当然,
它既然觉得
annotation class kotlin.Depreacted 还带了 IDE 自动替换(比如,val T.isUserAMonkey get() = … 能够换成 .isMonkeyUser 自动的;至于 Null safety (Nullability) 更是 Kotlin 比 #Java 化腐朽为神奇的地方(当然,
in/out 型变性声明而不是 ?super/?extends 类型上下限界也是一个优秀之处),这实在可以说是 Kotlin 最骄傲的地方之一。它既然觉得
str: String?, 你 str!! 一下就可以拿到 kotlin.String! 了(也可以用 ?. 安全 Null 传导),不过如果真的是 null 引用的话,就会抛出 KotlinNullPointerException val files: Array<File>? = … files.forEach { f -> f.close() } // Unsafe use of a nullable receiver of type Array<File!>?(fn as (String) -> *)(json["user_nickname"]) // Type mismatch: inferred type is String? but String was expected
duangsuse::Echo
#Kotlin 是好事,你看那 Deprecated 本来就应该换了(而且 Kotlin 的 annotation class kotlin.Depreacted 还带了 IDE 自动替换(比如,val T.isUserAMonkey get() = … 能够换成 .isMonkeyUser 自动的; 至于 Null safety (Nullability) 更是 Kotlin 比 #Java 化腐朽为神奇的地方(当然,in/out 型变性声明而不是 ?super/?extends 类型上下限界也是一个优秀之处),这实在可以说是…
你会爱上 Kotlin 的,尤其是:如果你写算法的话
Java 简直能把 C++ 写5行的代码写10行,而且还不如 Kotlin 可移植
我几乎不写 Java,更看不上 Groovy 那种辣鸡语言,JRuby 和 JPython 试过,不太喜欢。不过 Scala 我是不常用,Eta(Haskell for the JVM) 的 IDEA 支持不好
Groovy 怎么辣鸡呢?IDEA 2019 打开你的 Groovy (我只用 Gradle)脚本看看,你会发现一堆 type error
这 TMD 绝对不是 JetBrains 人懒的锅。
谁叫你弄个什么鬼
还有(好像是 Gradle
脚本语言随便起来是可以很随便的,比如那个
有些完全是『工程派』的人是不了解这些事情的,他们只是觉得写得越少/多越好,嗯…… 所以才有了类似 Ant XML、SOAP("Simple" Object Accessing Protocol) 这种东西。
他们写的时候就只会以一种思路、一种视角看一个示例程序甚至完全不看,就是想当然地设计,设计出类似 Java 的 type wildcard 的东西(当然这个还是比较保守的),因为不管你在某些方面再好,也总有自己还没学过的方面。
有些人只看着所谓的测试文档覆盖率,所谓的持续集成,所谓的工程标准。
可是他们注意力的重点,几时才会放到自己要实现的东西本身上来呢?
再看看某些所谓的中文编程语言,他们到底在做个什么呢?老祖宗的好东西全给糟蹋了,中文了你还用逗号切参数列表,中文了你还把“”拿来和""一起都记字符串,中文你还管 boolean 叫布耳,中文你还把 break; continue 翻译成『打断』『继续』,翻译个驴唇不对马嘴,不够用心。
Java 简直能把 C++ 写5行的代码写10行,而且还不如 Kotlin 可移植
我几乎不写 Java,更看不上 Groovy 那种辣鸡语言,JRuby 和 JPython 试过,不太喜欢。不过 Scala 我是不常用,Eta(Haskell for the JVM) 的 IDEA 支持不好
Groovy 怎么辣鸡呢?IDEA 2019 打开你的 Groovy (我只用 Gradle)脚本看看,你会发现一堆 type error
这 TMD 绝对不是 JetBrains 人懒的锅。
import static System.out
def doge = "🐕"
out.println("Emmm ${doge}.") // can't be applied to GString 谁叫你弄个什么鬼
GString 的???难道你要惰性求值???还有(好像是 Gradle
Action<in T> 专属的),谁告诉你 it 可以被隐式作为 this 的?['1','2','3'].map { println(replace('1','')) }
为什么 def 也可以用来不带参数元组的定义?你们的作用域是怎么做的?为什么给人感觉那么奇怪?脚本语言就可以如此随便?脚本语言随便起来是可以很随便的,比如那个
0+'1' != '1'+0、'0'==0 的 JavaScript。有些完全是『工程派』的人是不了解这些事情的,他们只是觉得写得越少/多越好,嗯…… 所以才有了类似 Ant XML、SOAP("Simple" Object Accessing Protocol) 这种东西。
他们写的时候就只会以一种思路、一种视角看一个示例程序甚至完全不看,就是想当然地设计,设计出类似 Java 的 type wildcard 的东西(当然这个还是比较保守的),因为不管你在某些方面再好,也总有自己还没学过的方面。
有些人只看着所谓的测试文档覆盖率,所谓的持续集成,所谓的工程标准。
可是他们注意力的重点,几时才会放到自己要实现的东西本身上来呢?
再看看某些所谓的中文编程语言,他们到底在做个什么呢?老祖宗的好东西全给糟蹋了,中文了你还用逗号切参数列表,中文了你还把“”拿来和""一起都记字符串,中文你还管 boolean 叫布耳,中文你还把 break; continue 翻译成『打断』『继续』,翻译个驴唇不对马嘴,不够用心。
对所有编程语言里语言是中文的,若它属辣鸡设计,
锤爆(它)Forwarded from dnaugsuz
…… 你没有提到
其次有一个不容易被注意到的细节:
Kotlin 傻逼了!
你试试
return 有个副作用是下面的代码都别想执行了,那当然是没这个问题了其次有一个不容易被注意到的细节:
?: return 和 ?: throw 居然是一样的,都 TM 是普通版文法Kotlin 傻逼了!
return 怎么可以是表达式?你试试
fun op() { println(null ?: return as Nothing) }Forwarded from dnaugsuz
iseki 觉得
我觉得好奇怪啊
return 能够和 throw 一样被作为表达式吗 🤔fun op() { do { println(null ?: continue as Nothing) } while(true) } 我觉得好奇怪啊
Forwarded from dnaugsuz
我觉得不行,这样容易混淆……
我觉得可以提供语法糖,但不能是表达式
我很讨厌
我觉得可以提供语法糖,但不能是表达式
我很讨厌
fun op() { do { println(listOf(return, continue, break, throw Exception())) } while(false) } 可以解析甚至执行的情况Forwarded from dnaugsuz
我还是无法接受
尽管类型层面我们可以检查出 println 不能被执行
continue 可以被作为参数的 case……println(continue) 我简直发毛尽管类型层面我们可以检查出 println 不能被执行
Forwarded from iseki
不能编译啊,你怎么可以println(Nothing)呢
Forwarded from dnaugsuz
我是这样想的,如果没有足够的理由,『控制流』表达式我要有所取舍
比如说,一些能够不作为表达式的,return 什么的就专门提供语法糖
比如说,一些能够不作为表达式的,return 什么的就专门提供语法糖
Forwarded from dnaugsuz
这个问题我可以到时候弄出我设计那个语言第一个编译器的时候再找其他人谈谈,
之前我没考虑过
我之前以为 Kotlin 是不允许这样,但我得慢慢去想,举出实际使用例子来
其实这也不是没好处,好像是可以放在 block(closure) 里面的,不过我也不是很确定。
我打算设计的这门语言叫『绝句』,这里有一个简单的例子:
一个细节是,它在类型层面建模了 Kotlin 没建模的 Exceptions,这个建模被称为『无常类型检查』
之前我没考虑过
break 什么的类型级别可以是 Noting 这件事……我之前以为 Kotlin 是不允许这样,但我得慢慢去想,举出实际使用例子来
其实这也不是没好处,好像是可以放在 block(closure) 里面的,不过我也不是很确定。
我打算设计的这门语言叫『绝句』,这里有一个简单的例子:
引记法 绝句.区间.数域 (投、换)记得我当初设计的时候(重写是后来设计的一个特性)重写被弄成了个值是『断止』类型的表达式 🤔
对何<项>皆有
括物(我: 行<项>) 为
私下、尾递归的事 二分查找(你: 项、区: 数域): 引数? 为
量 中针 = 区的中值
判我[中针],
是你,回中针
大你,重写(区=区投末,中针前。) “逗号文法 确保(一行(1、2、3).全是,它 不小 一。)”
“二分查找(你, 区.mapBegin { 中针.dec() })”
小你,重写(区=区换始(中针))
“编译器知道没有其他情况”
一个细节是,它在类型层面建模了 Kotlin 没建模的 Exceptions,这个建模被称为『无常类型检查』
//!语者 无例之物比如说,假设有
//!语者 此即存储
对何<值者>皆有
密封的内联物 无常 为
私下的内联物 断止: 无常<*>()
私下的内联物 决常(量 你: 值者): 无常<值者>() 为
算符的事 此即值() = 你
“大致是这样吧,注意这是编译器的 intrinsics”
事 读一行(): 无常<文>
尝试,
量 输入行 取者,读一行()。 “内联类没有『我』分配,无法作为通常值存储”
“或者,你也可以写 量 输入行 = 读一行()作决常”
输出 写 输入行 “编译器知道『输入行』的「推帧单位」和赋值语句的是一个,且这一句能执行必定代表无异常发生,智能转型。”
接迎 文件结束不常成『异事』,异事去写出()。 “『标识符』是绝句的一种文法”
“当然关键字<接迎>后不加空格也可以”
终焉,
“这只是个例子”
Forwarded from dnaugsuz
你这指定的好像是类型上界
有时候我觉得类型上、下限好理解些,一个「界」字我觉得断言的不够明显,让人困惑(虽然是通用说法)
<T: Any> capture<T extends "Any">?有时候我觉得类型上、下限好理解些,一个「界」字我觉得断言的不够明显,让人困惑(虽然是通用说法)
Forwarded from dnaugsuz
就是类型检查啊,Java 都是泛型擦除的实现,Kotlin 有强十倍的类型系统你还璇它……
Java 早些时候(1.5的时候才加的泛型)没泛型才这么干,到处瞎转转
现在 Kotlin 有了,你不多看点 Kotlin 最佳实践,还是先重新学学再说吧,Kotlin 不是 JDK stdlib 辣鸡。
而且什么叫做都是,链表也是么
Java 早些时候(1.5的时候才加的泛型)没泛型才这么干,到处瞎转转
现在 Kotlin 有了,你不多看点 Kotlin 最佳实践,还是先重新学学再说吧,Kotlin 不是 JDK stdlib 辣鸡。
而且什么叫做都是,链表也是么
Forwarded from dnaugsuz
我给你解释一下,拿一般的那一套,不说什么类型理论、集合不集合值不值的。
既然你已经知道 PECS(producer-extends, consumer-super) 原则什么的,就不说了。
Java 的泛型通配符是一种类型变量,
类型就是谁兼容谁的问题,CharSequence 兼容 String、Number 兼容 Integer, Long, Float,或者说兼容者是被兼容者的子类型(subtype)。
就是说任何操作
泛型加了个参数,不过这里就出了个问题,比如说,
其实我们谈的
所以大家都这么写:
所以为啥现在可以了,就是因为
所以说,为什么现在可以用了,就是因为
对于任意类型
为什么要有输入/输出两种情况,就是比较对象顺序上的差异(我也不知道为什么)
为什么要有型变性,是为了完整化类型系统多态(polymorphism)的代码复用
+ 如果有某个收某类型
+ 如果有某个返回某类型
然后 Kotlin 的
不过区间好像是包含两个比较的,
—
岂止是没区别,单就泛型看(这还不提 Kotlin 化腐朽为神奇的
那种雷轰感,没写过是不会明白的……
Kotlin 的 in/out 都是在类型变量声明处,此外对于 <A, B>
(因为如果允许,实际上意味着
成熟个毛线啊,Java 简直老掉牙了!Kotlin 写十行的代码 Java 不得不写 20 行,有些 Java 程序员能写 40 行,带上所谓的内联文档和注释和『完全覆盖』的测试!
和兼容 Java 没有关系,Java 对泛型兼容也主要是编译器上下工夫,类文件格式其实是没有变化,只是加了点元数据
就是这样
既然你已经知道 PECS(producer-extends, consumer-super) 原则什么的,就不说了。
Java 的泛型通配符是一种类型变量,
?extends A 配属 A (或者说『小于A』)的任意类型,?super A 配超 A(或者说『大于A』)的任意类型类型就是谁兼容谁的问题,CharSequence 兼容 String、Number 兼容 Integer, Long, Float,或者说兼容者是被兼容者的子类型(subtype)。
就是说任何操作
(CharSequence) -> * 都可以被 invoke("string instance")
哪怕是 xs[1], xs[2] 这种操作都一样(他们就是 Kotlin 的 operator fun get(index: Int): T) 嘛泛型加了个参数,不过这里就出了个问题,比如说,
interface Box<T> {
T get();
<R> R let(Function<T, R> f);
}
这里我们写点使用者角度的代码,void printNumber(Number num) { out.println(num); }
// Function<Number, Void> printNumber = out::println;
Box<Integer> ba = new ValueBox(1);
ba.let(::printNumber);
// Box<Integer>#let(Function<Integer, R>) is not acceptable for (Function<Number, Void>)其实我们谈的
Box<T> 就是 Kotlin 的 Box<in T>,因为它的类型参数只在「消费者位置」出现,你说对应 ?super XXX,很正确,但知道不等于理解,所以要再学。所以大家都这么写:
<R> R let(Function<? super T, R> f);然后对 Java8,it checks,很好可是对理解没帮助,我们应该看看
Function<T, R> 的定义:interface Function<T, R> { R apply(T in0); }
准确的说是 Function1: (T) -> R.所以为啥现在可以了,就是因为
Function<T, ...> 里的这个 T 成了 ?super Integer, 所以它关于它的类型参数 T 逆变(contra-variant)了(Scala 的 Function[-T], Kotlin 的 Function<in T> 或者 (T) -> *)。所以说,为什么现在可以用了,就是因为
对于任意类型
<R>,Box<Integer>#let(Function<?super Integer, R>) 可以收 Function<Number, R> 类型的实例了为什么要有输入/输出两种情况,就是比较对象顺序上的差异(我也不知道为什么)
为什么要有型变性,是为了完整化类型系统多态(polymorphism)的代码复用
+ 如果有某个收某类型
A 的操作 Op<A> { void op(A); }, 则对于所有的 Op<A0: ?super A> { void op(A0); } 它肯定能够 op((A) x),因为它连 A0(如 Number) 都能收自然能够收 A(如 Integer)+ 如果有某个返回某类型
T 的操作 Ret<T> { T get(); }, 则对于所有的 Ret<T1: ?extends T> { T1 get(); } 它肯定能够 (T)get(),因为它连 T 的子类型都能返回,为什么不能返回 T然后 Kotlin 的
* (就是说在某个「位置」被一切参数兼容,比如 Consumer<in *> 可以收 Consumer<Int>, Consumer<String>, ...) 在 in 的位置是 in Nothing, out 的位置是 out Any?
Nothing 是 Kotlin 里一切类型的子类(bottom type)、Any? 则是一切类型的超类,所以区间 Nothing..Any? 就代表可接受一切类型不过区间好像是包含两个比较的,
a<=x&&x<=b 代表 x in (a..b), 类型检查只用判一次超类链—
岂止是没区别,单就泛型看(这还不提 Kotlin 化腐朽为神奇的
T?, (?.) (?:) 操作)都是天壤之别呢,你去看那么多 Java 库,他们类似 Action<T>, Consumer<T> 这种都不得不对每个受参的方法写 Action<? super T> 那种雷轰感,没写过是不会明白的……
Kotlin 的 in/out 都是在类型变量声明处,此外对于 <A, B>
fun copyArray(dst: Array<A>, src: Array<B>) 为保证类型安全和代码复用可在 Array<...> 里填 out B, 它会禁止对 set(Int,B) 的访问(因为如果允许,实际上意味着
(ary as Array<Int>).set(Int, Number) 可以被使用,因为 src 可以是任何 Array<T> where T: Number,这是败坏类型安全性的)(给填 in A 也是可以的,效果一样)成熟个毛线啊,Java 简直老掉牙了!Kotlin 写十行的代码 Java 不得不写 20 行,有些 Java 程序员能写 40 行,带上所谓的内联文档和注释和『完全覆盖』的测试!
和兼容 Java 没有关系,Java 对泛型兼容也主要是编译器上下工夫,类文件格式其实是没有变化,只是加了点元数据
就是这样
Forwarded from dnaugsuz
所以,对你的方法大概是用
我猜你是这么想到这个问题的:Java 里你用的是
你是担心它有额外的性能开销?那就用
你说你能保证,这可以理解(因为编译器不可能知道你到底想干什么),可问题是编译器也无法靠你的口头保证得知
计算机是死的,
即便是编译期,你觉得你能保证,但谁知道你会不会写出
所以你要检查就写进类型系统的依赖数据,不检查就 Suppress,不要说自己可以保证,没有谁是敢保证自己的代码的。
比如说我就经常被 IDEA 自动推导的结果打脸(被打的是我自己的推导结果)。
另外,一般为了保证泛型的型变性(在 Kotlin 里,也就是输入输出方法被调用的可能性)安全,应该用
Array<kotlin.Int> 我猜你是这么想到这个问题的:Java 里你用的是
final T[] xs;
可是如同 kotlin.Int 不是所谓的『primitive』一样,Kotlin 里也没有 primitive 的 generics array。你是担心它有额外的性能开销?那就用
IntArray, CharArray 什么的…… 他们是自动拆箱(unboxing)优化的你说你能保证,这可以理解(因为编译器不可能知道你到底想干什么),可问题是编译器也无法靠你的口头保证得知
arrays::get 是的确是 (Int) -> E,是不是?计算机是死的,
Array<T> 由于 Java 的泛型擦除(泛型参数只在类型检查时存在,除非你用 Kotlin 的 inline + reified type parameter),其实际存储是由 JVM newarray/anewarray 自动创建的,alength/xaload/xastore 指令来取长/访问实际数组对象。List<T> 运行时全是 List<Object>, 知道他们的类型全都依赖类文件存的元数据即便是编译期,你觉得你能保证,但谁知道你会不会写出
arrays[0] = Any().also { assert(Any() !is E) }?这样实际上就和无类型检查没啥区别了。所以你要检查就写进类型系统的依赖数据,不检查就 Suppress,不要说自己可以保证,没有谁是敢保证自己的代码的。
Array<*> 替换 Array<java.lang.Object>
如果你不向数组写东西(就是说它的 set(Int, E) 不会被使用),Array<kotlin.Int> 就可以了。Forwarded from dnaugsuz
但它也是困难的,你试试写个 partial evaluation 的翻译器出来,带寄存器分配的那种,或者自己独立思考一下 Java 的反编译器该怎么写。
Dalvik 还好,不存在什么存储寄存器(link register) 什么栈寄存器维护的问题,要些 native compiler 还弄什么层次差啊…… 什么常量存储段啊…… Lua 还弄什么跳转链表……
iseki 你是大学生,不如照着 USTC 分享的那个 PL/0 来一次实验?
https://github.com/USTC-Resource/USTC-Course
Dalvik 还好,不存在什么存储寄存器(link register) 什么栈寄存器维护的问题,要些 native compiler 还弄什么层次差啊…… 什么常量存储段啊…… Lua 还弄什么跳转链表……
iseki 你是大学生,不如照着 USTC 分享的那个 PL/0 来一次实验?
https://github.com/USTC-Resource/USTC-Course
GitHub
GitHub - USTC-Resource/USTC-Course: :heart:中国科学技术大学课程资源
:heart:中国科学技术大学课程资源. Contribute to USTC-Resource/USTC-Course development by creating an account on GitHub.
Forwarded from dnaugsuz
Kotlin 里可以啊,难道
只不过提供了
Java 里,
噢…… 你的意思是
一般来说,能够静态检查的话都检查了,如果你说,你能保证,你就
比方说,
我知道 JavaScript 表达式
算不算『我能确保』?那你就 Suppress 就可以了,实在嫌难看你
Type checker 只是按照类型系统的规范检查你的程序,如果你确定你的建模是对的,一般情况(除非是运行时决定,这种情况就不是『静态』的类型)都可以推导/确定它的类型,
参数化类型也不是辣鸡(虽然 Kotlin 算是 Java 泛型擦除的一点改进)
Array<T> 不是?只不过提供了
IntArray 之类的优化(可以用 .toTypedArray() 来互化)T: Array<Any?> 是这个意思吧?换成 Java 就是 T: Object[] Any?: Array<Any?> 是不存在的,Any? 的确是 Array<Any> 的超类,你是要表达 T where T: Array<Any?>, Any? 吗?T where T: A, B 的意思是,一个类型 T 的元素可能是任何既是 A 也是 B 类型的对象,也就是交集类型(intersection type)Java 里,
capture<?extends A+B>
但是我可以保证数组存的都是某个泛型 E 的元素,那么在取数组元素时就需要强转下:arrays[index] as E。但是此时会报编译警告:Unchecked cast: Any? to E。 噢…… 你的意思是
<E, ES: Array<E>> …… Array<ES> ?二维数组?也不对啊?一般来说,能够静态检查的话都检查了,如果你说,你能保证,你就
Suppress 掉它。比方说,
val res = RhinoJavaScript.eval("1+1")
val a: Any? = if (res == 0.0) "s0" else 1 我知道 JavaScript 表达式
1+1 求值了一定是不等于我们这边的 (0.0 as Double)算不算『我能确保』?那你就 Suppress 就可以了,实在嫌难看你
val a: Int = a as Int 就可以了Type checker 只是按照类型系统的规范检查你的程序,如果你确定你的建模是对的,一般情况(除非是运行时决定,这种情况就不是『静态』的类型)都可以推导/确定它的类型,
参数化类型也不是辣鸡(虽然 Kotlin 算是 Java 泛型擦除的一点改进)
Forwarded from dnaugsuz
如果只是溢出了符号位应该就可以用
呃…… 或许,你的意思是,你要从
首先,我建议:不要用
我的意思是,它本该叫
然后是,如果 Kotlin 标准库没提供
你可以自己写解析算法,就用那种常用的十进制移位即可:
toULong 的吧呃…… 或许,你的意思是,你要从
kotlin.String 解析 kotlin.ULong,首先,我建议:不要用
String.toInt(), 不过既然 Kotlin 它就只提供了这个名字就算了…… [kotlin.text.toInt]我的意思是,它本该叫
parseInt 或者 readInt…… 或许是我多想了然后是,如果 Kotlin 标准库没提供
String.toUlong(radix),你又不想进行(实际上不一定正确,如果溢出的不对的话)的 toULong() 转换你可以自己写解析算法,就用那种常用的十进制移位即可:
fun String.toULong(radix: Int = 10): ULong {
var accumlator = 0.toULong()
for (digit in this.reversed())
accumlator = accumlator*radix + digit.toInt()
return accumlator
}Kotlin
toInt - Kotlin Programming Language
#China #book #school #life 🏫📔 最近从本校的物理老师处得赠了一本 #Algorithm 算法书《算法》 Algorithms in C++ I-IV: Fundamentals, Data Structure, Sorting, and Searching by Robert Sedgewick
https://book.douban.com/subject/1116599/
这本书甚是有价值,不仅仅在它的三观、逻辑、数学、图示、新奇、编程实现上,我就不谈它的价格相比之下实在是不高了,质量上,高德纳(Donald Knuth)的学生,算法,不是盖的。
包括这本书的作者据称都是刚开始写 C++,一个离开计算机都能编程的人,又是计算机科学研究者、算法研究者…… emmm……
我从这本书(我第一次见到用 TeX 排版得这么好看的书,前一次是看 Kanru Hua 的信号处理(signal processing)论文和期刊切片,排版带内联的,虽然我没看懂)的代码块里了解到了原来还有人和我一样不那么遵循所谓的『代码风格』……
他们说,他们努力设计出了一套简洁、可移植的 C++ 编程模板(这里不是
尽管我不是很喜欢数学的表达方式,我还是得承认我实在找不出一点茬子了 — 比起我从其他书里,尤其是 LLVM Cookbook — 那简直无法入眼的 C++ 代码、类 — 那哪能叫设计啊?分明就是无序杂乱的方法定义!即便能够工作,却怎么也不可能达到 Algorithms 一书示例代码的层次。
虽然我的智商不高所以能实现的东西不多,但我觉得,我的代码依然相对而言较甜,而 Algorithms 一书的代码则是带点数学薄荷的咖啡,醇香微苦,不过 — 都从不油腻、不会拖泥带水让人无从入眼。
这本书里,我学(且暂时只学到)了快速查找、合并查找(不带权重和压缩的)(因为我智商低,而且最重要的是,我有别的事还要完成……)
这是快速查找(也可以作为实现集合数据结构的一种算法),怎么快速法呢?
是解决『传递性问题』的一种算法,它的合并操作速度较慢(所以也叫慢合并),不过判断速度很快。
我们知道,对于一个 M 个输入,它都只需要 O(1) 的时间复杂度来判断是否联通,可在更新时每次都要执行 O(N·M) 次比较
这个问题就是给你一堆『对』,如果它是新的,输出它,否则就继续下一个(C++ iostream 扫描器)。
因为快速查找(quick search)/合并(merge search)有 up(父树)所以也称树状数组。
1 R 2
2 R 3 => 1 R 3
4 R 0
0 R 1 => 0 R 2, 0 R 1 R 2 R 3 且 3 R 4
(因为
细心的同学可能会觉得奇怪:假如我有 b - a; d - a,他们在树上不是一个树支的,可是却被认为在一起了。
其实这很正常:传递性关系是对称(symmetric) 的,也就是说
至于为什么像是树,这是因为集合传递性就是树状的
问题是:这不是一个逻辑问题啊,
上面的实现被称为快速查找,你可以注意到它把离散的 a R b; b R c 传递来了,至于为什么没有 a,是因为我们的实现方式是
我不会算法可视化,所以没有图(不像那个,我艹还是算法可视化专业研究者,计算机图形学基础啊。
至于合并查找(我不知道两颗树的权重算法和路径压缩该怎么做)(题外话,我自己设计了一个类似 Dijkstra 的 DFS 算法当然还没实现,关于狄克斯特拉算法我也自己想了6个细节,只等着写文。)
它就是
(准确的说也没啥 parent 不 parent 的,一切都是临时的,比如我们每次把 c1 的 up 都置为 a,实际上只是需要一个『最大』的集合加入。换句话说,如果 c1 是以后被另一个 up=a 的 d 关联起来的,都一样)
如果他们有同一个太爷爷(也就是说,之前
就是
就像 Rc(refcount GC)、hashmap 一样,树状数组也是只记录部分信息的,就是传递的关系,而不实际保持「继承」关系,所以才能有优化啊(要不然你只能
至于那个 O(...) 呢,书上也有在 Fundamentals 一节讲到,定义是:
O(g(n)) = {f(n):存在正常量c和n0,使得对所有n>=n0,有0
唉,没一个讲清楚的,这些数学家真的会自然语言吗?真的知道啥是定语啥是状语吗?
看不懂的话,自己用反对称套一下给自己一个主语,不然算了,毕竟数学不是给常人用的。
书上还有讲状态机、形式化文法等常见计算机科学/算法问题的,不能不说是丰富丰富再丰富。
谈到算法:数组(二分查找、选择排序、冒泡排序、合并排序)、链表、递归(尾递归)、分治算法(快速排序)、哈希表、图(无向、有向无环、广度优先搜索bfs、深度优先搜索dfs、狄克斯特拉算法)、动态编程DP、线性规划、傅里叶变换、二项式、随机化、状态机、形式化文法、二分查找树BST、跳跃链表SkipList、哈希算法、非对称密钥系统……
现在时间比较不充裕,我只能暂时先写这么多。
尽管在学校里我几乎没读这本书,我还是非常感谢赠我这本书的老师,要不然,或许我自己是找不到它的,谢谢你!
更详细的书评/笔记等我以后,会慢慢发出来、写成文集。
但愿我能够理解那些烧脑精妙的算法、但愿我能设计出我所需要所有简单的算法、但愿不论我走到多远、多高,都能够保持不卑不亢,也不会忘记曾经那个真心爱着技术的自己。
https://book.douban.com/subject/1116599/
这本书甚是有价值,不仅仅在它的三观、逻辑、数学、图示、新奇、编程实现上,我就不谈它的价格相比之下实在是不高了,质量上,高德纳(Donald Knuth)的学生,算法,不是盖的。
包括这本书的作者据称都是刚开始写 C++,一个离开计算机都能编程的人,又是计算机科学研究者、算法研究者…… emmm……
我从这本书(我第一次见到用 TeX 排版得这么好看的书,前一次是看 Kanru Hua 的信号处理(signal processing)论文和期刊切片,排版带内联的,虽然我没看懂)的代码块里了解到了原来还有人和我一样不那么遵循所谓的『代码风格』……
他们说,他们努力设计出了一套简洁、可移植的 C++ 编程模板(这里不是
template<> 的意思)。尽管我不是很喜欢数学的表达方式,我还是得承认我实在找不出一点茬子了 — 比起我从其他书里,尤其是 LLVM Cookbook — 那简直无法入眼的 C++ 代码、类 — 那哪能叫设计啊?分明就是无序杂乱的方法定义!即便能够工作,却怎么也不可能达到 Algorithms 一书示例代码的层次。
虽然我的智商不高所以能实现的东西不多,但我觉得,我的代码依然相对而言较甜,而 Algorithms 一书的代码则是带点数学薄荷的咖啡,醇香微苦,不过 — 都从不油腻、不会拖泥带水让人无从入眼。
这本书里,我学(且暂时只学到)了快速查找、合并查找(不带权重和压缩的)(因为我智商低,而且最重要的是,我有别的事还要完成……)
#include <iostream>其中的
const size_t N = 1000;
int main() { unsigned up[N], b, c; size_t it;
while (cin >> b >> c) {
auto a = up[b]; // R(a, b): up[a] = up[b]
if (up[c] == a) continue; // c R b
for(it=0; it<N; it++)
if (up[it] == c) up[it] = a; // R(it, c)
cout << b << " " << c; } }
unsigned up[N]; 也可以被泛化为 std::map<unsigned, unsigned> 这是快速查找(也可以作为实现集合数据结构的一种算法),怎么快速法呢?
是解决『传递性问题』的一种算法,它的合并操作速度较慢(所以也叫慢合并),不过判断速度很快。
我们知道,对于一个 M 个输入,它都只需要 O(1) 的时间复杂度来判断是否联通,可在更新时每次都要执行 O(N·M) 次比较
(==) 运算。这个问题就是给你一堆『对』,如果它是新的,输出它,否则就继续下一个(C++ iostream 扫描器)。
因为快速查找(quick search)/合并(merge search)有 up(父树)所以也称树状数组。
1 2我们的输入上有一个性质(property):
2 3
4 0
0 1 — 4-0-1-2-3
forall a b c. a R b, b R c => a R c
我们管叫逻辑断言的传递性(transitive)。1 R 2
2 R 3 => 1 R 3
4 R 0
0 R 1 => 0 R 2, 0 R 1 R 2 R 3 且 3 R 4
(因为
R(a=1, b=2, c=3) 得证 1 R 3 且 R(a=0, b=1, c=4) 得证 0 R 4)细心的同学可能会觉得奇怪:假如我有 b - a; d - a,他们在树上不是一个树支的,可是却被认为在一起了。
其实这很正常:传递性关系是对称(symmetric) 的,也就是说
forall a b. a R b ≡ b R a
换句话说,这实际上不是一棵树,是一个树状的集合,因为集合关系 (a `是同集` b) 也具有传递性,即 『小鸡是小鸭同集、小鸭是小鹅同集,小鸡也是小鹅同集』。至于为什么像是树,这是因为集合传递性就是树状的
问题是:这不是一个逻辑问题啊,
上面的实现被称为快速查找,你可以注意到它把离散的 a R b; b R c 传递来了,至于为什么没有 a,是因为我们的实现方式是
unsigned up[N]; 数组,如果我们用的是 strcut { unsigned *up; } 就也还是一样……我不会算法可视化,所以没有图(不像那个,我艹还是算法可视化专业研究者,计算机图形学基础啊。
至于合并查找(我不知道两颗树的权重算法和路径压缩该怎么做)(题外话,我自己设计了一个类似 Dijkstra 的 DFS 算法当然还没实现,关于狄克斯特拉算法我也自己想了6个细节,只等着写文。)
它就是
size_t i, j;也就是说,找到 a R b R c R … 里的这个 a,它的 parent 绝对是它自己,它就是最大的太爷爷。
for (i=0; up[i] != i; i = up[i]);
for (j=0; up[j] != j; j = up[j]);
if (i == j) continue; // common ancestor
else up[i] = up[j]; // merge ancestor
(准确的说也没啥 parent 不 parent 的,一切都是临时的,比如我们每次把 c1 的 up 都置为 a,实际上只是需要一个『最大』的集合加入。换句话说,如果 c1 是以后被另一个 up=a 的 d 关联起来的,都一样)
如果他们有同一个太爷爷(也就是说,之前
up[x] = a c1 R a 的那种)就是
up[a] = up[b] = up[c] 的情况(有关系的,我们都应用了相等关系的传递性,和传递性伴生的对称性)就像 Rc(refcount GC)、hashmap 一样,树状数组也是只记录部分信息的,就是传递的关系,而不实际保持「继承」关系,所以才能有优化啊(要不然你只能
xs.filter { it.first == this }.exist { it.second == x } 递归下去[因为这一个表达式应该被叫做『存直接链接』],就不好了)(当然,也可以 dfs 或者 bfs。)至于那个 O(...) 呢,书上也有在 Fundamentals 一节讲到,定义是:
对于所有 c、N0,若有 g(N) 使得对于所有 N0 < N,g(N) < c·f(N) 成立,则说 g(N) 是 O(f(N))。就是说数学函数的变化率啦,数学喜欢 \lim_{n\to\infty}{\frac{a}{b}} 趋近于 0 表示 b 渐进地大于 a 什么的……
O(g(n)) = {f(n):存在正常量c和n0,使得对所有n>=n0,有0
唉,没一个讲清楚的,这些数学家真的会自然语言吗?真的知道啥是定语啥是状语吗?
看不懂的话,自己用反对称套一下给自己一个主语,不然算了,毕竟数学不是给常人用的。
书上还有讲状态机、形式化文法等常见计算机科学/算法问题的,不能不说是丰富丰富再丰富。
谈到算法:数组(二分查找、选择排序、冒泡排序、合并排序)、链表、递归(尾递归)、分治算法(快速排序)、哈希表、图(无向、有向无环、广度优先搜索bfs、深度优先搜索dfs、狄克斯特拉算法)、动态编程DP、线性规划、傅里叶变换、二项式、随机化、状态机、形式化文法、二分查找树BST、跳跃链表SkipList、哈希算法、非对称密钥系统……
现在时间比较不充裕,我只能暂时先写这么多。
尽管在学校里我几乎没读这本书,我还是非常感谢赠我这本书的老师,要不然,或许我自己是找不到它的,谢谢你!
更详细的书评/笔记等我以后,会慢慢发出来、写成文集。
但愿我能够理解那些烧脑精妙的算法、但愿我能设计出我所需要所有简单的算法、但愿不论我走到多远、多高,都能够保持不卑不亢,也不会忘记曾经那个真心爱着技术的自己。
Douban
算法Ⅰ~Ⅳ(C++实现):基础、数据结构、排序和搜索 (豆瓣)
图书算法Ⅰ~Ⅳ(C++实现):基础、数据结构、排序和搜索 介绍、书评、论坛及推荐