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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
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, ...) 之前自动回收资源。
This media is not supported in your browser
VIEW IN TELEGRAM
原来现在技术已经这么成熟了,好羡慕 SP/ML/CG 的dalao们啊