duangsuse::Echo
716 subscribers
4.25K photos
130 videos
583 files
6.47K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
其实我也相当努力了... 可是,其实有些东西就是要花时间... 而且我现在还没有机会沾染计算机图形学和人工智能
duangsuse::Echo
😔 Sticker
所以 GeekApk v1b trivial 的不得了的权限验证和所谓高难度的分页,都是智障级别的.... 除了被不良工程的 GeekSpec、Spectrum,我不知道它还有什么可取之处... 只要我现在完成这个临时的版本,真该啥时候重写的。
This media is not supported in your browser
VIEW IN TELEGRAM
#DuangsuseSB 煞笔警告一次
duangsuse::Echo
#DuangsuseSB 煞笔警告一次
好吧,好吧,duangsuse 来恢复一下心情,顺便写今天最后一个代码:默写重写 @YuutaW 的 RandomPicture

完全默写,包括 Gradle 构建文件(除了 Vertx 的依赖关系 Coordinate 在外)
并且,不得使用 IDEA 等具有自动补齐功能的 IDE

== 首先,我们来玩个简单的文件排序游戏,因为 duangsuse 不是学算法的(我以后会学)所以不考虑算法层的东西,就快速排序吧(因为它不需要我花时间去记,皮一下哦)(反正怎么说我,交换、插入、选择排序里,我也只会快排、冒泡、简单选择,虽然 OI 基础里还有简单插入、希尔排序、堆排序)。

肯定要用 Kotlin 最好,Haskell?那玩意 IO 模块的 API 我还不是老熟,现在弄了又要花掉十几分钟时间。

首先,我们要这么定义我们的『顺序』(基于 Int 优先级):
优先级,0 为最高优先级,往后依次递减,排序时使用 greaterThan ([a|b],若 a greaterThan b,则交换顺序) 来比较

针对每个字符,使用求和函数归并出优先级:
[A-Z] 范围内,递减优先级(0..23)
[a-z] 范围内,递减优先级,比 [A-Z] 小(23..49)
[0-9] 范围内,比 [a-z] 小(49..59)
任意其他字符,不影响优先级(0)

首先我们定义快速排序函数:

fun <E> quickSort(compare: Comparator<E>)
= fun (items: List<E>, lhsStart: Int = 0, rhsEnd: Int = items.lastIndex) {
// recursion base: like [1] / [2]
if (items.size < 2) return items;

// select swap pivot
val pivot = items.size / 2
// recursion
val lhsSorted = quickSort(compare)(items[lhsStart..pivot], pivot, );
val rhsSorted = quickSort(compare)(items[pivot..rhsEnd], pivot, );

// exchange mybe?
if (compare(lhsSorted.last(), rhsSorted.first()) > 0) {
// lhs is greaterThan rhs, swap then
return rhsSorted + lhsSorted
} else { return lhsSorted + rhsSorted }
}

以上这个函数写了我差不多半个小时,但是没有成功,我始终忘记了应该怎么模拟调用栈来设计算法... 🤔
所以我索性直接抄了某本算法书上的算法,我说过我是本本主义者。

fun <E> List<E>.quickSort(cmp: Comparator<E>): List<E> {
if (this.size < 2) return listOf()
val pivot = this[this.size / 2]
val less = this.filter { cmp.compare(pivot, it) > 0 }
val more = this.filter { cmp.compare(pivot, it) < 0 }
return more.quickSort(cmp) + pivot + less.quickSort(cmp)
}

import java.text.Collator
val col = Collator.getInstance()
const val TEXT = "hello HELLO 810 114514 1919810 z ab cd dc"
TEXT.split(' ').quickSort(col)

[z, HELLO, dc, cd, ab, 1919810]


我开始居然忘记递归调用 quickSort 了... 最近思路有点崩溃,看不到数据的流动了。
然后居然又忘记了,基线条件,列表的大小必须得大于等于 2 才有顺序一说啊!
亏我还想画图分析来着... 回头发现自己连顺序是啥都忘记了
没有思路了... 大概是老了吧

我得恶补一下算法了... 我居然不知道,左边的必须全是比 pivot 小的元素、右边的全得是比 pivot 大的元素,才能交换啊!唉
可怜的、不是天才的 duangsuse,长得又不好看,emmmmm....

然后是 [A-Z] [a-z] [0-9] 的排序

class StringComparator: Comparator<String> {
override fun compare(r: String, l: String): Int = toLevel(r).compareTo(toLevel(l))
override fun equals(o: Any?) = o is StringComparator // Stateless comparator

companion object Helper {
fun toLevel(str: String) = str.fold(0, ::appendLevel)
private fun appendLevel(acc: Int, ch: Char) = acc + getCharLevel(ch)
fun getCharLevel(ch: Char): Int = when (ch) {
in 'A'..'Z' -> ch - 'A'
in 'a'..'z' -> ch - 'a' + 26
in '0'..'9' -> ch - '0' + ('a'-'0'+2)
else -> 0
}
}
}
}

且慢,那么,假若我们希望开头的字符优先级更高呢?
这就是我们用 fold 而不是 sum 的原因啊!

修改一下:

fun toLevel(str: String) = str.fold((0, str.size), ::appendLevel)
private fun appendLevel(acc: Int, chnk: (Char, Int)) = (acc + chnk.second * getCharLevel(chnk.first), chnk.second - 1)

:: 从第一个字符开始以字符串长度数,每个字符都递减,然后把当前字符的长度,乘当前字符的优先级

那么是测试时间了呢。
好了,我玩够了。


和画出 mindmap,给大家讲出『Vibrator 到 System Service』所有的知识点。

快快写完睡觉吧。
结果
TextSort.kt
2 KB
This media is not supported in your browser
VIEW IN TELEGRAM
https://github.com/duangsuse/RandomPicture 我必须得感谢自己,因为我使我自己发现了无计划死拖死缠的坏处和自己能力、记忆的有限... #life #tech #dev
RandomPicture.kt
3.8 KB
duangsuse::Echo
Photo
熬夜了一个通宵,真的是相当无聊,我感觉我学习的效率也很低下,差点连 Docker 都没学到!
校验:记下了一个名字:openjdk:8u171-jdk-alpine3.8
但是效果的确很差!而且熬夜导致身体变差

真的不值得死磕
#Kotlin #backend #JVM #Java 但我肯定我的思路是没问题的,很清晰,但是我没有在可以接受的时间、可以接受的抄代码/记忆量内完成... 成功反驳了对脑力有限论调加以批判的年轻人 🐸 #Moha 好暴力
duangsuse::Echo
Photo
val ACCEPTABLE_REGEX = ACCEPTABLE_EXTENSIONS.split(',').fold(StringBuilder()) { ac, x -> ac.append("|\\.").append(x) }.let { "($it)" }.let(::Regex)

private fun isImageFile(f: File) = ACCEPTABLE_REGEX.matches(f.name)

这两行代码很有趣

它实现了 Trumeet 写了四行的功能(它利用扩展名判断一个 File 是不是可以接受的图像文件类型),不过可能会被说是 ineffective (低效)的

用更简洁的话说就是 ACCEPTABLE_EXTENSIONS.split(',').join("|\\.").let { "($it)" },不过我觉得用 fold 的话秀技一点,因为我就这点技术再不秀就没啥可秀的了,所以就是这种无聊的东西也得秀啊!(跑路


Trumeet 的实现是:

 for (acceptableName in ACCEPTABLE_FILES) {
if (name.toLowerCase().endsWith(acceptableName.toLowerCase())) {
return@listFiles true
}
}
logger.warn("Ignoring unacceptable file $name")
return@listFiles false

换句话说:
对于所有可接受的扩展名:
若文件名的小写以扩展名的小写结尾:
让过滤器接受此文件
警告:
"Ignoring unacceptable file $name"
让过滤器忽视此文件

其实根本不该说低效,稍微了解一点算法和数据结构的人都会知道,Regex,尤其是 JVM 这种大型平台上的正则表达式引擎,可以是高度优化的,举个例子,看起来使用正则低效

其实很多正则引擎基于复杂而快速的各种状态机匹配算法 — NFA、DFA,正则表达式也类似各种 parser compiler,但它的匹配是可以高度优化的,如果使用类似前缀树的方式进行剪枝,复杂度不至于变成 O(n**m) (指 lowercase 和比较子串次数,对每个文件名,都要把文件名和所有可接受文件后缀名比较一次),而正则就可以做到这种匹配优化,ACCEPTABLE_REGEX 的构造也就是这样的:

"png,jpg,jpeg,gif,webp,raw,bmp,img,svg".split(',').fold(StringBuilder()) { ac, x -> ac.append("|\\.").append(x) }.let { "($it)" }.let(::println)

(|\.png|\.jpg|\.jpeg|\.gif|\.webp|\.raw|\.bmp|\.img|\.svg)

非常模式化

我还抽提出了一个定义

fun <A, B> Future<A>.catching(chain: Future<B>): Future<A> = this.setHandler { if (this.succeeded()) chain.complete() else chain.fail(this.cause()) }

它是这么用的,把当前的信号链接到另一个 Future 上

val startServerPromise = Future.future<HttpServer> {
fut ->
server = vertx.createHttpServer()
val router = setupRouter()
server.requestHandler(router)
server.listen(env(ENV_PORT)?.toInt() ?: PORT_DEF, fut)
}.catching(startFuture)

顺带一提,如果要 equalsIgnoreCase 的话直接写上就好

("ABC" as java.lang.String).equalsIgnoreCase("abc")

还有,上面那个『判断算法』的原 index 方法的逻辑其实相当不简洁,有点啰嗦:

fun index(root: File) |>
root.listFiles do |parent, name|
if theFile is not File(a Dir) |> proceed, return
if theFile is acceptable |> proceed
otherwise, logger warn file is not acceptable, filter the file
then stream |>
if theFile is plain file, add its absolute path to picture list
if not, then it must be Dir, logger info recursively scan file tree



以一个更高的抽象层次换说,就是:

列出目标文件夹里的所有文件,然后,对于每一文件:
若它是文件夹,则跳过以下步骤
若它是文件,并且是图像文件,则接受此文件
否则,logger 输出警告,忽略此文件

对于上一步结果中的每一文件:
若它是文件,则将它的绝对路径加入图片列表里
若它是目录,则对它应用上一个步骤

其实为啥不能是这样,反而更简洁高效一些:

fun index(root: File) |>
root.listFiles.forEach |>
when:
it.isFile and isAcceptable(it) -> pictures.add(it.absolutePath)
it.isDir -> index(it).also { logger info }
else -> logger warn, ignore file


或者很啰嗦地说:

列出目标文件夹里的所有文件,然后,对于每一文件:
若它是目录,递归深度优先扫描
若它是文件并且是图像文件,则将它的绝对路径加入图像列表
否则(不可接受)logger 输出警告

我是直接用了 Kotlin 的 FileTreeWalker

啊,我感觉我老了,代码都不会写了... 😵
#PL #CS 有道理。 #China 我以后要写一个