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
我再也不想写 Trie 树了!
第一次尝试就是非常失败的
眼高手低的最佳实例系统(指网络小说圈玩梗)
平时总是觉得数学有点难,其实除了自己真的是数学不好以外,还是有其他原因的。

数学,或者至少是『中等』(也就是高中水平)程度的数学,是运行思维的『形式化』方法。(其实也没有那么形式化哈)

数学也有很多抽象模型:数、运算、结合性、数轴、区间、集合、对(pair)、未知项和关系、逆运算、直角座标、极座标、映射、命题逻辑、微积分……

数上面的很多运算,除了可以用一个个计算的纸面表达方式(比如,递等式、开根式、除法上位,减、乘法十进制移位叠加……),其实也可以用其他的思想去表达:
+ 加法就是重复 "1"
1 + 2 = 1 + (1+1)
我知道这十分不好理解,不过『0』『1』的确应该是最特殊的数字了(但和二进制无关),小数分数都不够特殊。
从 1 开始的加法,可以直接从『数羊』这个行为想。

+ 乘法就是重复 "+"
1 * 3 = 1
11 + 21 = (1+1+1... (*10)*1 +1) + (1+1+1... (*10)(*2) +1)
解类似『我们这每人都有 10 只羊,一共有多少只?』这种问题的时候可以使用。
「羊」就是羊,「小明」的羊和「小红」的羊其实没有区别,都是「羊」。
数学允许我们在这个抽象角度上忽视掉无关量来思索问题,但需要注意的是,数永远代表着实际的含义:羊、用户、一棵树的分叉数目……
所以也就出现了『有效』和『无效』的数以及描述数值的『表达式』。
当然,表达式也是可以『惰性计算』的,就是说你 1+2+3 你可以描述成 (1+2)+3 而不一定非得计算时求 3+3 等于几

求解『两个足球队A,B(分别是a人,b人的)一对一PK能赛几场』这种问题也可用到乘法的定义:
显然对于 A 队伍的每个人,都得和 B 对的每个人 PK
所以,我们把 A 的每个人视为 (1+1+1... *b),这里的 "1" 表示一次比赛,最后实际上是b被『重复』了a次。
就是 solve(a, b) = a*b 了,或者说 solve = (*)
求解配对问题也很类似,不过要去掉自己和自己的配对:
paircount-of(n) = n*(n-1)
这也是一种抽象:不管你是什么,我只知道你是 "1"
不管你是小明还是小红,我只知道为了求得所有配对,你要和除自己外(n-1)个数的人配对。

+ 求幂就是重复 "*"
解类似可以用到乘方运算的定义:重复了『指数』次的『底数』
比如说,国家实现「全面二胎」的理想情况是,每两个人都有两个宝宝(惊恐)
如果一个住了5对夫妻的豪宅,照这样下去3代,第三代多少人?(无论死活长幼,子代一定与外人结婚……)
5*(2**3) 得40人,其中 (2**3) 是8,算的是一个家庭三代产生的宝宝数目。
想象一下:
+ 第一代的夫妻有2个宝宝
+ 第一代产生的2人找到配偶后,每人也都有2个宝宝
+ 第三代的宝宝长大后也是一人两个宝宝
+ 2*2*2 = 2**3,就是这个逻辑,算的是三代后的宝宝数目
如果要算总人数可以用递推函数式表达:
total-count(b, gen, c)
|gen == 0 = c
|else = c+total-count(b, gen-1, c*b)
其中b是每代娃娃数,c是此代总人数,gen是代数
当然是可以优化的

+ 求对数(logarithm)就是给定『乘』的并列次数求并列项
这个相对困难些,用处比如: log10 100 = 10,你 floor(log10(x)) 就可以得到十进制位数了(二进制有 Striling 公式,不过也是可以用对数算)
对数还应该提到 ln 和 e,不过我不知道是为什么。
+ 求方根(root)就是给定『乘』的并列项求并列次数,这不是太困难,因为可以直接除:
sqrt(2) = 1
sqrt(n*2) = 1+sqrt(n)
求最终解构的乘法数目(也就是可以除的数目),所以不困难
+ 除法(a / b)就是算 a 里出现了几次 b
比如:小明一家每天炖一只羊,请问:50只羊够他们炖几天?
+ 取模 (a mod b) 可以保证 (a mod b) 结果小于 b,如果不够除,则值是a。
它的「循环」(因为『除』是按『一块一块的b』来算的)性质偶尔被拿来作指针碰撞(移动啦……)
整除 (a mod b) == 0 的性质偶尔被用来作图
+ 小数就是把一切乘一个倍数,以某一种最小单位(而不是整数 "1")执行计算
+ 小数和分数都是可以互化的(不过有些分数只能化成无限位长的小数……)
不过分数是有一个基本性质: a/b = (a*c)/(b*c)
分子分母同时乘一个c,分数值不变。
举例:(9)班有10位同学,全班的同学一共有20只眼睛,每人都有两只眼睛(20/10,10位同学平分20只眼睛,正好一人一双)。
某个学校有10个班,每个班的情况都是这样,请问这个学校每个人有几只眼睛?
(20*10/10*10) = (20/10) = 2
由分数的这个性质我们可以在不实际除法计算的情况下,得出这10个班级里也是一人2只眼睛。

这不是小学数学,同时,我想说:现在很多程序员真的是连小学数学的所谓『数学逻辑』『数学性』都败光了。
数学的优雅性和准确性,嗯哼?
老实想想,这些看似『低级』的解释方法,自己现在还记得几条?自己编程的时候又用到几条?

当然数学也是可以涉及除了表达式本来『结合顺序』外的基本逻辑的:
+ 函数可以是『分支函数』,实现了控制上的『判断』
+ 函数可以定义成递推式的形式,也就是递归『重复』

关于是否的确能够『循环计算』,当然是可以的,毕竟至少有应用序 Y 组合子(applicative order Y combinator)嘛
Y = \f. (\c. (f (c c))) (\c. (f (c c)))
注意这里两个 \c... 是一样的(也自然是循环的『样子』喽)

稍微有点数学/逻辑学爱好的人一定知道逆波兰记法(reverse polish notation):
(1+3)*6 等价后缀(postfix)表示的 1 3 + 6 *
后缀表示我就不说什么了,一个 LIFO。(后压入的先弹出,换到Java字节码就是先求值push的最后pop,按此顺序『倒数第一参数最先求值』)

数学主要的缺点是在表示上:虽然一些人眼里数学的确是很『天才』(当然不天才也没什么过错)

数学的高冷风格(什么希腊字母啊……把「美」和「丑」、「简洁」和「冗长」、会和不会分得太开)反而是不好。

比如说,『年久失修的伦敦大桥终于塌了。』
用数学的话说就是『伦敦大桥塌了。』
这很简洁,可是也包含一点不那么令人满意的因素:
+ 一些数学者认为,『年久失修』这个信息是可以从『大家都会』的知识上下文里推导出来的,所以不需要写明。
并且当他们往下说的时候,已经默认你把『伦敦大桥』想成是『年久失修』的伦敦大桥,可是,万一有些人认为它只是『设计不当』,或者干脆笨一点,没想过『伦敦大桥』是什么样的,该怎么办?
这类人往往被数学者称为『IQ低』『理解力差』。
+ 『终于』可以删掉而完全不影响这句话的数学含义,可是它实际上是有作用的:表达了作者对此事件的情感。
虽然这一条你看不出它对下文有什么用处,可保不准缺了它,就会使得本来不多的相关表达更少了隐含的信息。

其实写不写明这是个仁者见仁的问题,实在是和『艺术是瞬间的还是永恒的』这个问题一样争论不休,自然是各有各的好处,所以我们说点别的。

一般来说,接触某个新问题的人都必须先大概知道为什么会有这个问题,以及解决这个问题的示例,这样才能更好的理解问题。
一上来就以一种 a=1 => ab 式的语序(当然一个问题里还不止这一种)说定义,甚至弄出什么 若……对所有……且……当…… 这样连在一起分不清主次和句子结构的东西,那不用说自然是只会从自己的角度考虑问题的『聪明人』了。
从这个角度看,《算法(C++)实现III》一书可以说是正面例子了 -- 讲算法先讲道理,全书内容信息依赖安排得当,与目标群体的原本知识系统相契合。
Robert Sedgewick 就真是字面意义上的『教授』,名副其实。

大部分人会觉得计算机科学是为计算机解决问题而生的,而数学是为人解决问题而生的。

其实好像一切都喜欢反着来:计算机科学看起来是更考虑人的感受,而数学则是半人半机。
为什么数学有时候反而应该是『更适合计算机阅读』?其实是因为数学想表达的东西太宽泛也太松散了,
数学家说数学有很强的逻辑性,其实让自然语言学家来看看:的确是很明确 -- 比如,p q 是一个『对(pair)』里两个项目的名字
这很准确,但这会使得我们不知道 p, q 里哪一个是『主语』、哪一个是『宾语』,即便实际上哪个都不是也哪个都是(因为它们的关系具有对称性(symmetric)),这才是数学嘛。
编程时我们一般认为从左到右,左边的是『宾语』(操作的对象,相等判断的变量) 比如 [i++] == '\0' ,为什么我们不反着来?可是如果命名为 p q,理论是优雅了,写起来可是略微『美丽冻人』。
上面这个问题也出现在合并查找(union find)的算法里,如果引入逻辑上的传递性(a R b; b R c)而不止『数对』,就会很容易解决『没主语』的问题了,而且不会影响到逻辑的严密性。
其实不少数学问题在正常人看来都是描述『语序不当』甚至略微『成分残缺』,之所以觉得没有问题是习惯了而已,但习惯了也不能代表是最好的做法。

数学习惯的就是把一堆『特化』的东西(比如「逆命题」「简单命题」「复合命题」「分数」「小数」「无理数」「非负数」……)和没特化的大体混在一起,所以你得自己给他们归到树型,你得知道「XX命题」都是「命题」、「XX数」都是「数」…… 并且好好总结一下他们的特点和计算 -- 这不就是编程时高层抽象的多态吗?
如果只有看一个问题的能力,数学水平不容易提高,但是数学家也真是应该好好改正一下自己的说话水平了,如果不能以初学者的视角看问题,就找不到在表达上优化的方法。

计算机科学,它不像数学里,有些东西实际上是来自于数学者的『直觉』的,直觉这个东西往往很有用,但对传达知识来说,它会严重影响其他人对知识的理解(因为你「隐式」也就是「含糊」地引用了某些概念,比如什么「即得」啊「易证……」啊「只要证……」啦)。
许多数学书,比如你们的人教课本上,就会在某些问题上略过一些直白的解释,使得数学感不好的人保持半通不通只能靠做题找感觉的样子。(当然即便做了题考满分也未必代表彻底理解了)

计算机就像是什么都不会的婴儿,它很有执行能力,但完全是一片空白,所以我们不得不更加严密地处理『导入』问题(因为它和所有人一样,是有所不知的,最重要的是它比任何人都「蠢」,只会你教它的),仔细界定问题涉及到的计算、模型,这一点是许多数学问题从来不曾想的 -- 反正那一切都是数学,数学就是一堆纠缠在一起『密不可分』的模型概念。

甚至一个偏序理论,都或多或少地和几何纠缠在一起,问题得不到根本上的简化,自然对大部分人来说难以理解了。

所以计算机科学和数学对某个问题的解释,往往是数学的含糊一些,计算机科学的长不少;像是很多计算机会区分整数和浮点,而数学认为数可以是整数、分数、小数、非1自然数……,甚至是虚数)

同桌问我(一个app)为什么分苹果版和Android版,我反问他:
猫能吃鱼,狗也可以吗?所以食物得分开给。
他又问『那为什么这个(web应用)不分了呢?』,我就回答:
猫是动物,能叫;狗也是动物,也能叫,所以有时候不用分太开。

难道我要提到,Android和iOS本身「软件包『格式』不兼容」甚至「Android有libdvm.so,iOS没有」「基于JavaScript和DOM只需要他们都实现了这种(WWW)技术即可,和(原生)应用大不一样」这种技术细节?
其实我也清楚这很不准确,但我觉得对一个问题,兴趣和简明的第一印象,远比定义是否准确更重要吧。

只是说一下看法而已。 #CS #Math
我可以很轻松写 Lisp 系的解释器,也可以同样轻松地写 JSON 解析器、计算器;但是 Trie 树实在是难写啊!怎么还分四种情况?还要把叶子上岔出分叉来?

可是如果我不写 Trie 树,就没法实现自定义的二元运算符了…… 因为没有线索树的话,扫描 a+b 这种就不知道该何时分 a,b 这些名字和加号了 emmmm 😂

我不敢用别人的实现,我觉得他们实现得肯定更复杂更难看,我只需要一个简单、递归的 API 就够了……

还以为能够写出带启发式合并的 Radix 树的
RangeMap 什么的也很绕啊…… 不过不像 Trie 树的泛型那么烧脑
trie.zip
2.1 MB
已经失败了,初试不利。
看来只能量力而行,我打算这周暂时不写 Trie,更不可能写 Radix 树了,先做点力所能及的工作吧。

但是,其实也是有好的地方的,因为我的 Feeder 写的可以说是史无前例的简单、不强耦合,简单到我都觉得有点不正常,但它就是 works
至于 Trie 的泛型和建支问题,我相信以后会有改进。
已经累死(这是今天上午
这居然是 duangsuse 写 JSON 解析器的方法??!
自从用上 Java 后,我写算法的能力越发降下了;本来我还想 peek 一下 BST(binary search tree) 的 mid 然后判 GT LT EQ 再二分找的 (GT -> first(LE); LT -> last(LE)),JDK NavigableSet<E> 都直接弄好了……
duangsuse::Echo
自从用上 Java 后,我写算法的能力越发降下了;本来我还想 peek 一下 BST(binary search tree) 的 mid 然后判 GT LT EQ 再二分找的 (GT -> first(LE); LT -> last(LE)),JDK NavigableSet<E> 都直接弄好了……
有了 RangeMap,可以实现光标下的符号高亮解析这样的功能,当然没有的话就几乎很难实现了,太慢

虽然只能在 JDK 上用……(Kolin 好像没 sorted sets?)
完全迷了
比较 freestyle (迫真
Forwarded from dnaugsuz
如果用我之前设计的绝句来说,可以这么写(只是说可以,不是说推荐),比较一下:

事 f(数、实): 元二<实、浮> {} 
数据物 S(x: 字、y: 数、z: 实): 存储
常S s = S()
对你s,元三(x、y、z)。里的,它置为1
你s,元三(y、z、x)。批量置为(元三(3、3.2、'x')) — 肯定不是标准库里的
f(*你s,元二(y、z)。)
f(*你s,元二(y、y)。)

……简直写不下去
不过『存储』这个特性蛮高级的,我还没设计好emmm……

https://t.me/dsuse/11777
这是一些随记,还很不成熟,我最近是没时间实现的,不过我在写一个解析组合子框架(ParserKt,已经拿它写了一个JSON解析器尚待修完……)
Forwarded from dnaugsuz
简而言之,如果把这些代码翻译到绝句可以用这些特性:

事 Name(x: T0, y: T1): R 为……
当然后面的缩进块是可选的,也可以用 {}

元一<T1> 元二<T1, T2> 元三<T1, T2, T3>…… 元组类型
之所以不说是『一元』『二元』,是因为数字可以这么表达:二十三+二十万二千一百

你,……。
这种『第二人称文法』
当然它和 let 是有区别的,区别在于有 (你a且大b、c) (你x在a且不在b) 这种语法糖

所以C-forall的所有 x.[a, b] 在有存储抽象的绝句上其实都是 你x,元二(a, b)。的形式,我搞不懂的就是何时编译器要选择存储、何时要选择值,打算加入多态特性的。(类型推导麻烦一些)

Name(*Expr)事 …(变长 Name: Type)… 就是变长参数传递了,所有支持 算符的事 项目n 的都可以这么照 *

字(Char)、数(Int)、浮(Float)、实(Double)、效果(Unit) 这些
「数据物」,还有基于『分配』(而不是『值』)的「物」
因为我实在不想引入 struct 之类的东西,我觉得它使得语言的数据类型被过分分割了,写起来莫名其妙,继承自分配是等价用 struct

不过没有东西等价 struct 的,因为「struct」必须有架构器…… 就是说 数据物 Name(…): 分配 了……

最后的
v.[x, y.[i, j], z.k]
就是(好像很难看了呢,因为逗号简记法不能写在一行里……)
你v,
元三(x、你y,
元二(i、j)
z的k)

最后的 f().[2, 1] 就是
你f(),元二(我[2]、我[1])。

说白了就是在翻译 Kotlin……
Forwarded from dnaugsuz
也的确是,不过如果要在类型级别是不是很困难?因为时间可能和输入相关