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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Forwarded from dnaugsuz
So, did you kept backup of your old blog (CN) somewhere?
Forwarded from Teslarend Zhang
Is it no longer accessible?
Forwarded from dnaugsuz
I can't find its link at https://ice1000.org/ :(
Forwarded from Teslarend Zhang
Oh, the blogs
Forwarded from Teslarend Zhang
Yes, they're cleaned up. Low quality posts don't deserve a place in my blog
为什么冰封的博客没人去备份啊…… 我早该去备份的
至少,它们还是能让人看懂的,而且我看了其中一篇博文才写的 ParserKt…… 应该说还是有不少参考价值的
Forwarded from Kurisu🇨🇳 |伽蓝院朱雀
Forwarded from 荔枝木
这并不是愚人节玩笑
“想用 Java 重写原 Python+OpenCV+Tesseract 的 extract-subtitles ,并且还要弄一个支持处理视频的 Montage 图处理 Python 脚本”

“是不是在逃避要写的关系式求解器啊……”
“既然这次显然完成不了,不如放 TODO List 里吧。”
“放了又有啥意义啊,都鸽子了,还不如简化一下, Java 重写的那个取其纲要,弄个手写 OpenCV 的 JNI 绑定好了。Montage 处理不用 CV,用 PIL 先做单帧的,也好重新熟悉设计。”
“OpenCV 里 imread, imwrite, imshow 应该怎么重新设计啊, imread 变成 Image 的 constructor overload,imwrite 变成 Image.write ?”
“那样的话也不一定好啊, Image("01.png").write("01.png") 看起来是比 imwrite(imread("01.png"), "01.png") 好,可是 img.write("x.png") 真的能比 imwrite(img, "x.png") 突出重点么?现在一个是构造器一个是实例方法就没问题?那 imshow 呢? 给弄成 Window.show 还是 Image.show(String title) 呢?”
“你们知道怎么把 delete operator 绑定给脚本解释器吗? Lua 有 userdata 和 lightuserdata 啊……说错重点了”

…… #Python #Java #CV duangsuse 的日常自我争论
duangsuse::Echo
#China #Low #weibo
https://github.com/FirefoxBar/HeaderEditor/blob/master/src/background.js HeaderEditor 的 response body 修改 #Learn #JavaScript

解码用到了新的 ArrayBuffer / TypedArray API

browser.runtime.onMessage.addListener(function(request, sender, sendResponse)
传统的 Message 化控制流扩展

if (browser.contextMenus != undefined)
browser.contextMenus.onClicked.addListener((info, tab) => {


WebExtensions ContextMenus API

browser.browserAction.setIcon({ path: "" })
常见的 BrowserAction

_textEncode(encoding, text) {
let encoder = this._textEncoder.get(encoding); //cache
if (!encoder) {
// UTF-8使用原生API,性能更好
if (encoding === "UTF-8" && window.TextEncoder) {
encoder = new window.TextEncoder();
} else {
encoder = new TextEncoder(encoding, { NONSTANDARD_allowLegacyEncoding: true });
}
this._textEncoder.set(encoding, encoder);
}
// 防止解码失败导致整体错误
try {
return encoder.encode(text);
} catch (e) {
console.log(e);
return new Uint8Array();
}
}

还有许多不能直接发上来了,只有简化版,待会还有一个 time limited cache,看看。
const filter = browser.webRequest.filterResponseData(e.requestId);
let buffers = null; //result

// onNext, onDone, onError
filter.ondata = (event) => {
const data = event.data;
if (buffers === null) {
buffers = new Uint8Array(data); return;
}
const buffer = new Uint8Array(buffers.byteLength + data.byteLength);
//v 将响应分段数据收集拼接起来,在完成加载后整体替换。这可能会改变浏览器接收数据分段渲染的行为。
buffer.set(buffers); buffer.set(new Uint8Array(data), buffers.buffer.byteLength);
buffers = buffer;
}

filter.onstop = () => {
if (buffers === null) { filter.close(); return; }

for (let item of rule) { //< 缓存实例,减少开销
const encoding = item.encoding || "UTF-8";
try {
const _text = this._textDecode(encoding, buffers.buffer);
const text = item._func(_text, detail);
if (typeof text === 'string' && text !== _text) { buffers = this._textEncode(encoding, text); }
} catch (e) { console.error(e); }
}

filter.write(buffers.buffer);
buffers = null;
filter.close();
}

filter.onerror = () => { buffers = null; }
duangsuse::Echo
const filter = browser.webRequest.filterResponseData(e.requestId); let buffers = null; //result // onNext, onDone, onError filter.ondata = (event) => { const data = event.data; if (buffers === null) { buffers = new Uint8Array(data); return; } …
首先我们来学习第一个模式,拼接 Uint8Array
fun concat(buf0, buf1) {
let buf01 = new Uint8Array(buf0.byteLength+buf1.byteLength);
buf01.set(buf0, 0); buf01.set(buf1, buf0.byteLength);
return buf01;
}

注意 buffers = buffer; 那一段的类型正确性: buffer 没有 it.buffer.byteLength? 其实用 it.buffer.byteLengthit.byteLength 在这里是一样的。
(Uint8Array 是 TypedArrayit.bufferArrayBuffer

然后是 filter 的 Observer,Observer 是一种利用副作用的模式,它有 onNext, onFinish, onError 三个 slot (message handler)。

const filter = browser.webRequest.filterResponseData(e.requestId);
/* filter.ondata = (event) => {}; filter.onstop = () => {}; filter.onerror = () => {}; */


顺带提及一下 let buffers = null; 作为数据依赖的合理性,是合理的,毕竟我们要在 ondata 里拼接这个 buffer。 这就好像整个 filterReponseData 的程序是某种意义上的 Array,它必须有一个私有属性以存储状态一样。

我们在 filter.onstop 里添加了后继逻辑,当然这整个也可以被抽提出来(作为一个类,暴露抽象的 onRequestData(mergedBuffer) 即可)
filter.onstop = () => {
if (buffers === null) { filter.close(); return; }
onRequestData(buffers.buffer);
filter.write(buffers.buffer);
buffers = null; //< 这个 dispose 必须要做,可没 GC 帮你削对象了
filter.close();
}

大体流程就是处理(consume),尾操作(index+=1 那一类的,这里是 filter.write(buffers.buffer)),和 close()
consume/filter.write/close
然后,
onRequsetData(buffer) {
for (let item of rule) { //< 缓存实例,减少开销
try { //< should be removed
const text = this._textDecode(item.encoding, buffer);
const replaced = item._func(text, detail);
if (typeof replaced === "string" && replaced !== text) { buffers = this._textEncode(encoding, text); }
} catch (e) { console.error(e); }
}
}


无论怎么看,滥用 try 都是不对的。首先一条是 better flat than deep,不重视代码嵌套深度的人是写不出容易维护的代码的(因为深的嵌套层次往往意味着存在模板化的代码或者过度设计)
如果已经在 _textDecode_textEncode 里做好了错误处理,一般而言无论对变量还是对控制流都要采取最小化作用域原则,能精确的就不要把整个子程序都套到 try 里去。
使用 ES6 还有一种骚一点的写法:

processRequestData(buffer) {
for (let { encoding, func } of rules) {
let text = this.decodeText(buffer)
let replaced = func(text);
if (typeof replaced == "string" && replaced !== text) return this.encodeText(replaced);
}
return buffer;
}


当然,其实
for (let x in xs) {
if (p(x)) return f(x);
}
return f1();

是一个通用模式——线性查找(民科私货知识注意!)

我们也可以用 kotlin.sequences 重写一遍:

return rules.mapNotNull { rule ->
val text = decode(buffer, rule.encoding)
rule.func(text).takeIf { it != text }
}.firstOrNull() ?: buffer
This media is not supported in your browser
VIEW IN TELEGRAM
duangsuse 重构小讲堂(迫真)
duangsuse::Echo
首先我们来学习第一个模式,拼接 Uint8Array fun concat(buf0, buf1) { let buf01 = new Uint8Array(buf0.byteLength+buf1.byteLength); buf01.set(buf0, 0); buf01.set(buf1, buf0.byteLength); return buf01; } 注意 buffers = buffer; 那一段的类型正确性: buffer 没有 it.buffer.byteLength? 其实用…
better flat than deep 之所以是一个原则,是因为若不作为原则,它也不是不可替代的选择,例如
if (p) {
f();
}
cont();
当然也可以写成
if (!p) return; // 或者 break 之类能够跳过下面的 else branch 的东西
f();
cont();

但是,语义上条件执行并不能证明 if (p) f(); 或者 if (!p) { return; } f(); 是唯一的正确选项。
较浅的块嵌套层次对好的代码很重要,比如 Promise——把 callback 和 error handling 结合在一起,如果我们用 onOver/onDoneif (res.error) else …… 那代码还能看么?

对于代码来说,一致性也是相当重要的——所以当你开始降低嵌套层次,就会更容易和其它浅嵌套的部分契合一些,也更容易发现可以重构的一些细节。

我们来看看一个更广义的 if:(下文 f() f1() 是对立的两条分支, cont() 是 continued block 的意思)

if (p) {
f();
} else {
f1();
}
cont();


如果我们用线性指令流来表达:

(br.not b) a (jmp c) b c
或者
(br.if a) b (jmp c) a c

其实控制流图上,c 一般被称为 merge block…… 这里就用 cont() 代表了:P

这种 if/else 的结构没法用 early-return 改写,举个例子:
if (!p) return; f(); cont();
(test p) (br.not return) (call f) (call cont) return

if (p) f(); else f1();
(test p) (br.not f1) (call f) (jmp cont) f1 (call f1) cont

但是 if (!p) return 的 false branch 不包括 cont 就是了,如果 dirty 一点可以随意 jump (goto),但不建议。

不过,如果有可能还是得做进一步封装,比如 C++ 里如果 cont() 仅包含对资源的 close(),你就可以用 RAII(resource acquire is actual initialization) 技术,它能够在离开局部作用域 (return, ...) 之前自动回收资源。