duangsuse::Echo
718 subscribers
4.26K photos
130 videos
583 files
6.48K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
Forwarded from yunfeng
隔壁 java 留下了眼泪(
Forwarded from dnaugsuz
Jawa 也有一些可以“跨运行时平台”的实现,比如 JSweet transpiler
JVM 本身对 C, C++ 等所谓 Native 语言的编译器也有实现的,至少人家还支持 JNI 扩展底层访问性呢 [比如说这个]
曾经的 GCJ (GNU Compiler for Java) 也是支持 Java -> native code 的
现在 GraalVM 、之前 ExcelsiorJET 不也在吹 AOT 编译的说
Forwarded from Yuuta
不懂那么深奥的,反正 J AV A 给我的感觉就是有大又笨又老(x
Forwarded from dnaugsuz
Java 是很老,但也是 Kotlin 它干爹啊 🌝
Forwarded from Yuuta
不能完全算吧?从某种意义上说 Kotlin 是 “兼容” Java(x
Forwarded from dnaugsuz
C 自己也有不好的地方,比如 Null safety
你得自己写代码处理输入 ptr 可能为空但不应该为空的情况,而 Kotlin 是自动处理

如果你是比较优秀的程序员,肯定会用子程序对 raw FFI 进行高层封装(比如,一个 Optional<T> 会被你弄成 @Throws(XXXNotFound::class) 的形式),这样就避免了每次检查 null safety 的问题,Kotlin 只是做了对的事情,检查了 null 指针的可能性,把运行期的错误提升到了编译期,虽然 Kotlin 它不 Check Exceptions... 也有自己的道理,但完全不 sound (没 error 也没 warn)的编译检查还是....

这一点 ice1000 的博文《形式验证、依赖类型与动态类型》讲得很好
Forwarded from dnaugsuz
是啊,几乎是能完全兼容 Java,至少排除元编程的情况下是完全双向兼容

但是 Java 依然还是可以用的,这点不可否认,不过我要说的是,即使是被所有人批判的 Java,也不是被人完全理解的,Java 自己看起来很简单,就算排除掉 JVM,某些细节很多资深工程师也都是说不上来,比方说所有类型表达式的求值顺序、所有语法结构、Annotation Processor 加入后的编译流程

毕竟『精通』和『创造』还是有区别的,一个是使用,一个是实现,实现意味着你得什么都知道,使用是你用什么学什么。

当然如果工程师实现过符合标准的 Java 编译器算我没说
Forwarded from dnaugsuz
🤔 说起来我这么开放地发了那么多字含代码的内容,会不会本群不欢迎贴代码呢?

这么久也没被踢,感觉很不对劲(跑
曾经我一行代码没贴,贴了个 ruby 文件,结果瞬间踢出
duangsuse::Echo
== 无关的 Win32 Form API static Application.EnableVisualStyles(); static Application.SetCompatibleTextRenderingDefault(bool); static Application.Run(Form); — namespace System.Windows.Forms { Form } class Form { protected void OnLoad(EventArgs); } == 有关的…
#Statement 可能有一些人觉得我只看理论,只会空谈不能实践,一样是废物一个,但是我相信这只是时间不足的问题而已,现在我已经开始准备学着实践了,上面嵌入的代码就是证明。
可能也会被人喷废话太多,或者『这些东西太简单,你去学学写 Android 设计模式、你去学学 JavaEE 设计模式』什么的

但我相信,只要理论做足了、代码写多了,那些看起来很魔法的东西也是可以做到的。
反而是某些地方使用的某些设计模式,是真正意义上的凭空增加复杂度,只是因为面向对象只允许受限制的函数指针,(Java 里)我们要起无数个方法名,在所有地方都要用匿名内部类(曾经没有 Lambda 的时候),只是为了实现某一个类似 FunctionalInterface 的东西... 现在也还是要记类名,因为没有 Java 10 里才出现的 var

一些诸如依赖注入的耦合问题,不少函数式编程语言里都有类似的解决方案,它们看起来会更优雅,但函数式给你最大的礼物其实不是好看的代码,而是对程序逻辑本身拥有更好的直觉,让你用最直白最通透的视角看一切程序,最大程度上泛化编程,让你不止不再局限与某种语言,甚至不再害怕新的编程范式,实际上这才是优秀的工程师会需要的东西。

我觉得理论重于普通的工程,因为理论可以引导我走到更高的地方或者更低的地方,高到范畴论\逻辑和一些数学、低能看到电子元件、信号、处理器的晶震、逻辑门、一个一个二进制位、时序电路、触发器,让我完全理解某些模式、算法、模型,给我一种直觉,而不是学一个懂一个、抄代码看文档改代码,我要把所有代码默写下来,我觉得这才是我追求的东西,我追求的是技术,但仅仅是技术而已,而不是它伴生或者为之而生的其他东西。

"Talk is cheap, show me the code." Linux Kernel 的创造者,Linus 说过这句话
可是有时候,恰恰 talk,恰恰是设计某件东西才是最困难的,在没有一个设计大纲,算法没有弄出门路、模型完全还没有彻底熟悉之前想 "write code" 是不对的,它只会让你写出糟糕的代码,而且如果要实现的东西本身没有那么简单即得,你花在调试上的时间绝对会比理论准备可能花的时间多。

软件工程有个名词,叫做『代码复用』,已经实现了一遍的逻辑就不要再来写第二次了,它对程序代码的可维护性、可移植性、可读性都有很大的好处。
实际上做好理论,就是做好了最好的“代码复用”。

有些人可能知道一种叫做“文学化编程”的东西,它把编程和对自己程序最直白的解释结合在了一起,难道这不是理论和实践最完美的结合吗?

根据高德纳本人所说,文学编程为高质量程序而生,因为它强迫程序员显式描述程序背后的思路,让不充分的设计决策无所遁形。
高德纳还声称文学编程提供了一流的文档系统,它并非插件,而是随着编程思路的慢慢展现而不断自然发展的过程。

高德纳先生是谁?他是美国的计算机科学家 Donald Ervin Knuth

著名计算机科学家,斯坦福大学计算机系荣誉退休教授。高德纳教授为现代计算机科学的先驱人物,创造了算法分析的领域,在数个理论计算机科学的分支做出基石一般的贡献。在计算机科学及数学领域发表了多部具广泛影响的论文和著作。1974年图灵奖得主。

高德纳所写的《计算机程序设计艺术》是计算机科学界最受高度敬重的参考书籍之一。他也是排版软件TeX和字体设计系统Metafont的发明人。

实际上使用 Literate Programming,虽然可能“有违简洁”“开发效率低下”(而且在某些逻辑非常简单的程序里,的确相当耗时间) [code example]
但在某些领域,无法用简单而复杂难看的堆砌替换优雅但是难做的设计的领域,Literate Programming 是最合适的编程方法

它的确强调了程序员必须有好的思路才能做到 Literate Programming,也提高了编程的门槛(因为它要求你理解很多本来抄代码就 OK 的东西),但这不正是优秀的程序设计者所必须做到的事情吗?

🤔 程序 = 数据结构 + 算法 — Niklaus Wirth

这是结构化编程的宣言,当然其他范式还有别的看法,但有一点是一致的:

算法是程序的灵魂

既然编程就是在利用语言的抽象,描述你的算法逻辑,为什么不把它明明白白地写出来呢? 🌝

不抄代码无以至今日,不学算法无以终余年。
Forwarded from Richard Yu
因为不是C/C++,谁知道有没有指针?没的话那就传个0吧。C++我会写nullptr。
Forwarded from dnaugsuz
如果没有指针类型的话也是可能的,但是,如果是我也会搞个全局的常量 nullptr, 尽可能避免混淆

如果类型系统菜,程序员负责给它洗地
Forwarded from Richard Yu
"" 不代表 nullptr,它是有一个地址的。
Forwarded from dnaugsuz
是啊,所以我说是一个常量

const char *EMPTY_CHARP = "";

这里我没有指定具体分配位置,但它是常量,换句话说编译器喜欢内联也可以直接翻译成

GetModuleHandle("");

或者

const static char EMPTY_CHARP[] = {'\0'};

然后
GetModuleHandle(EMPTY_CHARP);
duangsuse::Echo
我觉得就 Scanner 的逻辑结构上应该不够优雅,state 几乎是个笑话、lineDoUntil 非得加一个『keepLastLineOnce』才能用(doUntil 是在 scan body 里看到 new message header 的时候用的,可是等到这个 Message 返回,新 message 头会被下一次 iteration 直接忽视掉(因为我没设计好数据流,然后每次由判断 hasNext 的函数读新行的),导致漏掉偶数消息,不得不引入类似『mark/reset』的机制才可以),不过能用就好(跑路…
后来我会发现这个『keepLastLineOnce』其实就是一个长度为 1 的 reset buffer,不过因为比较短,就没有使用数组存放

所谓的 keepLastLineOnce 功能,就是

String hello = readLine(); // "Hello"
String world = readLine(); // "world"
readLine(); // EOFException

但是如果这么写

[Hello world]

String hello = readLine(); // lastLine=null, result="Hello"
String world = readLine(); // lastLine="Hello", result="world"
keepLastLineOnce = true;
readLine(); // Hello

就可以实现 lookAhead1 的功能:我可以向后阅读一个项目之后若无其事地告诉 stream 向前 <- 移动一个字符,
实际上是告诉 stream 下次我 nextLine() 的时候给我之前存下已读的输入 @-1,但实际上我已经读过了下一个字符),然后决定我做什么分支(状态迁移)

lookAhead1:
var ln = readLine();
// next 1 char
keepLastLineOnce = true;
// reset buffer @-1

其实本质上和这么做是没有不同之处的

markLine(); // enter marking state
var ln = readLine(); // push old line
resetLine(); // enter initiate state

若 reset buffer 还为空,则读入一行返回,此时即使长度为 1 的 buffer 充盈
if (lastLine == null)
return readLine();

若 reset buffer 不为空且之前提交请求 lookback(backseek) 到上一个字符(keepLastLineOnce),
则返回上一个字符,把 boolean (这里可以视为一位二进制数值 i1) true 减去 1(= false)
否则就向前进一位,并且把已读数据加入 buffer
if (!keepLineOnce) {
return readLine();
} else {
keepLineOnce = false;
return lastLine;
}

(这里为了明确性我对控制流进行了展开)(删掉,之前是我搞混了 lastLine 和 readLine() 的值...)
以后我要再写类似的解析程序,就会直接使用 mark/reset 了
duangsuse::Echo
Image to Bar3D... 😵???
注意,这里这个 var lum = ...; 是依据 RGB 颜色计算 Grayscale 灰度值的代码

Gray_linear = 0.2126 × R + 0.7152 × G + 0.0722 × B

可是它还实际是被 255 减去了... 实际上这个值最大可能是 255(因为是一个色彩通道的色值)
duangsuse::Echo
顺便说一下为啥要进行 n 次,每次都要排序列表从 0 到 n - i -1 的项目,虽然我现在还没有证明为什么 bubble sort 输出的列表就一定是有序的的能力(归纳证明) 排序算法,就是给一个输入序列、一个测试函数『序(order)』,输出序列满足以下条件 forall i. list[i] `order` list[i+1] 比如 list=[3,2,1]; ord=(<) 简单的选择排序每次挑一个『最大』的元素出来(它满足 forall x in list. x <= it),复杂度是 O(n)…
#Java 修正:刚才看这个泛型通配符模拟的时候感觉有点不对劲,首先方法泛型应该只在很少的地方出现型变通配符,其次,
我模拟的时候发现这个实际算法逻辑

private static <T extends Comparable<T>> void propagateOnce(List<? super T> xs, final int ri);

中这个 xs :: List<capture<? super T>> 的使用 T get(int);void set(int, T); 不对称

首先 xs 从 producer-consumer 模型来看它接受 T 也生产 T,实际上就无法保证同时的类型安全(但是如果从这个方法的一个参数对象读另一个写,比如 copy 方法是没有问题的),所以它不应该有泛型协逆变(invariant)。

我考虑了一下 TInteger xs 是 List<Number> 的情况,PECS 原则 List<in Number> 也即 List<? super Number> 是可以安全地使用 set(int, T) 方法的(Integer 实例可以转换为超类 Number 的实例,虽然 Java 泛型擦除所以 List<T> 实际上运行时是 List[^1] 这个 raw type...),可是不可能同时安全使用 T get(int); (要将 Number 强制转换为 Integer,这是不合法的)

这点是需要注意,我之前没有注意到,现在也及时修正了 #fix #Kotlin

其次,我从 Java 的 Collections API 看到了 sort 的方法签名

public static <T extends Comparable<? super T>> void sort(List<T> list);

现在我把这个 <T extends Comparable<T>> 改为 <T extends Comparable<in T (? super T)>> ,显然 Comparable<Number>int compare(T, T); 可以接受 T=Integer 作为比较输入
我得使用通配符泛化一下(逆变声明)才能支持这个... (当然 Kotlin 里包装的就直接使用声明处型变了,不需要你操心它的型变性)

在此希望大家注意.... 不要搞混了 PECS 原则的实际含义


^1: List<Object>, 和 List<?> 还是有区别的,因为这里的 ? 不能作为输入但作为输出可以强制转换成 Object,它像子类型系统里的 bottom type、Kotlin 的 Nothing,它是任何类型的子类型