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
duangsuse::Echo
#tech #daily #Java #JavaScript #dev 🐱 duangsuse 的学习日常 ⭐️ + 程序设计语言::Closure、Pattern Matching、Stream、Generators #pl #cs + 程序设计语言类型::这些名词,你知道吗?(涉及 Parameteric Polymorphism 和 Empty Types(aka. Bottom types)、Product types、Subtyping 等内容) + 高性能计算::C 语言和高性能计算、x86…
这次就有一些我最想先弄的东西:

+ 程序设计语言::Closure、Pattern Matching、Stream、Generators #pl #cs

+ 程序设计语言类型::常见名词

+ 数据库::关系代数 #db

+ Java 的二进制流读写器 #bin

+ Java 的 Android android.content.SharedPreferences 代理库 Prefer
+ {软件架构, Android}::编写 Android ShutdownService
+ Kotlin::简单的 Realm ORM + RecyclerView Android 应用

+ JavaScript ES6: sm.ms.js
+ Kotlin::Shell execute
+ Kotlin OkHttp 同步/异步封装
#security https://t.me/dsuses/2956 🤔 说起来我之前一直丢脸地不知道加盐是什么原理... 嘛其实也未必,我知道 hash 算法、局部敏感的 hash 算法、HashMap、SHA 算法,可是之前知道的没有这么详细

比如说,对我的名字计算 sha256 值:

import java.security.*

val sha256 = MessageDigest.getInstance("SHA-256")
sha256.reset()
sha256.update(("duangsuse" as? java.lang.String)?.getBytes())

// fold Left
fun <T : Number> Iterable<T>.hexDigest(): String = this.fold(StringBuilder()) { acc, it -> acc.append(String.format("%02x", it)) }.toString()
fun <T : Number> Array<T>?.hexDigest(): String? = this?.toList()?.hexDigest()
fun ByteArray?.hexDigest(): String? = this?.asList()?.hexDigest()

fun MessageDigest.mkHash(bs: ByteArray): ByteArray = this.run { reset() }.run { update(bs); digest() }
val String.bytes: ByteArray? get() = (this as? java.lang.String)?.getBytes()

val name_shasum = "duangsuse".bytes?.let { sha256.mkHash(it) }.hexDigest()

但是如果 cracker 有一个 hash 字典,然后它知道我名字的 hash 值是 e3b0c44...

我就傻冒了,我的身份将被冒充,并且他这个肮脏侥幸的数学破坏者会弄到我的明文密码 😭

不过也可以这样,允许用户设置一个『盐』值,我们把这些信息以一种我们自己知道可以重复(幂等)的方式混淆到原数据上面,作为一种预处理:

一般直接用 concat 就可以了

inline fun String.salted() = System.getProperty("salt", "") + this

只要每次计算 hash 的时候都先按照可重复的方法预处理,就可以增强 hash 的保密效果,不那么容易碰撞了,也可以进行同样的身份验证。
…8103 年 SHA256 早被撞烂了吧🌚
duangsuse::Echo
…8103 年 SHA256 早被撞烂了吧🌚
现在还好哇,我们现在很多文件验证也是用 SHA-256
何况我不是密码学的人不懂这些东西,我们只是用来写应用而已,很多 JVM 程序员这辈子干过和密码学最近的也不过就是用 IDEA 自动生成的 * 31 int hashCode(); 算法了,我以后只要不是要忽略某些字段的 hashCode,再也不会让 IDEA 去自动生成这种无意义的代码
duangsuse::Echo
这次就有一些我最想先弄的东西: + 程序设计语言::Closure、Pattern Matching、Stream、Generators #pl #cs + 程序设计语言类型::常见名词 + 数据库::关系代数 #db + Java 的二进制流读写器 #bin + Java 的 Android android.content.SharedPreferences 代理库 Prefer + {软件架构, Android}::编写 Android ShutdownService + Kotlin::简单的…
这周份的 duangsuse::Echo 技术日常!包括计算机科学、关系代数、程序设计语言理论相关内容!

= 程序设计语言::Closure、Pattern Matching、Stream、Generators #PL #CS

Closure[wiki]: aka. Proc(Ruby)、闭包、Block、Chunk(Lua, 不完全是, Lua 里 Closure 等价的概念是自动内存管理和存储层的 UpValue 和 Proto,当然得配成一对才是闭包, Chunk 说的实际上是『一段完整的程序』)、Lambda(Lisp, Scheme, Haskell, Scala, etc.)、匿名函数(JavaScript, 虽然 ES6 也是有”箭头函数“的)

Python 和 ES6 的 decorators,也是基于他们的高阶函数支持而设计的。

代码块实际上是『上下文代码的一部分』,其概念源于 Lambda calculus — 函数可以返回函数,而『函数作为值』本身的意思,就是函数可以”独立“于调用栈的存储空间而存在,这意味着函数要知道自己定义时的作用域以保证再次执行时不会出 undefined reference 错 — 它可以『携带』自己所需要的定义离开一层函数调用栈帧,这就是 Lexical Scoping

(lambda x.
lambda y. x) 1
; res0=λy. 1
(res0 2) ; 1

非常明显的行为是,返回的 λy.... 里依然可以访问到 λx.... 里的变量 x

def foo()
a = 1 # 闭包里包含了这个局部变量,如果 lambda 被当成值返回,这个变量将会被保存
[1,2,3].map { |x| x + a } #=> [2,3,4]
end

而以前我们直接用 Java 的 Collections framework 会这么写(虽然这里好像都是在用 [Array...):

public static final void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}

非常愚蠢(但还是比汇编和 while 循环好多了)于是就有了 forEach,正如后来的 tryWithResource 和 Autoboxing 一样

public static final void main(String... args) {
for (String s : args) System.out.println(s);
}

大括号保不保留就是一个见仁见智的问题,支持者认为不保留好看,不支持的人觉得重构麻烦容易出错,当然控制流跳转的 return 怎么用也是一样的见仁见智问题。

可是有人要写这样的程序:比如把 int[] 变成 boolean[], 这要求 forEach 做它做不到的事情:先预备好一个同样维度的数组;知道当前在哪次循环;然后下标赋值目标数组,也就是说得同时读写两个数组,都需要下标

public static final void main(String[] args) {
boolean[] args_bs = new boolean[args.length];
Predicate<String> predicate = s -> s.length > 10;
for (int i = 0; i < args.length; i++) {
args_bs[i] = predicate.test(args[i]);
}
}

于是又有了 init; p; op 的 for statement....

但是很不幸,函数式编程和面向对象混合的多范式成了主流;随处用高阶函数 Higher-Order-Function 和指定 receiver 的方法;于是这种辣鸡代码写起来不再有难度:

fun profiteer(giftList: String)
= fun output(writer: PrintStream)
= giftList
.split("\n")
.map(::Gift) // Constructor, (String) -> Gift
.map { it.mapIncreasePrice(10.dollars()) }
.forEach(writer::println)

Pattern Matching[wiki]:

在 ES6 里支持模式识别的一种子集:
Destructuring assignment 解构赋值
我只知道,ES6 支持:

+ List pattern like [a, b = 2, c], [head, ...tail]
+ Object pattern { length: 2 = -1 }
+ 当然上面的 Object destruct 也可以用 ES6 的同名属性变量语法糖
+ let { length } = [1] 里的 {length} 说的其实是 {length:length}, 给它指定了默认的键:length

详情 MDN

Scala 的 Pattern Matching 详情这里
(比 ES6 的牛逼多了,因为 ES6 的只是结构赋值、Scala 的不仅可以解构赋值还可以判定)

def escape(s: String) = s match {
case 'q' :: xs => "\\q" ++ escape(xs);
case 'x' :: xs => "\\x" ++ escape(xs);
case x :: xs => x ++ escape(xs);
}

这种的 ES6 的解构赋值就做不到,不过也是因为模式匹配搞基一点的原因,所以会有诸如 Pattern exhaustiveness 之类的问题(比如 Haskell 里的 Maybe)

data Maybe a = Just a | Nothing deriving (Eq, Show)

class FromMaybe a where
fromMaybe :: Maybe a -> a

instance FromMaybe String where
fromMaybe (Just s) = s

可是
instance FromMaybe String
... 会报错,因为 (
fromMaybe

:: Maybe a -> a
) 只定义了解构 (Just ...) 这个数据构造器的解构器,到 Nothing 这个 case 就傻眼了(
因为上面定义了:Maybe a 可能是 Just a 或者 Nothing,它是 a 的 Sum type (tagged union)

data Maybe a where
Just :: a -> Maybe a
Nothing :: Maybe a

Sum type is a data structure used to hold a value that could take on several different, but fixed, types.

实际上 FromMaybe 这个用来进行 operator overloading 的 Typeclass 是完全设计错误的 ,它的 (
fromMaybe

:: Maybe a -> a
) 类型签名真的就是很愚蠢啊(除非有默认值,否则 (Maybe a) -> a 就是完全没有意义的事情)!

Stream:

流式 API,充分利用对象自治性,是面向对象架构必须有的东西

val preference by lazy { Prefer.from(UserPreference::class) }

preference
.beginTx()
.setUserName("duangsuse")
.setIcon(File("duangsuse.png"))
.setAge(preference.age)
.delete("oldUserName")
.put("newUserNameNth", preference.read("newUserNameNth").or(0).inc())
.commit()

preference.userName //=> "duangsuse"
preference.length //=> 5
preference.has("user_name") //=> true


Generators[wiki]: aka. Coroutine(Lua), Fiber(in Ruby), Goroutine(Go), Suspend function/Coroutine(Kotlin), Generators(Python, JavaScript ES6, CSharp), Continuation(Scheme, Continuation 是一个抽象的程序执行状态,比 Coroutine 概念大一些)
duangsuse::Echo
啊嗨! java.lang.Deprecated 是什么鬼类型…
Kotlin:

交叉 inline 哦,双重 inline 那就叠在一起好了,免得 return 不到地方
inline infix fun <A, B, C> ((A) -> B).andThen(crossinline op: (B) -> C) = { x: A -> op(invoke(x)) }

REPL 里好像有问题,就

infix fun <A, B, C> ((A) -> B).andThen(op: (B) -> C) = { x: A -> op(invoke(x)) }
inline fun <T> eq(me: T): (T) -> Boolean = { other -> me == other }

fun Class<*>.static() = object { public operator fun get(name: String): Field = this@static.getDeclaredField(name) }

fun assertSignedMinMax<reified T>(klass: Class<T>)
= fun assertLen(len: Int)
= (klass)

😶 没力气写了...

println("forall x in Int. x in [2^32, 2^32-1]")
duangsuse::Echo
这周份的 duangsuse::Echo 技术日常!包括计算机科学、关系代数、程序设计语言理论相关内容! = 程序设计语言::Closure、Pattern Matching、Stream、Generators #PL #CS Closure[wiki]: aka. Proc(Ruby)、闭包、Block、Chunk(Lua, 不完全是, Lua 里 Closure 等价的概念是自动内存管理和存储层的 UpValue 和 Proto,当然得配成一对才是闭包, Chunk 说的实际上是『一段完整的程序』)、Lambda(Lisp…
import static java.lang.System.out;
import java.util.Iterator;

class Clock implements Iterable<Boolean> {
private boolean state;

public Clock(boolean initialState) {state = initialState;}
public Clock() {this(false);}

class ClockIterator implements Iterator<Boolean> {
@Override public boolean hasNext() {return true;}
@Override public Boolean next() { state = !state; return state; }
}

@Override public Iterator<Boolean> iterator() {return new ClockIterator();}
}

public final class ClockMain {
public static void main(String... args) throws InterruptedException {
Clock clock = new Clock();
for (boolean b : clock) {
out.print(b ? "🕐Tick" : "🕒Tock");
Thread.sleep(500);
out.write(0x1b); out.print("[6D"); // https://en.wikipedia.org/wiki/ANSI_escape_code#DL
}
}
}

如果使用 Coroutine,就可以写更优雅的 Clock 了
,再比如,如果使用控制结构:

= 程序设计语言类型::常见名词

= 数据库::关系代数 #DB
#Java #code 里面有个 inner class
运行起来有一点鬼畜而已(...
ClockMain.java
835 B
duangsuse::Echo
#Java #code 里面有个 inner class
当然,你也可以使用 Qualified class instance creation,只是因为 Java 有 inner class 自动绑定上父域类的状态而已
@Override public Iterator<Boolean> iterator() {return new Clock().new ClockIterator();}
duangsuse::Echo
import static java.lang.System.out; import java.util.Iterator; class Clock implements Iterable<Boolean> { private boolean state; public Clock(boolean initialState) {state = initialState;} public Clock() {this(false);} class ClockIterator implements…
然后我们还可以去写一个更 precious 的 Clock,它不仅会 Tick tock 还能记录时间,它被称为:TimedClock

也很简单,代码里几乎不需要什么修改

(这里明显不是教你怎么用 Generator,因为 yield 太简单了很多人章口就莱,是教你们怎么用 Iterator + switch 在 Java8 里手动写基于状态 🐔 的 Generator)
TimedClock.java
1.4 KB
也相当鬼畜,不过还没有教到使用 switch 进行自由控制流跳转的局面,待会会为大家做现场的表演~
duangsuse::Echo
也相当鬼畜,不过还没有教到使用 switch 进行自由控制流跳转的局面,待会会为大家做现场的表演~
修一下 bug、增加 Tick Thock,下面有更加 excited 的 clock。
TimedClock2.java
1.7 KB
然后大概是这样
duangsuse::Echo
修一下 bug、增加 Tick Thock,下面有更加 excited 的 clock。
最后我们还有一个最搞基的 Stream:

它的逻辑类似:

function *excitedGenerator() {
yield 3;
yield 2;
yield 1;
// Go!

for (let x of [1,2,3]) yield (Math.trunc(Math.random() * 100) * x)
let clock = new TimedClock();
yield *clock;
}

我们要使用裸 Java,不使用编译器的 suspend function 支持自己手写出来这种 Iterable!