/tmp/duangsuse.sock
23 subscribers
303 photos
3 videos
92 files
337 links
从 duangsuse::Echo (@dsuse) 跟进出来的分支,将在作者恢复原帐号访问的时候合并删除。
Download Telegram
duangsuse 你真机智,那么我们来休息一下算了,别写代码了……
我们最后玩一个梗吧,
李明开始喜欢上了坐在教室一隅的小静
现在咱有字典树了,看看怎么提取这个句子结构
李明(r) 开始(pre) 喜欢(v) 上(b) 了(b) 坐(v) 在(pre) 教室(n)一隅(n) 的 小静(r)
r=代词、引用
n=名词; v=动词
pre=前导
b=补语(迫真)

李明(r) 开始(pre) 喜欢(v) 这是一部分
(r) (v)
(v) 前面可以有 (pre)
(v) 后面可以有 (b), (pre)

(v) (pre)

坐(v) 在(pre) 教室(n)一隅(n) 的(c) 小静(r)
(n) (n) 是名词连接

艹写不下去了

enum class WordKind {
Ref, Verb, Noun, Pre, Mid, Unknown
}

typealias Word = Pair<WordKind, String>
val dict = object: TrieReplace<Word>() {
override fun from(path: String) = WordKind.Unknown to path
}.apply { }

算了不写了,狗命要紧
第975884位,但是只要我活着,就没有影响!
This media is not supported in your browser
VIEW IN TELEGRAM
PPOP 😂
PPOP SURDIES CCDP SJIT
向不优雅的东西告别吧。
Forwarded from Deleted Account
艹春秋大梦破灭
Forwarded from Deleted Account
最底层的 SatisfyPattern 可以取 negate,我以为编译期就可以做外层包装 pattern 的 operator not 支持的
Forwarded from Deleted Account
既然不支持,我不知道该怎么样了
Forwarded from Deleted Account
越写越复杂,干脆不支持,手动 not 最里面那层 satisfy 好了
Forwarded from Deleted Account
fun digitItem(digit: SatisfyPattern<Char>) = Convert(digit, {it-'0'}, {'0'+it})
val digit = digitItem(elementIn('0'..'9'))
Peek(!digit) {0}.clam("no octal notations")
我就想能用这个
老实说,如果 ParserKt 不支持 rebuild 那这个泛型没有问题
可是支持了,所以不能瞎弄了……
Forwarded from Deleted Account
这还是我第二次破灭了,上一次是以为有了 always(T) SurroundBy 能不靠 Pair<ConstantPattern?, ConstantPattern?> 的 null test
可是实际上 ConstantPattern 的 T 值也要用来 show,所以不能用 always(value) 来随便提供一个 Unit 值,哈。
我还以为是优化的
Forwarded from Deleted Account
越写泛型参数和子类型越多,越发现 Kotlin compiler 的精妙
有时候我以为是 Kotlin 类型推导错了,后来发现其实是自己的参数顺序、类型参数什么的搞错了
如果不知道类型推导有什么意义是不会领悟到这一点的,真是不容易啊
很久以前我以为类型推导就是 var pair = Pair(1, "string"),所以很容易做

如果不写这种高复用性的框架,还真是不知道类型推导可以有多方便
This media is not supported in your browser
VIEW IN TELEGRAM
我该怎么办
ParserKt 灵感智销,请帮帮我们
ParserKt 的底层 Feed 抽象出了一点小问题,就是基于『所有 peek 最终目的都是 consume』这个假设现在不成立导致的,真的让人头疼
Forwarded from Deleted Account
从算法学啊,当然 Java 的结构是面向对象

类 包含许多字段(field) 和关于 this 的行为(称为方法,method)
this 是主语,field 是代词,装着的是某个类的对象的实例的“状态”,method 是动词。
class Dog {
String name;
public Dog(String name) { this.name = name; }
void bark() { System.out.println(name+" barks."); }
}

上面的例子里,this 是主语『狗』、name 是代词(狗的)名字、bark() 是动词『叫』
状态是什么?比如一个 int 可以是 0,1,2,... 某个 field 可以取为这些不同的 int 值,就是“保存了状态”

如果你用 IDEA 这可以 Alt+Insert 自动生成不少结构比如 constructor(new Dog() 的那个)、equal/hashCode/toString 什么的。
如果你用 Kotlin class Dog(val name: String) { override fun bark() = println("$name barks") } 就可以定义这样的类。
个人建议只要有可能 Java 入门立刻换学 Kotlin。

类是对一类对象的泛化,比如狗这类东西,就可以有 new Dog("王叔家的狗")new Dog("二爷家的狗")

而刚才提到一堆 new 的东西,就是对象(object) 了,它们是一些比类更加特殊化的东西,比如世界上所有母亲都是母亲,而你妈是更特殊化的母亲。
狗这种东西有名字,也可以叫(向控制台“叫”出一段文字(String)……)。
但是我们把让某个狗叫的这种行为封装成了需要一个『狗』的 this(实例,instance)的行为
使用你的『抽象(我们叫类库)』的程序员,不需要知道一个狗到底怎么叫,只需 dog.bark() 就可以了,

我们把这种 class (methods, properties), object instance 的模式称为『封装』。

Java 里你能 class Foo { static int n = 1; } 定义『静态』的方法和字段/属性,但正统的面向对象里是不存在所谓的『静态』成员的,在 Kotlin 里,成员(字段/属性/方法)是这么定义的:
class John {
static String name = "John"; // 字段
static int getAge() {} // 属性是 getXXX / setXXX 形式的 "propety accessor",方法的一种规范形式
static void setAge(int age) {}
static void call() {} // 方法
}
// In Kotlin
object John {
val name = "John"
var age: Int = 0
fun call() {}
}
Kotlin 里面如果不用 get() = valueset(value) {} 的话一般都有所谓的 "backing field",但 Kotlin 是不太重视所谓 field 的,
一个好的 Java 程序员应该重视代码的封装能力,如果你把 field 暴露出去,别人的使用都是 obj.field 形式,如果后来你要再加额外逻辑 e.g. 取的时候 println 呢?就不可以了。

Kotlin 里面的 object 在许多 Java 程序员嘴里都是 singleton instance,而且为了实现这个东西 Java 从程序员到 2020 年了还要写老长一段代码,10 行的效果和 Kotlin 里 5 行能完成的没区别,所以说能用 Kotlin 尽量用 Kotlin,利于个人发展。

Java 里,成员还有可见性(visibility)或者说可访问性(accessibility)
private 成员是一个类私有的、protected 成员是类和 extend 它的类共有的、public 成员是整个世界共有的……

这么说吧,在另一门面向对象的编程语言 Ruby 里,private 成员只有一条规则:它的主语(receiver) 也就是 this,不能被显式指定
比如我们举个骂人的例子(开心就好),哔~你妈 的主语『我』,Java 里是 this
它可以是隐式的,但在如名字与参数冲突的时候也可显式写出来。
小明哔~你妈
的主语『小明』,又是另外一个对象,这就要求小明开放了 ”哔~某人的妈“ 这个行为
但小明 private void fxxk(Person other) 了这个行为,你就不能让人家去哔妈了,这是人家”私下“的事,只能人家在自己的领域自己决定。

再比如,如果有一类事情私下里都有一些共通的行为,这时不必让他们每个 class 都去 private method 一次,可以弄个共同的超类,再添上 protected method,这样就实现了在它们这些 child class 里这个操作的代码复用

import java.util.Date;
class Logger {
protected void printHead(String head, String text) { System.out.pintln("["+text+"] "+text); }
}
class TimedLogger extends Logger {
void printLog(String message) { printHead(formatTime(), message); }
private String formatTime() { return new Date().toLocaleString(); }
}
class PersonNameLogger extends Logger {
void logPerson(Person person, String message) { printHead(person.getName(), message); }
}

三种 Logger 都有通用的子程序 printHead 来输出一行 [head] message 这样的东西,然后子类又都依赖这个子过程组织成新的程序
这个子过程,本身是不能被”外部“,代码的纯用户访问到的,也就保证了未来的维护不会导致一堆依赖你代码的程序员找上门来,说”这个为什么又不能用了"、“你早不说是内部的过程,我们都已经用滥了”。

——
面向对象的第二个大特性是『抽象』,咱现在先不说。

先说继承吧,比如这里有一个扩充的 Dog:

import static java.lang.System.out;

class WildDog extends Dog {
void bite(Doge other) { out.println("Dog "+this.name+" bites "+other.name); }
}

野狗也是一种狗,注意,它「是」,一种狗,这意味着所有可以提供一个狗的地方,也都能接受野狗。

野狗是狗的超集,因为它可以上演狗咬狗的闹剧(当然,因为野狗也是狗而且 bite 接受的是 (Dog),野狗也可以咬同类)。

在 class WildDog 里面我们称 Dog 为子类(child class) 或者子类型(subtype)
Dog 则叫亲类(parent class) 或者父类、超类,因为 Dog 的成员可以被 WildDog 里的 super 关键字引用。

放到工程实践里来,继承给了我们更加细化一个类的方法,由超类提供一些更大范畴的东西(比如狗是动物、黄种人是人),再由它的子类加以细化特化,就形成了一个更容易复用代码、定义的编程方法。

抽象呢,在有了继承以后就很好理解了。
上面的 class Logger 例子,是基于 java.lang.System.out 的,它是 java.io.PrintStream 的实例 (终端工具 $ javap ClassID 可以查看它们的成员定义)
可是现在甲方又有一个要求,要兼容另一个支持 void appendln(String line); 的…… JiaFangStream 实例,怎么办?
我们 Logger#printHead 的核心算法是『输出一行 [head] text』。
这个动词,可以是很抽象的。细化一点,我们说它接受一个 String,没有返回值的子程序,或者说函数。
函数可能有很多参数,以及一个返回值,或者 void

abstract class Logger {
protected abstract void printLine(String line);
protected void printHead(String head, String text) { pintLine("["+text+"] "+text); }
class StandardLogger {
StandardLogger(PrintStream outs) {/**/}
@Override protected void printLine(String line) { outs.println(line); }
}
Forwarded from Deleted Account
差不多是这样,许多设计模式也正是基于 abstract 构筑而成,比如 Adapter、Listener、Observer。
interface
的语法和 class 差不多,但只能有方法成员(所以说,要学会用 property 而不是 field……)
interface 里的所有成员都可以是 abstract 的,而且必须是 abstract 的,要不然怎么能叫“接口”呢?
不过如果你加 default,也可以包含实际定义,也就是常用的面向对象 Mixins,可惜只能是 public 的。
Kotlin 没有 default 关键字,假如你要提供默认实现,只需
interface Door {
fun open(); fun close()
fun 鬼畜() { repeat(10) { open(); close() } }
}
即可,不需要 default 修饰这个 鬼畜()

如果你觉得麻烦,可以去看看 Project Lombok,它可以在编译时自动为 field 生成 getXXX/setXXX。

好像写了很多懒得写了…… 总之最后再来看看『多态』吧。
多态就是对名字的重复使用,比如对「打开窗户」的行为你可以叫 "open"、「打开文件夹」的行为也可以叫 "open"。
实际一点,大家常用的 println,其实有许多 overload — 对第一个参数是 int/long/float/double/char 什么的,都有特殊处理,但你用它们,都是叫 println(obj);

面向对象一般有三种多态,子类型多态(subtyping polymorphism)、参数化多态(parameterized polymorphism)、特殊多态(ad-hoc polymorphism)

子类型多态就是一些人可能听说的『虚方法』。Java 里所有方法(除了 final/static 的)都可能是虚方法,虚方法意味着可以被 override 的方法。(注意 @Override 只是一个由 javax.annotation.processing.AbstractProcessor 检查处理的 Annotation,或者说编译器某种程度上的插件)

什么意思呢?比如上面的 class Dog,如果我给这个对狗进行“某种程度”的建模的抽象模型,再进行扩充的话,

class DogWithSex extends Dog {
boolean isMale;
DogWithSex(String name, boolean isMale) { this.isMail = isMale; super(name); }
@Override void bark() { if (isMale) System.out.print("(gently)"); super.bark(); }
}

这时候,给你一个 Dog 的实例,按照上面我们的说法,实际被提供的可能是任何 Dog,包含它的子类的实例。
你可以用 dog instanceof DogWithSex 来测试 dog 是否支持了某种 Dog 的扩充 (extends 或者 implements 的 class/interface)

你仍然可以用 dog.bark(),但这个调用实际代表的操作不止视 dog 的状态,比如 name,还视它的实际类型而定
这也是一种多态。在“后面的东西”实际执行 dog.bark() 时,需要有一个虚方法版本选择(method lookup)的过程,不细说。

参数化多态,看看列表(按索引访问、遍历,可以动态增长如 [a, b, c] size=3, lastIndex=2)这类的基本思路就不必说了,

List<String> names = List.of("Jack", "Rose");
List<Integer> ages = List.of(25, 23);

然后它们上面的 get(int index):T, set(int index, T) 的时候,就可以按照类型系统,依据类型参数进行检查了,更安全一些,不然也可以都弄成 java.lang.Object 啊。
Object get(int index) {/**/}

这个是属于比较高级的操作了,在比如说给你两个列表 xs, ys,如何知道 xs 里的元素是否都大于 ys 呢?
很简单,只要 xs 里最小的元素都比 ys 里最大的元素大,就可以了。
我们的这句话,需要一个能找出列表里最小/最大元素的子程序,或者说过程。
但是算法不好写,我们可以请另一个程序员——或许是 GitHub 上的,哈哈——去实现需要这些专业知识的部分,
不过首先需要协商好,自己到底需要什么样的东西,我们把这 maxIn/minIn 给抽象出来。

abstract class AbstractListTools<E> {
abstract E maxIn(List<E> list);
abstract E minIn(List<E> list);
}

接下来我们就可以实现自己的逻辑了。

我们刚才说,minIn(xs) > maxIn(ys)…… 可是这怎么可能呢?

(Java 的 Comparable<T> 不支持 > 这种数值专用的 operator a.compareTo(b) 小于零代表 a<b,但下文我们假定,支持那样)
(顺便提一句,Kotlin 里是支持的。)

对了,应该给 E 一个限制——它得支持 > 这个操作,这个限制怎么加?
abstract class AbstractListTools<E extends Comparable<E>> ……
这样就可以了。

AbstractListTools<E> tools; // 让这个变量和类型变量 E 能在下面的函数里访问到,比如都放到一个 class 里
boolean allGreater(List<E> xs, List<E> ys) {
return minIn(xs) > maxIn(ys);
}

当然,对于 min/max 的调用一般都不分家的,而且计算 min/max 的算法也一般同时能搜到这两个结果,所以为了性能和效能(内存问题),我们应该使用更合适的返回值——比如一个 Pair<Integer, Integer>
Kotlin 里这很简单,不幸的是可怜的 Java 到 12 了还是难于登天,我们就不吐槽 Java 了吧。

面向对象的基本构成条件其实不是类,是对象,所以你能看到 new Object() {}new OnXXXListener() {} 的写法(称为 anonymous subclass,匿名子类实现),它们可以有正经的“成员定义”,他们也是“类”——只不过不能复用而已。

Java 是静态类型的,看起来要严格不少——实际上也是的,可是如果面向对象语言没有类会怎么样?
许多『脚本语言』也支持类似面向对象的编程范式,比如有 obj.name 这种语法,如 Ruby、Python、JavaScript

但它们虽然也是『面向对象』的,可没有 Java 的多态特性呢(这样就不能有方法 overload、override 什么的了)

# Ruby
class Dog
def initialize(name); @name = name; end
def bark(); puts("#{name} barks"); end
end
dog = Dog.new("小王家犬")
dog.bark

# Python
class Dog
def __init__(self):
self.name = name
def bark():
print(f"{name} barks")
dog = Dog("二爷家犬")
dog.bark()

# JavaScript ES5 (NodeJS)
function Dog(name) {
this.name = name
}
Dog.prototype.bark = function() {
console.log(this.name+" barks");
};
var dog = new Dog("爪哇咖啡犬");
dog.bark();
Forwarded from Deleted Account
我们最后在关于代码复用的问题上多磨叽一下。
Java 的面向对象其实是过程式+复用性/抽象性优化的增强范式,当然到 Java 8 里增加了 SAM(Single Abstract Method) 的 method reference (String::concat), (dog::bark) 和 lambda 了,所以范式,也不能算严格的非此即彼。

面向对象是什么呢?类、对象。
class, extends, implements,。
类包含属性和方法,属性是数据、方法是子过程,或者说行为。
this 是主语、属性是名词、方法是动词。
小明打碎了家里的花盆。 怎么导致这个结果呢?
小明.去打碎(家.get花盆());
哈哈哈,一个玩笑别太当真。

属性可能是变量,也可能被多次赋值——注意两者之间的区别,还是分开点好。
对象造于架构器(constructor),在那里被提供一些数据,可能也有一些数据被提供在 void setXXX 方法里面。
所谓的依赖注入,就是通过这两者方式,把值(value),或者说 Object 的实例,提供给对象。

我们可以了解一下什么叫『闭包』,比如 Java 8 里

import java.util.function.Function;

Function<Integer, Integer> pluses(int a) {
return (int b) -> a + b;
}
void main() {
Function<> add1 = pluses(1);
add1.apply(2); //1+2 = 3
}

我们在 pluses 里拿到了返回的一个『函数』,但它依赖的一个变量却是 pluses 的参数
——那可是一个 pluses 局部作用域的量啊
这已经可以称为闭包(closure) 了,相关的概念是 Lexical Scoping

有人说,对象是有类型约束的闭包、类是它们的类型、架构器是返回这些闭包的函数。
我觉得蛮对的,并且 Ruby 里就是这——那个架构器就是 Class#new 、那个代表对象的闭包就是 obj.method(:send)
反正 obj.method(1) 不就是 obj.send(:method, 1) 吗?
ES5 里也一样,Array.prototype.find.bind([1, 2])(x => x>1)

如果说,数据的依赖是有一个层次的,那么把局部变量上升一个层次,就成了 class instance 里的状态
函数以这种方法提升一个层次,也变成了类里面的方法,有一个 this,可以访问那些更高层次的『局部变量』

这样就可以把一个函数,分成几个部分来定义了,比如 Iterator 就是一个很经典的例子——可以认为 ListIterator 至少得维护一个指针吧?那是不是把 for (int i=0; i<size(); i++) 换了一个侧面另做建模的实例呢?其实随着编程经验的深入,还有更贴切的例子。

而这个的下面还可以有方法级别的数据、状态,还有 inner class(相对于 static class 而言),Kotlin 里还可以有 local function……

所以说呢,面向对象不是一群对象互相发消息,而是一群对象互相驱使的过程,大家互相以对方能或不能访问的信息,访问、调用,进行基本传统的算法操作,顺序 (a; b; c)、分支 (if, switch)、循环(for, while, break, continue),最终构造出实际的进程。

菜鸡我现在是这么想的,如果你也这么想就好好学习吧~