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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
CnEn.class
2.1 KB
具体的算法比较偷懒,开始的时候我用 java.io.Writer#write(int) 被扼了,这厮居然只取 int 的 low 16 bit 当一个 int 输出,后来我觉悟了,才学会 append 输出两个字符....
duangsuse::Echo
具体的算法比较偷懒,开始的时候我用 java.io.Writer#write(int) 被扼了,这厮居然只取 int 的 low 16 bit 当一个 int 输出,后来我觉悟了,才学会 append 输出两个字符....
第一次把 intchar 用(Unicode UTF-32),而且没有用 java.util.Scanner,而且没有莫名其妙拆分 int 为四个 byte 再去创建新 String 输出(有很严重的 GC 开销的,逃逸分析栈上分配除外),算是『轻量级逻辑』,开心(虽然本来就没有必要...)

全部使用 GNU nano 和 javap, Oracle JavaSE https://docs.oracle.com/javase/8/docs/api/ 完成
Main.java
1.4 KB
#Java Java 的 Swing 控件 布局 Pane 什么的有点麻烦... Dimen 知道但是 Rigid 的一些参数不知道... 今天到这里
duangsuse::Echo
Main.java
不如 Android Container 们方便(
Android 的 Widgets 可以直接 match_parent 什么的,非常方便...
duangsuse::Echo
#zhihu #recommended #China #school https://www.zhihu.com/question/320967006 🤣和呵呵呵哈哈哈
其实就 996.ICU 来说,我非常支持他们运动。 #China #Low

加班加点对身体不好,在现代的中国,维权本来就是正当的(正如之前的奔驰轿车车盖维权和超市找零维权一样)

况且某些公司的管理层,居然要求人去无薪加班加点地敲代码,这就是折磨人。

对于专业水平来说,不同的人会有差异,但是对于工薪和工作时间来说,不应该对所谓的『码农』有歧视,他们也是职员!
#week 那么我懒得再说其他的消息了... (基本想看的看完了,欸你们更新怎么这么慢?)

现在我说说我这周的设计(和之前的 Cat 程序设计语言),并且预期能在下午六点之前完成 Parserc 包(包括 RangeMap、SourceManager、BinaryStreamProcessor 和 TextCombinator)
写的太多不知从何说起...
时间还是不够啊

我不是把时间给虵(shé 🐸)了... QAQ

就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意

Parser 框架我还打算自己手写这些算法:

TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了)

RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现 NestedRageMap)
LinearRangeMap (线性查找的 RangeMap,充当演示,时间复杂度捉鸡)
HashRangeMap (空间开销捉鸡,而且不能给不可枚举的类型创建映射,比如 Rational Number)

BsearchRangeMap (这才是主流)
CollisionRangeMap (支持非边缘碰撞检测的 Bsearch)
FuzzyRangeMap (支持简单碰撞替换情况的 CollisionRangeMap,不过没有额外的碰撞类型计算)
NestedRangeMap (可以用来开发 IDE 的 CollisionRangeMap)

VersionizedMap (可以实现 Dynamic Scoping)
CollisionVersionizedMap (基于碰撞检测的 VersionizedMap)
HashCollisionVersionizedMap (HashMap,Hashtable 实现)

MultiMap (K -> Multiply Value 的 Map)
ReverseIndexedMultiMap (同上,还自动维护反向索引)

还有这些辅助的东西
MixedArrayLinkList (就是类似猾为那种『IO 热区检测优化』... 不过就是两种模式『Link 插入模式』『Array 访问模式』切换而已)
Pair, Range
ArrayMap
MixedArrayHashMap
RandomReadStream (完全保存输入流的所有已读字节,随时可以 mark / reset,即使输入流不让 Mark / reset)
StringView (覆盖 get, length, subsequence 方法的 CharSequence)


Cat 因为连一个解析器框架都没有不好定义语法呢(虽然已经有语法和语义稿了,足足写了五面多)
ApkPool 因为 GeekSpec 什么的技术都还没有准备好,就不能说了,不过我觉得如果有时间可以谈谈 Java 类的『行为 hashCode』是否可行
duangsuse::Echo
写的太多不知从何说起... 时间还是不够啊 我不是把时间给虵(shé 🐸)了... QAQ 就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意 Parser 框架我还打算自己手写这些算法: TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了) RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现…
RangeMap 其实是相当有用的数据结构,前几天我亲自算过的,二分查找算法优化可以实现!

所谓一个 RangeMap,有插入、删除、查询三个基本操作,就是给你一对 Comparable(Range)、以及他们的映射 Value,进行插入后,查询 i1 <= x < i2,或者说 x in range 就能获得之前的 Value

比如:

val offsetLineMap = RangeMap()

val str = "abc\n123\n!@#"

var startLine = 0
for ((i, c) in str.withIndex) when (c) {
'\n' -> { offsetLineMap[startLine..i] = str[startLine..i]; startLine = i+1; }
else -> {}
}

然后我们就可以利用 RangeMap,在 O(log2 n) 的时间复杂度(而不是 O(n) 辣鸡线性复杂度下)完成 offset -> line 的索引

offsetLineMap[0] -> "abc"
offsetLineMap[1] -> "abc"
offsetLineMap[2] -> "abc"
offsetLineMap[3] -> "123"
offsetLineMap[5] -> "123"
offsetLineMap[6] -> "!@#"
offsetLineMap[9] -> IndexOutOfBounds

当我们有了 NestRangeMap 这种逆天的数据结构(和普通 BsearchRangeMap 比更类似 HashMap 和 MultiMap 比)后,还能快速实现类似 IDEA 的辅助功能,给可能有嵌套结构的 AST(抽象语法树,JB 也有一个类似的 PSI,程序结构接口)加上索引

这样,只要我们能拿到 TextField 的光标 offset,就能做类似
+ 拿到光标下的语法嵌套结构,能够知道语法路径在哪

这样的事情 🤪

这种功能很重要吗?的确很重要啊,如果你用线性查找这种省事但不讨好的算法,或者不考虑 Range 的碰撞,是绝对没有这种福利的

然而 NestedRangeMap 就可以实现这种操作,让人为之一振

val documentOffsetNesting = NestedRangeMap()

val document = KommonMarks.parseString
("""
# H1
_a_ __b__ *c*
## H2
[name](url:)
""".trim())

/* Add pairs to nested range by document marker offsets */
documentOffsetNesting[0] => HeaderNode(children = [TextNode("H1")], n = 1)
documentOffsetNesting[5] => [DocumentSection(name = "H1", n = 1), TextDisplayNode(mode = Italic, text = "a")]
documentOffsetNesting[19] => HeaderNode(children = [TextNode("H2")], n = 2)

看到 documentOffsetNesting[5] 的返回值了吗?它能够提示某个 Markdown 元素的路径,是在哪个标签里

对于 NestedRangeMap 来说,它会自动处理任何的区间碰撞(被分成五个大类型),将碰撞的部分替换成一个列表

val rm = NestedRangeMap()
rm[0...10] = "0 to 10"
rm[2..6] = "2 to 5" // "Full Overwrite" collision from index 2 to 6

rm[0] => "0 to 10"
rm[2] => ["0 to 10", "2 to 5"]

程序员在写 SourceManager 的时候,就只需要把某个语法结构的 startOffset 和 endOffset 记录一下就好了,NestedRangeMap 可以自动完成代码路径索引创建任务
duangsuse::Echo
写的太多不知从何说起... 时间还是不够啊 我不是把时间给虵(shé 🐸)了... QAQ 就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意 Parser 框架我还打算自己手写这些算法: TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了) RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现…
MultiMap 呢? 🤔

比如说,

很多时候,我们可能想写一些类似『CodeStyle』这样的程序,
更近一步,我们(可能)希望 IDE 有这样的功能:实时检查 CodeStyle,一个空格都不放过,并且能自动修复

我们可能希望,解析器能做最少的工作(比如,如果程序结构实际上没有被修改,修改的只是空格或者说字面量)
在不需要的时候,尽可能只做增量解析

我们可能希望,能知道程序代码在哪里有一些注释,有些内联文档

我们可能希望,能够通过某一个位置的 '{' 字符找到对应的 '}' 字符位置(比如,代码高亮的时候就用的到)
怎么找妥帖?应该先找到字符对应的语法结构,再找到这个语法结构的 '}' 字符

我们可能希望,把排错信息存储在一个『其他的』地方,而不是 AST 结构类本身里面(就是使用 SourceManager 存储 SourceLocation)
而且不仅仅这样,我们甚至还希望能够通过 SourceLocation 拿到某种语法结构,来实现索引功能(虽然 RangeMap 也可以实现,不过这种虽然没啥可行性,性能还更高)(没有可行性是因为对于 val n = 1 这种,用 SourceLocation 反向索引要求你只能选择一个『字符位置』索引到正确的结果,比如 val'v' 字索引到了,光标指到后面的 '1' 的时候就啥都找不到)

这些通通需要 Reverse index,有些再使用 RangeMap 就可以优雅地实现了(因为 AST 一般只会保留程序语义相关的内容,文档什么的都是忽视的,反向索引才行)


比如说上面的空格查找功能和 Marker highlight 功能

空格查找其实还真没有那么容易实现,不过靠 SourceManager 其实也不难,只是不容易『优雅』地实现

比如说,考虑以下简单形式化语法:

let whiteSpace = [ \n\r\t]
let parser = terms

nl = [\n\r]
terms = term*

term =
<def> name <=> value <nl>

name =
[A-Za-z0-9_]+

value =
string | number | boolean | nil

string = <"> .* <">
number = [0-9\.]+
boolean = <true> | <false>
nil = <nil>

这是一种描述方法,其中,每个重复的『终结符』都会留意之后的『终结符』是否匹配,如果匹配则自己结束匹配

比如
string = <"> .* <">

其中的 .* 自动先行判断后面的 <"> 是否 match,如果不 match 自己才能继续下一个,否则返回

假设我们有如下示例程序

def num = 1
def str = "Hello, world"
def boo = true
def null = nil

现在有一个人名叫小明,是个辣鸡程序员,非常喜欢捣乱,就把代码弄成了这个 B 样 😠

def
num = 1
def str
= "Hello, world"
def boo =
true
def
null = nil

这 TMD 还能看!
傻逼的人的选择必须是自己一个空格一个换行的删除,『手工』修复这段代码的缩进
聪明人的选择可能是 CodeStyle 自动 Format,可是,这是小明自己写的『编程语言』,你找不到支持这语言的格式化器
更笨点的人的选择可能是弄一个 DumpVisitor,手动格式化代码,但是很可惜,小明用的 IDE 非常脑残,它不推荐使用这种方式(直接 setCode(String)),一定要你手动生成修改代码的方案才行

我们现在的问题是如何生成一个修改列表(只有“在某个 offset 删除 n 个字符”这一个操作)
帮助小明的同事修改好这个程序 😉
duangsuse::Echo
MultiMap 呢? 🤔 比如说, 很多时候,我们可能想写一些类似『CodeStyle』这样的程序, 更近一步,我们(可能)希望 IDE 有这样的功能:实时检查 CodeStyle,一个空格都不放过,并且能自动修复 我们可能希望,解析器能做最少的工作(比如,如果程序结构实际上没有被修改,修改的只是空格或者说字面量) 在不需要的时候,尽可能只做增量解析 我们可能希望,能知道程序代码在哪里有一些注释,有些内联文档 我们可能希望,能够通过某一个位置的 '{' 字符找到对应的 '}' 字符位置(比如,代码高亮的时候就用的到)…
解决小明可怜同事的问题,首先我们必须得解析这段代码,但是解析的时候还不能忽略空格!

用伪代码,我们这么设计解析器(输入就是字符流,Scanner less parsing):

terms :: Parser [Definition]
terms = many term

wsP = satisfy $ `elem` " \n\r\t"
nameP = satisfy $ \x -> x == '_' || x `elem` 'A'..'a'
valueP
= keywordP "nil"
++ booleanP
++ numberP
++ stringP

term :: Parser Definition
term = do
keywordP "def"
wsDefName <- wsP
name <- nameP
wsNameEq <- wsP
keywordP "="
wsEqValue <- wsP
value = valueP
wsValueEnd <- wsP
newlineP
return $ Definition name value

这... 我不是来教大家写 Parser Combinator 的,也不是来传道 Haskell 的(我连 Monad 的什么 MZero 啊 MPlus 啊都不知道,感觉就是顺序 Application 的一种方式,勉强一知半解范畴论)

所以我们来看看小明同事的机制解决方案:

小明的同事解析出了可怜小明编写的可怜代码,下面是一部分:

definitions !! 0 == Definition "num" (Number 1)
definitions !! 1 == Definition "str" (String "Hello, world")

可是,机制的同事注意到了,这里没有给出任何的空格信息?那怎么判断呢?

于是还记得上面的 wsXxx = wsP 吗?

wsP :: Parser String

它会返回记录空格的,于是机制的同事利用了多年的 Haskell Monad 经验,单子化了生成算法,引入了优美的线性... 反向索引,这里实现大概就是『把所有的 Value 拿去 collect 对应的 key,然后再 put 回来』
或者直接『对每一个 key,都 putAll(values, key)』

于是就有了
getAll spaces $ definitions !! 0 == [wsDefName "\n", wsNameEq " ", wsEqValue " ", wsValueEnd ""]

小明同事检查了一下空格:
filter (not . (flip elem) " ") [wsDefName "\n", wsNameEq " ", wsEqValue " ", wsValueEnd ""]
=> [wsDefName "\n", wsEqValue " "]

0 不是空格,应该删除!
1 比预期的 1 空格多了 2-1=1 空格!

在两个 ws 的起始位置删除 1 个字符就万事大吉了,今晚吃鸡(喷

这下小明同事解决问题了,只要我们使用 MultiMap 存储空格、文档之类的信息,就可以做到检查代码风格这种小事情
duangsuse::Echo
写的太多不知从何说起... 时间还是不够啊 我不是把时间给虵(shé 🐸)了... QAQ 就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意 Parser 框架我还打算自己手写这些算法: TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了) RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现…
待会继续说『绝句』的,我一定要先实现这个玩意... (虽然可能不如 parser 们更优雅,因为虽然它是完整的模板语言,也有被当成 CPP 这种『逐行处理』的风险... 汗)

先一言不合就念诗:

int main() {
~绝句~
[唐] 杜甫
『两』个『黄鹂』;鸣『翠柳』
『一』行『白鹭』... 上『青天』
『窗』含『西岭;『千』秋『雪』』
『门』泊『东吴』。『万』里『船』
}

说这玩意是代码你们相信么?
如果这执行玩意可以打印出下面这种东西,你们相信么?

🌴🦜🦜🎵
🐦🐦🐦🐦👆🌥
🏠👅西岭📆*1000years:❄️
🚪🅿️东吴🚢🚢🚢🚢*9997km


🤔???
duangsuse::Echo
待会继续说『绝句』的,我一定要先实现这个玩意... (虽然可能不如 parser 们更优雅,因为虽然它是完整的模板语言,也有被当成 CPP 这种『逐行处理』的风险... 汗) 先一言不合就念诗: int main() { ~绝句~ [唐] 杜甫 『两』个『黄鹂』;鸣『翠柳』 『一』行『白鹭』... 上『青天』 『窗』含『西岭;『千』秋『雪』』 『门』泊『东吴』。『万』里『船』 } 说这玩意是代码你们相信么? 如果这执行玩意可以打印出下面这种东西,你们相信么? 🌴🦜🦜🎵 🐦🐦🐦🐦👆🌥 🏠👅西岭📆*1000years:❄️
PerlYuYan 略有不同,因为绝句是自带 scannerless 解析器的模板系统,而且不支持自然语言分词(我觉得这有益于可读性)

和 itorr 大佬的 e 网页templetJS 也有不同,但有相似

绝句也是模板语言,可是它的功能比普通的模板语言要多,而且支持 2D 文法,所以 templet 的这种语法:

<p> {{$1==1}}
<code>一等一</code> 是正确的,所以我显示出来啦 {{/1==1}}
</p>
<p> {{$变量一==变量二}}
<code>变量一==变量二</code> 是不是正确的呢 {{/变量一==变量二}}
</p>

可以理解为 JavaScript 代码

if (1==1) { writesM("<code>一等一</code> 是正确的,所以我显示出来啦"); }
if (变量一==变量二) { writesM("<code>变量一==变量二</code> 是不是正确的呢 {{/变量一==变量二}}"); }

而换到绝句,就是这样

@:: lang=HTML
@// 语言分词支持是可以自己定义的,可以用 Java 定义也可以用绝句定义分词规则,定义『标记、字符串、注释、标识符』这几种词条(绝句提供了更好的方法,对 Heredoc、String interpolation 和多重 string 这三种模式都有支持)
@? 1 = 1
<code>一等一</code> 是正确的,所以我显示出来啦
@? 变量一 = 变量二
<code>变量一==变量二</code> 是不是正确的呢

至于 if 的嵌套问题,二维文法可以解决

@?a
a
@ ?b
b
@ ,? e
d
@ ,
c
当 a 成立,输出 a
当 b 成立,输出 b,否则 e 当成立,输出 d,否则什么都不输出
当 a 成立,输出 c

templet.js 支持的
变量、子属性、变量判断、运算判断、数组循环、对象循环、管道

在绝句里都是语言支持,绝句还支持函数柯里化和函数作为值,函数即是所谓的宏

变量
@:: a -> 1
@:: a += 1
@write "a = $a"

子属性是 JavaScript 里的,绝句不面向对象,不支持

<dl>
{{#hash}}
<dt>{{键}} <dd>{{值}}
{{/hash}}
</dl>

这就是说如果要遍历一个对象(如果是数组,则使用 {{.}} 来引用当前项)
则就可以支持引用对象属性 {键:1, 值:2}

templet 的实现是『对每个循环属性名使用索引前缀』... 我不知道为啥这么做,好像是因为模板处理器没有递归所以害怕冲突?(可是都用 var 好像不会冲突...)

变量判断说了,运算判断比较复杂,templet 是直接执行 JavaScript Function 的,因为绝句是单独解释的编程语言...

数组循环、对象循环,绝句因为是 Java 编写所以支持反射对象访问,不过对绝句来说 Object 会被映射为 Map...
函数管道,绝句支持函数式编程,可以这样定义一个函数:
@无聊的操作(数字):从『将『数字』转为『字符串』』解析数字
@无聊的操作二(数字):
let 函数:(将...转为『字符串』++ 从...解析『数字』),
in 函数『数字』

绝句的参数列表自然是要求比较高的... 目前想到了一个指定 index 的方法 @『a,2』操作『b,1』。 允许绝句代码这样使用 @操作(b,a)。 (或者 @操作(b)(a).

#Android #GUI https://github.com/ayaseruri/TagsView
是时候好好学学前端了...
看到前端们的 HTML, CSS, JS, CSS 动画,又被自己菜自闭了。

勉强仗着自己还会点 HTML、SGML,会使用 DOM 以及能够抽象/符号化/结构化分析问题以及比别人快点,往往能和作者共意稍微有点好转的倾向...

duangsuse 在抽象分析的时候稍微看得清那些眼花缭乱的 API 使用、存储和算法结构一些

啥时候我要去写点前端。