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 Richard Yu
C里面一般是 #define NULL (void*)0 的,C++ 里面 nullptr 转换为指针的时候也是 0。
Forwarded from dnaugsuz
... 真的不该这么写的,以后千万千万不要把 nullptr (char*) *{'\0'}; 之类的东西写成 0 了,很多人以为代码短一点很秀,其实反而会导致很大混淆,这是不正确的

LLVM Cookbook 这本书里,把所有 * 指针类型的空指针值全写成了 0,我眼睛都要看瞎了
书中有一个大概类似这样的子程序:
ConstLiteral::Codegen override 了超类 BaseAstvirtual Value *Codegen();
Value *ConstLiteral::Codegen() override {
switch (type) {
case vaConstInt: return ConstantInt::get(Type::getInt32Ty(vCtx), (int) value);
default: return 0;
}
}

有的时候父语法树节点依赖子节点的值,然后要 null safe,他是这么判断的(比如 for i=0, 10 循环节点):

if (initval.Codegen() == 0) return 0;

其实要这么写(也很辣鸡莫名其妙)我都能接受,但他偏偏写了这么多 0...
if (initval.Codegen() /*!=0*/) {...}

我的天啊,简直看瞎了,当时我就觉得他们都是些只会抄代码的货,虽然后面的确是有干货,为什么编程风格这么差
Forwarded from dnaugsuz
所以以后觉得 "" 难看就弄一个 EMPTY_CHARP (当然 Windows 里显然要叫 EMPTY_LPCHAR...) 常量算了
这么混淆的确是有点误人子弟,别人看第一眼还以为是 GetModuleHandle 函数处理了特别的值,其实只是 "" 而已
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 了