Forwarded from dnaugsuz
So, did you kept backup of your old blog (CN) somewhere?
Forwarded from dnaugsuz
I can't find its link at https://ice1000.org/ :(
Forwarded from Teslarend Zhang
Yes, they're cleaned up. Low quality posts don't deserve a place in my blog
为什么冰封的博客没人去备份啊…… 我早该去备份的
至少,它们还是能让人看懂的,而且我看了其中一篇博文才写的 ParserKt…… 应该说还是有不少参考价值的
至少,它们还是能让人看懂的,而且我看了其中一篇博文才写的 ParserKt…… 应该说还是有不少参考价值的
“想用 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 呢? 给弄成
“你们知道怎么把 delete operator 绑定给脚本解释器吗? Lua 有 userdata 和 lightuserdata 啊……说错重点了”
…… #Python #Java #CV duangsuse 的日常自我争论
“是不是在逃避要写的关系式求解器啊……”
“既然这次显然完成不了,不如放 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
传统的 Message 化控制流扩展
WebExtensions ContextMenus API
常见的 BrowserAction
还有许多不能直接发上来了,只有简化版,待会还有一个 time limited cache,看看。
解码用到了新的 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,看看。
GitHub
FirefoxBar/HeaderEditor
Manage browser's requests, include modify the request headers and response headers, redirect requests, cancel requests - FirefoxBar/HeaderEditor
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 是
然后是 filter 的 Observer,Observer 是一种利用副作用的模式,它有 onNext, onFinish, onError 三个 slot (message handler)。
顺带提及一下
我们在
consume/filter.write/close
然后,
无论怎么看,滥用 try 都是不对的。首先一条是 better flat than deep,不重视代码嵌套深度的人是写不出容易维护的代码的(因为深的嵌套层次往往意味着存在模板化的代码或者过度设计)
如果已经在
使用 ES6 还有一种骚一点的写法:
当然,其实
是一个通用模式——线性查找(民科私货知识注意!)
我们也可以用
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.byteLength 或 it.byteLength 在这里是一样的。(Uint8Array 是
TypedArray, it.buffer 是 ArrayBuffer)然后是 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
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 之所以是一个原则,是因为若不作为原则,它也不是不可替代的选择,例如
较浅的块嵌套层次对好的代码很重要,比如 Promise——把 callback 和 error handling 结合在一起,如果我们用
对于代码来说,一致性也是相当重要的——所以当你开始降低嵌套层次,就会更容易和其它浅嵌套的部分契合一些,也更容易发现可以重构的一些细节。
我们来看看一个更广义的 if:(下文 f() f1() 是对立的两条分支, cont() 是 continued block 的意思)
如果我们用线性指令流来表达:
这种 if/else 的结构没法用 early-return 改写,举个例子:
不过,如果有可能还是得做进一步封装,比如 C++ 里如果 cont() 仅包含对资源的 close(),你就可以用 RAII(resource acquire is actual initialization) 技术,它能够在离开局部作用域 (return, ...) 之前自动回收资源。
if (p) {
f();
}
cont();
当然也可以写成if (!p) return; // 或者 break 之类能够跳过下面的 else branch 的东西但是,语义上条件执行并不能证明
f();
cont();
if (p) f(); 或者 if (!p) { return; } f(); 是唯一的正确选项。较浅的块嵌套层次对好的代码很重要,比如 Promise——把 callback 和 error handling 结合在一起,如果我们用
onOver/onDone 加 if (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();但是 if (!p) return 的 false branch 不包括 cont 就是了,如果 dirty 一点可以随意 jump (goto),但不建议。
(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
不过,如果有可能还是得做进一步封装,比如 C++ 里如果 cont() 仅包含对资源的 close(),你就可以用 RAII(resource acquire is actual initialization) 技术,它能够在离开局部作用域 (return, ...) 之前自动回收资源。