/tmp/duangsuse.sock
23 subscribers
303 photos
3 videos
92 files
337 links
从 duangsuse::Echo (@dsuse) 跟进出来的分支,将在作者恢复原帐号访问的时候合并删除。
Download Telegram
Forwarded from Deleted Account
虚表是每次递归查找到了复制,并查树也是
两者都是传递性,不过一个是为了缓存(也是性质)传递、一个是一元相关性传递
Forwarded from Deleted Account
cats 就是找某个 node 最远的爹,然后一下解决所有娃子的传递性问题,concat 到一起去
对应的 find 实现是为了践行这个理念,顺便有缓存剪枝

可是具体的,比如 find 的 (make me dptr) 到底是不是删掉也正确,不清楚...
我终于知道为什么很多出版物里都能找到不完美的地方了,原来尽可能去修改就有这么难啊...

本来已经修改了很多地方,换掉了不少有问题或者可以改进的表达,再看一遍,我又找到比原来还多数目的...
目前我依然在修改,将在基本完成后对内容进行最后的校验

今天明天内,我会给它加一些 JavaScript 小挂件来提升阅读体验

目前还在对内容进行校对
当然是不会存在什么断章取义的,我会给每个链接部分都加上预览的功能,方便没法科学上网的看
此外,我还会和谐掉一些内容,放在 <template nsfw> 里,再随便多加些点评
#acg 啊... 好可惜啊 #China
Forwarded from 荔枝木
“我的朋友们啊,”他说,“我——我——”

但是他哽住了,他说不下去了。

他转身朝着黑板,拿起一支粉笔,使出全身的力量,写了四个大字:“熊猫万岁!”

然后他呆在那儿,头靠着墙壁,话也不说,只向我们做了一个手势:“关站了,你们走吧。”
This media is not supported in your browser
VIEW IN TELEGRAM
刚才被冰封批判了一番,两年进步的的确是很少了(
我还有脸讲什么 #Haskell, Monad 都不会

连 Identity/Reader Monad 都还无法默写,真是...
而且我还没有拿 Haskell 写过解释器,一个计算器求值的都是常量、纯的无参表达式,这怎么能叫做解释器呢...

推荐大家去关注 github.com/HoshinoTented 大佬,大小姐很擅长各种算法和 Kotlin... 和 Haskell,我就只是会点无聊的... 泪目
https://blog.hoshino9.org/2019/07/26/how-to-create-a-wonderful-type.html

艹,刚才看星野野大佬的博文才知道 newtype 是简易 Product 的构造方法(不能是 Sum type),之前欧阳继超大佬的猫论指南
就只告诉我 newtype 是定义同构类型(newtype Identity a = Identity { runIdentity :: a })...
啊,那个我没有考虑到,其实... 写的插件还可以讲一下,所以前端给 #HTML #JavaScript

之前因为 Fx 阅读模式里面 link anchor 有 underline(decoration) 所以想加去这个的 CSS,后来才知道本来就没有...
如果有的话,可以 a { text-decoration: none; }

接下来简要说说几个小辅助插件了

首先是 NSFW Template,这使用到了 HTML5 的新 <template> tag
bound 本来确实可以利用 property/reflect/proxy 来比较魔法的(而且不会担心性能问题,可以缓存)
不过有点长,而且不明显,所以这里用简单的方法

function bound(self, meth) { return self[meth].bind(self); }
function intoIter(xs) { var i = 0;
return function
iterator() { return (i<xs.length)? { value: xs[i++], done: false }
: {done:true}; }; }
function tryIter(xs) { if (!Symbol) return intoIter(xs); return bound(xs[Symbol.iterator](), 'next'); }
function forIter(it, f) { var xc; while (xc = it()) { f(xc.value); if (xc.done) break; } }
function args2ary(argsv) { var ary=[]; forIter(tryIter(argsv), bound(ary, 'push')); return ary; }

Function.prototype.flip = function() { var method = this; return function call() { return method.apply(method, args2ary(arguments).reverse()); }; };
Function.prototype.curry1 = function(x0) { return this.bind(this, x0); }
;
Object.prototype.lets = function(f) { return f(this); }
Function.prototype.andThen = function(g) { var f=this; return function composition(x) { return g(f(x)); }; }

foreach = tryIter.andThen(bound(forIter, 'curry1'));

function makes$_(fname) { return function(it) { return it[fname](); }; }
function makes_() {
var it = new Object();
return new Proxy(it, { get: function(t,a,p){return makes$_(a);} });
}
_ = makes_();

cssSelect = bound(document, 'querySelector');
var merges = bound(document, 'importNode').flip().curry1(true);

其中业务逻辑 部分,上面的留作代码重用
function showTemplate(attr, init) {
var templates = cssSelect('template[' + attr + ']=""');
var prepend = function (nd, x) { nd.parentNode.insertBefore(x, nd); };
templates.forEach(function(x) {
var mix = merges(x.content);
if (typeof init =='function') init(mix);
prepend(x, mix);
});
}
function removeElements(cs) { cs.lets(cssSelect).each(_.delete); }

function addCSSClass(cl) { return function(e){ e.classList.add(cl); }; }
showTemplate('nsfw', addCSSClass('nsfw'));

我还得写很多.... 所以代码重用、优先划分好模块非常重要

== Abbr view
这个逻辑比较简单,类似 NSFW template。因为在一些设备上不能使用 "hover" 查看 abbrev... 只好在点击时在后面插入内 abbr 的实际容了。
写入的时候先新建一个 attribute,用于记录 abbr 已经被展开,每次 switch 的时候判断一下 shown => hide、hidden => show

== Night

== Reflink Preview

== Music Player

== Footnote Xref

== Contents Tree
记录一种思维方式:

刚才在回答 @Zhai2333 大佬的问题时我提到了树(Trees)

我说(伪jzm 🐸

但是我想,我见到你们这样热情啊,一句话不说也不好。所以你刚才你一定要——在宣传上将来如果你们报道上有偏差,你们要负责。我没有说这很简单(逆向 JavaScript),没有任何这个意思。但是你问……你一定要觉得要问我……对这类代码保护措施有没有方法。我能不能写 AST 模式识别重构器?AST 处理是编译原理优化编译入门技能,我怎么能不会?
...
你们毕竟还 too young(太年轻),明白这意思吧。我告诉你们我是身入树状图了,见得多了!啊,OI 入门的哪一个算法我没讲过?码农他们——你……你们要知道,美国的 @ice1000 ,那比你们不知道高到哪里去了。啊,我跟他谈笑风生!所以说编程啊,要……还是要提高自己的知识水平!懂我的意思——识得唔识得啊?(懂不懂啊?)

(逃跑,,,我很菜的,不过希望有点幽默的说

我提到了树和 OI,这不禁使我想到了之前看 OI 入门书的二叉树,然后我就不禁考虑到了一个问题:完全二叉树(除了最后一层 leaf 外、每一层都有两个子节点)如果层数是 n 层,那么一共有多少个节点?(弄错问题了... 是第 n 层有多少节点、那个除了递归或者 sum 外,或者手动试着代数化简、我没办法)

本来是很 immediate 的内容(发答案了,就是 (n-1)**2 + 1)
n-1 是因为第 0 层很特殊、 +1 是补回第 0 层

我却想了一会(虽然开始我隐约有正确表达式的直觉,虽然只是一个伸展的树形图样,然后两层之间存在某种运算连接起来的那样)而且还体会到了之前完全不懂数学的恐惧... 😨

现在我得到了答案(莫名其妙就又想到了,噢,原来不每层就是前面的 n-1 层 *2 么)(我的递归素质就有这么差....)

同时又得到了两个思考方法:

1. 考虑递归程序的时候,把每层看成是自己想要的数据。
比如上面的算层数、每个节点就是一个『1』
这样你就能有
root = 1 + bin
bin = bin + 1 + bin
这种图的直觉了,然而光有它还不够,你要能看 (1 + 1) | (1 + 1) 换得成什么...
给我一种感觉 好像要反过去回溯的时候 才能感受到这种可能...

2. 考虑递归程序的时候,只看一层不够就看两层

bin0 = bin1 + 1 + bin1
bin1 = bin2 + 1 + bin2
....
bin2 = 1

3. 可视化数学计算,这样你就可以把数据结构和数学式子一起对称着看了

比如,要有把乘方看成树形的能力:
2**3=
(2*2)+2
/ \
2 2

4. 最后上面的问题我是怎么解决的,

首先,我不知道为什么就没有看根节点...

然后,每一层都有 2 个子树、直到 (n-1) 层(最后一层没有子树只有叶子,要不然你也不能非惰性地构造 infinity data structure)

本来我开始没有想出来的原因是我觉得这是递归的,没法直接算...(其实也是可以的)(废话)

后来发现,其实要求第 n 层(第 0 层就是 1),只需算 n**2 即可(每层都叉一次 *2、叉了这么多次,有这么多个 1)
...

唉,现在就只有这点水平

不过你看完后就发现,其实“数学”和编程从本质上看都是没有差别的,解析数学、微积分什么的也是属于数学的 buff,但都是很『基础』的东西
This media is not supported in your browser
VIEW IN TELEGRAM
日,数学... 为什么变化起来就有那么多特例
难学啊,排除计算也一样....
回来的时候我考虑了一下那个 C 莫名其妙的多维度数组的实现,以及为什么我平时用 y-first scanning

y-first 的意思就是说,当我扫描二维矩阵的时候,我会用 matr[y][x] 这种索引方式
因为一般数据都是从左到右扫描输入的,这样的话,按折行(下一个 y)切分会好写一些,不需要去每个去专门索引 matr[x][(0..len]]

前者的图示看起来像是这样:
int matr[y=6][x=4]
x
3 0 0 0 0 0 0
2 0 0 0 0 0 0
1 0 0 0 0 0 0
0 0 0 0 0 0 0
+ 0 1 2 3 4 5 y

当我们需要 ((int[6][4]) matr)[1] 的时候,得到 (matr + 4*1) 的位置
得到了
2: 0 0 0 0 0

int matr[x=4][y=6]

y
5 0 0 0 0
4 0 0 0 0
3 0 0 0 0
2 0 0 0 0
1 0 0 0 0
0 0 0 0 0
+ 0 1 2 3 x

它看起来像是这个样子

不过这很 naive:因为它只是幼稚的数据描述策略问题而已,还有一个不那么 naive 的:映射到一个一维数组,怎么递归去计算这个 a[n][m] 的索引呢?

你以为这是一个『n 维数组』,实际上它是一种对存储空间的描述,实际上内存是连续的,对于这种『每个维度长度都不同』的该怎么办呢?

方法就是类似上面的计算。
C 的数组给人不符合直觉的感觉,和指针、const modifier 什么的一样,很不对称...
我真的不知道这些和 typenames 有啥不一样的地方,为什么他们就是用一种不同的方式来描述

char * 这个类型是 char 的指针
char 这个类型是 char * 的指针

const char[] 是 const char 的数组
(C++) char &a[] 是 char 引用... 的数组
char (&a)[] 是引用数组 char[]

最后,
char*
是 char 的指针可是 char[n][m] 是代表一个多维数组,可是这个数组呢?
如果看作是数组的数组,实际上它是 长度 n 关于 char[m] 的数组
听起来很正常,问题是 (char[n])[m] 看起来怎么都类似长度 m 元素 char[n] 的数组... 就很奇怪

我...

其实这么倒着放还可能是有原因的,因为怎么看好像区分一维数组和多维数组是一个蛮正常的思路,数组的数组才不应该存在
可是对编译器就有这么一个问题:怎么分配它、怎么拿到元素的指针

分配数组的数组很简单,看看这个 Haskell 程序:

module Dim where

type Size = Word
type Dims = [Size]

allocSize :: Dims -> Size
allocSize [] = 1
allocSize (l:ss) = l * allocSize ss

这个『神奇』的递归程序是做什么?就是 items * size 而已,而 item 的 size 就是下一层的大小,如果没有了实际上还得在叠加一层,这时候用 (*) 乘法的透明操作数 1 即可

当然,allocSize <=> foldl (*) 1 <=> product ...

allocSize [1,2,3] = 1 * 2 * 3 *1 = 6,所以说 array 是 homogenus product type。

那么因为乘法是有交换律的,所以在两种数组上分配大小计算方式都有效。
再来看看索引的计算

getElemPtr :: Dims -> Dims -> Size

现在你有维度们的长度,给你索引们,请问:这个元素存在哪个位置?

对于递归的数组,还是比较简单的
思量一下,算 [1][3],首先我们有一个 [1] 的大小,然后乘索引 [3] 即可

getElemPtr [] [] = 0
getElemPtr (d:ds) (i:is) = (getElemPtr ds is) + (d*i)

getElemPtr [1,3] [0,1] = 6
getElemPtr [1,3] [0,2] = 9

这样就好了,欸,好像是错的吧... 总之就是和 C 的弄混了... 算
其实本身没有错(迫真),我的例子错了...

其实这个的确写对了,就是递归求基索引(idx*dim),再加上自己的索引而已,问题在于 size 没有得到显式地体现(迫真

比如有 (a[2])[10],(a[0])[1] 就是 1、(a[1])[0] 就是 10

这算是递归入门... 至于那个 C 的多 dimen 数组,倒过来算就可以了,做出来就行...
/tmp/duangsuse.sock
啊,那个我没有考虑到,其实... 写的插件还可以讲一下,所以前端给 #HTML #JavaScript 之前因为 Fx 阅读模式里面 link anchor 有 underline(decoration) 所以想加去这个的 CSS,后来才知道本来就没有... 如果有的话,可以 a { text-decoration: none; } 接下来简要说说几个小辅助插件了 首先是 NSFW Template,这使用到了 HTML5 的新 <template> tag bound 本来确实可以利用 prope…
那我也就只好快速地讲一下这七个插件了,发了文我还有很多事...

首先是
== NSFW template,暴露主要函数 showTemplate(attr, finit)、辅助函数 selectDelete(cs), addCSSClass(name) 就 OK 了

function showTemplate(attr, finit = doNothing) {
let templates = cssSelect(templateHasAtt(attr));
foreach(templates) (function(it) {
let into = merge(it.content);
finitize(into, finit);
prepend(finit, into);
});
}

selectDelete = cs => cs.lets(foreach) ( _.delete );
addCSSClass = name => nd => nd.classList.add(name);

这个插件就是去添加一些可选显示的内容,包裹在 <template attr> 标签里

== Abbr view

这个逻辑比较简单,类似 NSFW template。因为在一些设备上不能使用 "hover" 查看 abbrev... 只好在点击时在后面插入内 abbr 的实际容了。
写入的时候先新建一个 attribute,用于记录 abbr 已经被展开,每次 switch 的时候判断一下 shown => hide、hidden => show

暴露主要函数 hookAbbr(nd) 就可以了

cssSelect('abbr').lets(foreach).lets(hookAbbr);

逻辑也很简单,就是设置 onclick,二元状态机,在自己之后展开

function _hookAbbr(nd) {
return () => { if (!getAttr(nd, 'shown')) {
insertAfter(nd, makeTextTag(nd.title)); setAttr(nd, 'shown');
} else {
nd.nextSibling.delete();
} };
}
function hookAbbr(nd) { nd.onclick = _hookAbbr(nd); }

什么的
...我多么希望快速的讲完啊,可是又讲的很细,现在很晚了...

说功能和要点算了。

Night 的要点就是:提供文档阅读视图的用户风格选项

首先它自动根据当前时间,切换默认 Night/Day,其次,它支持:

+ 配色风格:Day/Night/Old
+ 字体:有无衬线
+ 字号:大小调整
+ 容器宽度:大小调整

实现:提供一个 autoDayNight() 导出逻辑来实现自动配色,没有其他要点。

Reflink preview 的要点

按照 invisible dt 提供的元数据,给指定的 anchor 标记(需要使用 id 来指定)挂上钩子,在 iframe float 里弄出来个(可以 close 的)内容,并且提供原本内容的链接

Music player 的要点:对 WebAudio 的一个简单前端封装,不支持播放队列什么的,只能全局放一个(动态 WebAudio)
可以调整音量、pos 和播放/暂停,支持画 WebAudio 的 Spectrum Canvas 图

本身暴露一个 playAudio(url) 的 API,使用 XHR 流即可

Footnote Xref 的要点

在某个 section 下面(扫描直到下一个 h*)
扫描所有的 subscript 区段起始的 footnote 元素,

接下来利用 class .fn 查找内容的 footnotes(foreach),
按照 subscript 对应到实际的 footnotes (自己动态建立 id,a href 到对应 footnotes,同时 footnotes 也建立 a href 到自己的动态 id)

foreach cssSelect('.fn') {
let footnote = footnotes[fnid(it)]
let [fid, rid] = makeId.times(2);
let fa = makeAnchor(footnote, fid);
let ma = makeAnchor(it, rid);
drawTo(fa, rid);
drawTo(ma, fid);
}

重要的是生成双向引用需要同时拿到两个引用

最后 Contents Tree 的要点是 DOM Tree recursive pattern destruct

subsec(root)

这个函数构造 <h1> - <h5> 的列表,
从 root 开始枚举它的 nextSibling,

如果不是 header tag 则跳过
如果是同级别 header,加入
如果是低级别 header,递归扫描,加入
如果是高级别 header,结束扫描

阅读时间的要点

就是提供字数统计(只需要算 length 即可,大概的)、行数统计、引用 链接 和代码统计、header 统计
本身虽然可以做比较好的预估(比如说,pattern matching)
可是不大算这一点,只需要算好后写 min 、字数行数拿 , 3 位隔一位就可以了,点击以显示详情
关于 Curry 函数

Curry 是什么呢?本来 curry 的意思是 Lambda composition ( f.g = \args -> f (g args) ) 很难去接受一些『多参数函数』(要不然处理起来就很迷人...)
于是 Lambda calculus 有个 currying: \x y. x = \x. \y. x (原本一个多参数的 lambda 现在变成了多个单参数 lambda 的组合)

这样就有一个好,因为 lambda 抽象(abstraction) 的 variable substraction 可以给一些项目提供参数,而未必要立刻『实际执行』这个函数:

add = \a. \b. (a + b)
add2 = add 2 = \b. (2 + b)
add2 1 = 3

它可以提供『不能重写』的参数默认值(因为那里 2 就是立即值或者 UpValue 了,递归的时候这个东西不是参数的)

可是很多函数式编程语言,比如 Haskell 是这个样子,JavaScript 就不是这个样子。

不过有 Function.prototype(参见我之前写的 LuaOOP 原型面向对象继承实现).bind
可以给指定默认参数

不过,指定就最好得一次指定完,不然很麻烦(因为 this 要传来传去的... 而且你还得手写 curry 参数的调用)

我希望这样的 curry:

var puts = bound(matrix, 'putXY').curry()
var puts00 = puts(0)(0)
puts00('0,0')

然后我早上赖床(逃跑)的时候就在想,该怎么实现这个多参数 curry 呢?

开始我很 naive 地想到,好像这是在针对 accumlator(计数器一样的东西,总之就是不断被『从自己构造』)
去操作,于是非常自然地想到了 foldl, foldr (其实 foldr 的确是可以的,而且 compose = foldr (.) id,不过这里不提右递归的方法)

可是我又想到,这显然是不行的。curry1 不管怎么样至少是接受一个参数返回接剩下参数的... 这样的话毫无意义,
因为我实际上一直在提供第一个参数。

然后我就想到了,既然是要构造这么一个递归的情况(第一次填 0、每次填第 n+1 个参数)
为什么不先构造 curry 最后一个参数的情况,然后串起来呢?这样不就可以了?
不过这当然是没有那么简单的,因为 bind 是 left-to-right,然后手动调用也得拿到每一个 curry 的参数
不过我们可以用 effect. 创建的时候弄一个 array 每层去 push 一下就好了

算了还是右递归 bind 吧(当然自己写也可以,就是给左边填一个参数返回右边剩下的)... 用 effect 没必要

function _curryN(f, ff, n) {
if (n == 0) return ff;
return function nextDefault(x_)
{ return _curryN(f, ff.bind(f, x_), n-1); };
}

Function.prototype.curryN(n) { return _curryN(this, this, n); }

总之递归构造数据还是... 应该是符合直觉好 吧...

#JavaScript #FP #Haskell