duangsuse::Echo
412 subscribers
3.85K photos
105 videos
574 files
5.15K links
duangsuse技术相干订阅
这是 @duangsuse 与技术有关的发布频道
duangsuse 的另外有 throws 闲杂频道
@dsuset
转载频道 @dsusep
duangsuse 有coding,github,gitlab帐号和bilibili帐号

极小可能会有批评zf的消息 如有不适可以退出

suse的小站:https://piped.stream
ps 另有别名 popf.rip
ʕ•̀ω•́ʔ✧ 🐶🍎🏠生死🐜
(>ω<)岂因祸福避趋之 一鿕
Download Telegram
duangsuse::Echo
#bin 然后为了证明我不比 drakeet 差,只是缺少拿来实践的时间, 我分析一下这篇文章《5分钟学会十进制转换成二进制的快速口算方法 & Java输出二进制的代码》,然后就睡觉。 当然,至于 Android 那一套,比如异步、Timer、MessageDriven、Android XML、Android Widgets、Resources、Android Gradle Build 集成、库打包、封装、Android Layouts、Views、Paint、Android Layout Animat…
其中有一部分弄错了,我还以为 base16 只能表示 base2,base2 不能表示 base16,所以 16 -> 2 不能快速笔算,其实不是这样的(当然是可以这么算的,因为 16 = 4 * 2 * 2 呢,要不然不该是等号)

0x133
= toBinary(0x1) * 0b1000_0000
+ toBinary(0x3) * 0b1000
+ toBinary(0x3) * 0b1
= 0b0001_0000_0000
| 0b0000_0011_0000
| 0b0000_0000_0001
= 0b0001_0011_0001

如果还有错误的,请赐教 #fix 🐱
duangsuse::Echo
@FunctionalInterface interface Parser<R> { @Nullable <? extends R> match(); } @FunctionalInterface interface Matcher<R> { @Nullable boolean matches(<? super R> value); } 所以提供的 parser combinator 方法都有 Parser<R> satisfy(Matcher<Character> predicate); charP…
顺便说说 #fix #Java

  @Nullable boolean matches(<? super R> value);

这一句是错的,应该改成,属于我不熟悉泛型语法...

@Nullable <T super R> boolean matches(T value);
duangsuse::Echo
草,没时间写了
#fix...

这个实际上是错的,因为我把 Main class 直接放在 source dir 下了... 本来应该放在

package org.duangsuse.djson;

这是属于不熟练...
duangsuse::Echo
#bin 然后为了证明我不比 drakeet 差,只是缺少拿来实践的时间, 我分析一下这篇文章《5分钟学会十进制转换成二进制的快速口算方法 & Java输出二进制的代码》,然后就睡觉。 当然,至于 Android 那一套,比如异步、Timer、MessageDriven、Android XML、Android Widgets、Resources、Android Gradle Build 集成、库打包、封装、Android Layouts、Views、Paint、Android Layout Animat…
这里还有一个 "bitselector" 的问题,我待会更新... #fix #bin

irb(main):001:0> 0x01000000.to_s(2).size
=> 25

irb(main):002:0> 0x80_00_00_00.to_s(2).size
=> 32

记得上面的表吗?

0b0001 = 2^0 = 0x1
0b0010 = 2^1 = 0x2
0b0100 = 2^2 = 0x4
0b1000 = 2^3 = 0x8

然后可以直接对应到 16 进制的,每 4 位 2 进制都可以对应到一位 16 进制

Math.log2(16) #=> 4.0

没有影响到程序的正确性(因为 C 的 if (...) 真值就是 0 false 其他都是 true,这里按位和 (&) 运算就算是使用 0xffffffff 也没问题,只要不是 0x0)
但其实是不对的,我之前以为 16 进制不真正能做 2 进制的简记法,现在知道了...
#fix #Java 上次本频道和频道主 @duangsuse 大幅误人子弟,实在是令人... 呃,我以后多编程、多写文档和设计草稿、多做算法设计,少直接 Telegram 上说话,少误人子弟了... 🤐
duangsuse::Echo
刚才阅读代码加强理解的时候忽然发现不对,这个 chianr1 怎么和 chainl1 是一样的,对着冰封的博客,发现我写错了...
#fix #haskell #fp #cs #wip #recommended #saved 这个问题,暂时就说到这里了

以后有机会我会跟冰封哥说... 看看他知不知道为什么
先做更有价值的事情

我也真是醉了,看了 #Monad Parser 是很迷啊
#Microsoft #Office https://github.com/duangsuse/Share/blob/master/Word0.vb

为了帮老师输入作文比赛的作文单子写了个 Word VisualBasic 宏,对 Basic 的怀旧肃然起敬....

https://github.com/duangsuse/Share/blob/master/Word0.vb#L41

我专门弄了个 Lexer-like 扫描算法扫描自己的文本字符串输入...

其中有一个循环,我后来发现是自己错了 #fix #Basic #Algorithm

本来因为我不知道 Basic 滋不资磁 continue; 所以就弄了个 Dim doNext As Boolean

后来如你们所见,doNext 是个死变量,没有被读取

是因为我在使用的时候发现它(跳过空白行和注释行的时候)会导致跳完所有空白注释之后直接结束处理,调试结果居然让我的木头脑袋百思不得其姐(跑


后来我发现是因为我没有重置这个变量(这样一次 skipThis, 后面全都 skip 掉了.... 实际上只是 skip 掉 "This" loop 而已).... 记下教训,这种丢脸的事情... 😭
希望以后不要发生了....

Open "foo.txt" For Input As #1

Dim line As String
Dim skipThis As Boolean
skipThis = False

While Not Eof(1)
skipThis = False '!!!! Add this
Line Input #1, line
If IsBlankOrComment(line) Then
skipThis = True
End If

If Not skipThis Then
...
'' Or (好像跳两下了...)
'Else
' skipThis = False
End If
Wend

Close #1
duangsuse::Echo
#life #school #China #dev
说起来,这个 TCO 函数我现在居然能直接通过原理自己想出来了(虽然和上面的不一样)

//export default tailrec;

function tailrec(rec_fun) {
let stack = [], isTailCalling = false;
let lastresult;

let tailcalls = (args) => {
while (stack.length !=0)
{ lastresult = rec_fun(...stack.shift()); }
return lastresult;
}
let cont = (...args) => {
stack.push(args);
if (isTailCalling) return;
else { isTailCalling = true; }

let result = tailcalls(args);

isTailCalling = false; return result;
};
return cont;
}

module.exports = {tailrec};

Node 对 ES6 的支持折腾了半天.... Node 的 ES6 Module 支持,真的很辣鸡
看起来,大部分 JavaScript 前端甚至『全栈工程师』的平均水平,还真是不如纯 Java『后端』呢,因为 James 他们给 Java 9 加了很好的模块化系统,可 Node 作为『最流行』的玩意,这方面比起 Java 来就显得 Java 很『学术派』了,emmm...

debug 了半天.... 对 Expansion operator 使用不够熟悉;现在还有点鬼畜,不能好好 Handle optional arguments
但是我 REPL 的时候没有问题,辣鸡 Bable CLI;前端程序员的细节水平果然辣鸡,如果你写的程序无限运行,你甚至不可以用 SIGQUIT 和 SIGINT,它给我的感觉就是 Node 太可怕了... 居然退出不了...

啊,终于发现问题在... 我的测试程序写错了,我这个数学渣渣... 应该拿去给 Engima 的设计者好好批判一番... 😭

function factorial$() { var rec = (ac, n) => n===0?ac: rec(ac*n, n -1); return (y)=> rec(1, y); }
tailrec(factorial$())(100)

当然 sum2 函数就好写多了

sum2 = tailrec((x, y) => x===0?y: sum2(x-1, y+1));

我忘记了,任何数乘 0 还得 0... #math
说起来这还是我最不搞笑的低级错误;最搞笑的莫过于把 『除数不能为 0』弄成『被除数不能为 0,除数可以,如果除数是那结果是 Infinity... 等等』

ghci> 0/0
NaN
ghci> 1/0
Infinity
ghci> 0/1
0.0

Prelude> 1 `div` 0
*** Exception: divide by zero
Prelude> 0 `div` 1
0

显然分母不能为 0...

哦对了 #fix 一个之前关于 #PLT 的隐式定义:在 Product type 里;如果两个乘类型都是 Zero sized type,那结果也是 Zero sized type
我之前说过 ZST 好像就是 Product /sum type 的 1 来着,1*1 可是还得 1.... 啊?好像也没错...(说了 1 是 ZST... 所以说数学辣鸡 duangsuse...)

替换了原来的短路式伪分支逻辑
if (!active && (active = true))
因为我觉得它不够明确
并且也没有使用书上的两个 if 风格,而是使用了 return 在更大区间上操作控制流
至少;现在的我比以前又强了一些,需要继续努力!

(别人之前的是)

function tco(fun) {
var value = null, active = false, accumlated = [];
return function accumlator() {
accumlated.push(arguments);
if (!active && (active = true)) {
while (accumlated.length != 0) value = fun.apply(this, accumlated.shift());
active = false; return value;
}}}

上面的代码会在过会的一些(当然会往 gh 上发的)文字上出现
https://ice1000.org/2018/04/27/CodeEditor/ #blog #fix

看完查找资料后,修复了我对一个单词的误解:

efficient 不是『足够』的意思,是『有效率』的意思
effective 是有效的意思
duangsuse::Echo
#Cplusplus https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture Lambda capture in C++11 #reveng https://github.com/freakishfox/xAnSo #recommended 即使作者的项目管理风格有点... 呃... 比较原始,而且作者貌似是自己能用了就跑路了,后来开了仨 issue 说是不能用或者建议作者给软件文档的也没有用的回复。 这个是看 @Trumeet…
#fix #Cplusplus 这里其实把 C++ 的 Lambda capture 功能弄错了,感谢一位细心读者的反馈!(我去,这么菜的频道还会有读者)

int var = 1;
++var;
auto f = [&]() { cout « var « endl; };
f();

其实这里 & 是 by ref 的 default capture
= 才是 by value 的...(说起来这很 immediate 嘛,两个符号语义明显有区别,这是我一时脑抽了...)

若想详细了解,我写了个例子
编译结果可以在这里查看

#include <iostream>
using namespace std;

int main() {
int var = 1;
auto f = [&]() { cout << var << endl; ++var; };
auto f2 = [=]() mutable { cout << var << endl; ++var; };
//...
}

(同时感谢细心读者的建议)然后在 [&] by ref 的时候有一个要注意的问题,就是从存储对象的『生命周期 lifetime』来看如果 local variable 实际生存期比 Lambda 短,那 lambda 访问时就会出现悬垂指针(dangling pointer)的问题,这一点需要注意

同样我也写了个例子

std::function<void()> higherOrder() {
int upvalue = 0xcafebabe;
return [&]() { printf("Wow! I got upvalue = %i\n", upvalue); };
}

可以在这里阅读 [它的 cppinsight]
duangsuse::Echo
Current settled on this. 关于某排序问题,我最终总结『为什么会有这个误解』的结论就是: (原问题来自 drakeet 的 Android 应用 PureWriter 文章列表实现『同时自动/手动排序』功能) drakeet 想了这个问题,然后举了一个类似 a > b > c 的(可主观自动排序列表,降序排列, ord=(>))例子 其中 b 是被用户安排的项目,且用户希望 b > c 5 没有被手动排序过,5 排在 1 前面 (5 > 1) 4 被*手动排序*过,4 也在 1…
对了,看到 drakeet 的这个广播,我突然想起来我本周有一个应该纠错和反省的东西得提到,不能忘了(虽然马上要放暑假)

有朋友说他会反复看我写的东西,令我很感动,因为我也是这样,我也会无数遍不停看自己写的东西或分享,所以当有人细心一点会发现我的内容即使有错别字或不妥的内容,但最终都会相对改正和变得相对得当,这点上我也很佩服王垠,他的博客文章,我从来没有发现错别字,即使我是一个很敏锐且阅读文章特别慢的人 ... 只是说人的东西永远都会成为黑历史,经典是非常难得的,因为一个好的人应该是不断飞速成长的,如果黑历史没有精力去一一重新审视扶正,就会变得不负责任,或自己也不能接受,所以删除是好事。

很不幸,因为本频道全 TM 是没有用的计算机科学理论性较强的内容,而且一方面我又是比较正常洋文水平也不太差的学生,所以错别字本身就少的可怜,然后内容我又经常
无数遍不停看自己写的东西或分享,所以当有人细心一点会发现我的内容即使有错别字或不妥的内容,但最终都会相对改正和变得相对得当。
所以从来没有过错别字
这一点本频道一直以来的态度都可以证实,显然 judge 别人是不好的,但是我想这么干 🥺
这点上我也很佩服王垠,他的博客文章,我从来没有发现错别字,也从来没有看透过其内容,即使我是一个很迟钝且阅读文章特别不走心的人...

人的东西永远都会成为黑历史,经典是非常难得的,所以要努力努力再努力把自己整个人都变成黑历史,从不迟钝和后退~ I won't give up, no I won't give in
Til I reach the end and then I'll start again
No I won't leave, I wanna try everything
I wanna try even though I could fail
Try Everything

(敲黑板)因为一个好的人应该是不断飞速成长的

如果黑历史没有精力去一一重新审视扶正,就会变得不负责任但又那么有意义,或自己也不能接受
但即使这样我们还是希望能回首过去,发现自己曾经的误区、鼓励别人走一遍自己走过、不可或缺的理解之路,所以保留是好事。

#OOP #fix 这个要订正的东西关于面向对象编程多态的底层实现。

这里提到的面向对象编程多态是指子类型多态 (subtyping polymorphism)

这里出问题的广播是我给 Ra 程序设计语言写的一点小示例

data Person { name :: Str }

data Student is Person
{ grade :: Nat * class :: Class }

data Teacher is Person
{ classes :: Vec of Class }

abstract fun Person.briefDesc(): Str = "a person called $self.name"

这里 Person.briefDesc 其实是一个虚(virtual)函数

我所谓的优化看这里

昨天我阅读《Lua 的设计与实现》这本书的时候,看了一下关于 GC 和面向对象实现(其实就是子类型重写 override 多态)的部分

"""
Ra 的多态也是使用类似 C++ 虚表的方法实现的,不过 Ra 支持的多态不多,也不支持多继承,但可以 mixin traits

当你定义拥有 abstract 操作符 (self fun) 的类型时,Ra 编译器自动为这个类型(必须 data,也即 Product type,因为 Sum type 可以直接进行判断没有必要使用多态)

data Person { name :: Str * _briefDesc :: <Self> -> Str }
"""

data Person { name :: Str * _briefDesc :: <Self> -> Str }
这是错误的,因为它没有明显地使用虚表优化
虽然它的确可行(我出错是因为多方原因共同作用,但是归根究底可以认为是学习还不够深入、想的太少、对所有权和优化不敏感),其实,虚表应该是被一个类所拥有的,而不是它的实例
也就是说,不存在可能的 _briefDesc 闭包成员,只可以有一个全局(和类本身一个作用域)的地方存储它们的虚表,其中包含实际实现闭包的引用

vtables[Person] = map(:briefDesc to ...)
vtables[Student] = map(:briefDesc to ...)

因为只有类可以 override 它超类的虚方法,而实例只能是类的实例,所以所谓的虚表,和一个类是 1:1 的数量所有权关系。
具体暑假再讲 #task
duangsuse::Echo
顺便说一下为啥要进行 n 次,每次都要排序列表从 0 到 n - i -1 的项目,虽然我现在还没有证明为什么 bubble sort 输出的列表就一定是有序的的能力(归纳证明) 排序算法,就是给一个输入序列、一个测试函数『序(order)』,输出序列满足以下条件 forall i. list[i] `order` list[i+1] 比如 list=[3,2,1]; ord=(<) 简单的选择排序每次挑一个『最大』的元素出来(它满足 forall x in list. x <= it),复杂度是 O(n)…
#Java 修正:刚才看这个泛型通配符模拟的时候感觉有点不对劲,首先方法泛型应该只在很少的地方出现型变通配符,其次,
我模拟的时候发现这个实际算法逻辑

private static <T extends Comparable<T>> void propagateOnce(List<? super T> xs, final int ri);

中这个 xs :: List<capture<? super T>> 的使用 T get(int);void set(int, T); 不对称

首先 xs 从 producer-consumer 模型来看它接受 T 也生产 T,实际上就无法保证同时的类型安全(但是如果从这个方法的一个参数对象读另一个写,比如 copy 方法是没有问题的),所以它不应该有泛型协逆变(invariant)。

我考虑了一下 TInteger xs 是 List<Number> 的情况,PECS 原则 List<in Number> 也即 List<? super Number> 是可以安全地使用 set(int, T) 方法的(Integer 实例可以转换为超类 Number 的实例,虽然 Java 泛型擦除所以 List<T> 实际上运行时是 List[^1] 这个 raw type...),可是不可能同时安全使用 T get(int); (要将 Number 强制转换为 Integer,这是不合法的)

这点是需要注意,我之前没有注意到,现在也及时修正了 #fix #Kotlin

其次,我从 Java 的 Collections API 看到了 sort 的方法签名

public static <T extends Comparable<? super T>> void sort(List<T> list);

现在我把这个 <T extends Comparable<T>> 改为 <T extends Comparable<in T (? super T)>> ,显然 Comparable<Number>int compare(T, T); 可以接受 T=Integer 作为比较输入
我得使用通配符泛化一下(逆变声明)才能支持这个... (当然 Kotlin 里包装的就直接使用声明处型变了,不需要你操心它的型变性)

在此希望大家注意.... 不要搞混了 PECS 原则的实际含义


^1: List<Object>, 和 List<?> 还是有区别的,因为这里的 ? 不能作为输入但作为输出可以强制转换成 Object,它像子类型系统里的 bottom type、Kotlin 的 Nothing,它是任何类型的子类型
duangsuse::Echo
Current settled on this. 关于某排序问题,我最终总结『为什么会有这个误解』的结论就是: (原问题来自 drakeet 的 Android 应用 PureWriter 文章列表实现『同时自动/手动排序』功能) drakeet 想了这个问题,然后举了一个类似 a > b > c 的(可主观自动排序列表,降序排列, ord=(>))例子 其中 b 是被用户安排的项目,且用户希望 b > c 5 没有被手动排序过,5 排在 1 前面 (5 > 1) 4 被*手动排序*过,4 也在 1…
#fix UPDATE: 我在写文的时候看见了他给的一点额外的信息(虽然其实也没有那么刻意)
发现还有一点资料没有看到(刚才我还以为我审题审错了... 两个都是手动,所以序可能是 (<) lessThan,那例子就是非序列的)

[Forwarded from Drakeet]
就是我我们要的是那样的结果,但你找不到一个逻辑能够让程序去实现

[Forwarded from Drakeet]
这里面有矛盾

不过对我来说好像没有太大帮助... 我开始猜的就是这种情况,正好符合他
找不到一个逻辑能够让程序去实现
的表达。而我的条件也没有弄错。

不过我是找到逻辑让程序去实现了,也不过是为每一项定义一个 lessThan 集合(Map<Item, Set<Item>>),每次去动态判断序,我不知道除了我枚举出的那个情况,是不是还有别的矛盾,但反正没人愿意告诉我了。

『要求』怎么抽象?其实可以做一个 lessThanC 集合,如果右项目是 c,则直接判断左项目是否在 lessThanC 布耳映射内否则返回 True(满足序列)
duangsuse::Echo
https://www.zhihu.com/question/329153374/answer/716655357
#db #fix 才看到自己曾经的消息,这里其实 ORM 的问题,真的不是问题...
#Java JavaEE 的 JPA javax.persistence 规范提供了解决这种问题的思路,我以为只能那样解决是我太菜,仅此而已。

图数据库解决的问题,是提供非规范化视图,用以进行快速的 bfs, dfs 图搜索操作,这样就可以找到诸如『你和某人共同的 follower 是哪些人』一类问题的答案,而无须(在语义上)存储所有临时结果,不是 ORM 所谓的 N:N 问题,它不存在,ORM 支持 RDBMS 的关系模型。

上面的例子里,这根本不是一个问题,这种 N:N 多—多 (而非 1:N / N:1)关系问题对 ORM,很简单。

@MappedSuperclass
public abstract class BaseEntity implements Identifible, Serializable, Timestampable {}

因为首先可以定义一个复用的公共『ORM 对象』接口规范,那么 Identifible 的接口定义如下

/* 32-bit Integeral identifible object */
protected interface Identifible { int getIdentity(); }

Timestampable
则是所有可以拿到 creationDate 和 updatedDate 的对象
protected interface Timestampable { Date getCreated(); Date getUpdated(); }

就可以开始定义基本的 Domain Object 们,他们装数据

@Entity @Table(name = "users")
class User extends BaseEntity {
@GeneratedValue(stategy = GenerationType.Identity)
private @Id int id;
@NotNull private final String name;
@NotNull private String nick;

@ManyToMany(mappedBy = "stargazer", cascade = CascadeType.ALL)
private @Column(name = "stared") Set<App> publicApplicationStars;
}

@Entity @Table(name = "apps")
class App extends BaseEntity {
@GeneratedValue(stategy = GenerationType.Identity)
private @Id int id;
@NotNull private final String package;
@NotNull private String name;

@ManyToMany(mappedBy = "stared", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval=true)
private @Column(name = "stargazer") Set<User> stargazers;
}

另外,王垠说的『等价 RPC』我也想到了一种可能。
他假设的就是,数据库的『数据结构部分』就放在某台计算机上;
“查询的本质,就是在那台计算机上执行一个程序,这个程序负责把结果搞出来,然后响应对应的 RPC 请求。”
所以说是 RPC。

不过是不是真的这样,上面说过了我不想重复一遍。

— References
Baeldung::Hibernate Many to Many Annotation Tutorial
duangsuse::Echo
🤔duangsuse 昨天晚上想到了一些话,愿意分享给大家。 #statement duangsuse(虽然一点也不好)是如何编程的呢? 他编程,是一个从整到零,再从零到整的过程。 首先是把一个相对比较大的目标,拆分成许多能够比较简单实现的小目标。 比如,把一个操作纷繁复杂的类(class),抽提出实现了很多接口的类。 接着是和自己争论实现的难点和复杂性,继续重新拆分和重新组织继承、数据依赖这样的关联,偶尔还会为一些经常联系在一起的东西(比如 Database, Controller) 起一些首字母简写,记住。…
还有就是这个,此外还应该提醒学会数据的依赖分析,这个其实主要还是看思想,实践是最好的方法。 #learn

比方说:
import java.lang.System.`in` as stdin
val input = Scanner(stdin)
val a = 1
val c = a + if (p) a else input.nextInt()
这就要求你有看出这个 c「偶尔」需要依赖 input 的(对象状态)意思。
还有,input 本身可能有状态,在控制流图(CFG, Control Flow Graph)和计算、副作用的产生中可能有怎么样的变化,必须清楚。
最基本的:给你一个 val c,你要知道求出它需要依赖什么数据,如果有些断言要做,你得知道哪些情况叫『正常程序状态』『正常代码路径/程序路径』有哪些则是可能错误的。

另外,要知道一些东西不能太注意刻板的风格,要更加在意可读性。
比如,像 Android Gradle 插件里的 android {} DSL
首先我们知道 compileSdkVersion 是不能缺的,而且对 android {} 来说只用指定一次也没同样重要的东西,所以它可以不折行直接放在 android { 后,目的是体现它与这块定义的关联性。
再如,minSdkVersion 和 targetSdkVersion 不是一个区间范围(没有说高于 targetSdkVersion 不能用的),但可以认为它们应该被写在一行内,不会引起歧义也并不莫名其妙。
versionCode 和 versionName 也是一样的。
偶尔该加的分号要加,这点和『优雅的顺序』思想是一致的。
这样,他们在基于行号的VCS里如果改一个记录会显示只有一行的变动,不过相信没人会必要这个吧。

然后是要有抽提的逻辑,比如,我们这里有一个Android活动:(不要喷,只是个例子而已,另外错了请不失风度地指出,谢谢)

class SomeButtonfulActivity: Activity() {
//KotterKnife
val btn0: Button by bindView(R.id.main.btn_0)
val btn1: Button by bindView(R.id.main.btn_1)
val btn2: Button by bindView(R.id.main.btn_2)
override fun onCreate(savedState: Bundle) {
super.onCreate(savedState)
//no-op ...?
btn0.onClickListener = {_ -> toast("wtf")}
btn1.onClickListener = {_ -> toast("wttf")}
btn2.onClickListener = {_ -> toast("wtttf")}
}
}

我们就不说 Activity 本身可继承、可抽提这件事了(可以对其中 onCreate, onResume 的逻辑等各另加组合,抽象出简单适体一点的版本给子类实现)
我们也不说其实 Activity 用 Intent 模型虽然是不得以(因为建模如此,而且语言层面也不好做什么)导致但不好看的问题了(理论上更优雅的Activity应该是带架构器参数而不是动态取参数的,否则也不该是类的形式,单实例就够了)

我们也不说 Kotter Knife 没有用 Annottion Processor 以及 Kotlin 的 first class delegates (delegated properties) 的事了。
我们也不说其实 onClick= 完全可以写在 init{} 里这件事,没必要覆盖 onCreate 这件事了……
我们也不考虑 onCreate 和其它 lifecycle event 比如 onResume 有啥区别了……
我们也不谈 Qt 的 signal/slot、.NET 的 event、Android 的传统 Listener 什么的有啥异同。

这里说说上面的一大堆 btnx.onClick { _ -> toast("...") } 怎么抽提
以前辣鸡Jawa弄得什么 anonymous subclass instance,害得那时候又年轻又阳光又帅气的我都看不懂那些代码,嘤嘤嘤。
相信写了很久Java但是不知道 new XXX() {@Override...} 啥意思的人不止我一个,以致于后来换Java8了,很多Android开发者依然不知lambda为何物。另外一些开发者则出于某些媒体拙劣的宣传把虚拟机的invokedynamic扩展和lambda挂钩了,根本不去注意一下Java8的代码dexer出来带RuntimeDesugar(其实Java8的()->也的确是sugar)。

一句话:
val buttons = listOf(btn0, btn1, btn2)
val texts = "wtf wttf wtttf".split(" ")
for ((btn, text) in buttons.zipWith(texts)) btn.
onClickListener
= { _ -> toast(text) }

稍微有点常识的人不难看出,里面那个 { _ -> } 的闭包依赖 for 里面某次的 text 数据引用。
对这里的闭包关键的一点是:for 有多少次迭代就有多少 { _ -> ...text... } 产生,所以说偶尔闭包其实也不能那么『直观』地看尽管它本身设计目的之一就是为了直观(其实也没有……)。

然后 Kotlin 的 single abstract method interface @FunctionalInterface 实现就不用说了
然后有些东西我也不懂

另:试试多加许多的 Button 然后动态创建(不是说用Anko)
typealias Consumer<T> = (T) -> Unit
Map<String, Consumer<User>>
怎么样?其实动态创建还是动态注册监听差不多。


最后的最后我想说一个问题,当然是我之前的一次误解 #web #JavaScript #fix

function fixedRateSchedule(time_ms, op) {
op(); setTimeout(() => fixedRateSchedule(time_ms, op), time_ms);
//也可以直接用 Function.prototype.bind
//语义上我们创建了一个依赖它上次调用的参数(是全等)引用的 fixedRateSchedule 闭包,实际上是不是新闭包可以自己用 (===) 判断。
}

你们说这个函数有没有『递归(recurse)』?
它有没有调用自己?答案是没调用自己,只是它做的操作引用到了自己而已。

递归的情况栈深度是除『基线』外每层调用往上涨的,非递归情况不变或者下降。

setTimeout 哪怕是timeout=0,也只可能在当前
fixedRateSchedule
实例的栈帧弹出(返回)后执行(要不然就不叫timer了),所以栈深度不变,没有递归。
(当然这不是递归最根本的特征,我只是详细说一个侧面的特征方便学习研究之用而已)

好吧这也不一定,尽管JavaScript是很单线程的语言,timeout=0 的具体情况也是看是否能抢断当前执行子程序的执行权而定的,这里假设timer需要等待应用程序的逻辑没在执行,而是回到底层的运行时库在维护工作状态时才能工作。