#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)
首先我们定义快速排序函数:
所以我索性直接抄了某本算法书上的算法,我说过我是本本主义者。
我开始居然忘记递归调用 quickSort 了... 最近思路有点崩溃,看不到数据的流动了。
然后居然又忘记了,基线条件,列表的大小必须得大于等于 2 才有顺序一说啊!
亏我还想画图分析来着... 回头发现自己连顺序是啥都忘记了
没有思路了... 大概是老了吧
我得恶补一下算法了... 我居然不知道,左边的必须全是比 pivot 小的元素、右边的全得是比 pivot 大的元素,才能交换啊!唉
可怜的、不是天才的 duangsuse,长得又不好看,emmmmm....
然后是 [A-Z] [a-z] [0-9] 的排序
这就是我们用 fold 而不是 sum 的原因啊!
修改一下:
那么是测试时间了呢。
好了,我玩够了。
和画出 mindmap,给大家讲出『Vibrator 到 System Service』所有的知识点。
快快写完睡觉吧。
完全默写,包括 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』所有的知识点。
快快写完睡觉吧。
duangsuse::Echo
好吧,好吧,duangsuse 来恢复一下心情,顺便写今天最后一个代码:默写重写 @YuutaW 的 RandomPicture 完全默写,包括 Gradle 构建文件(除了 Vertx 的依赖关系 Coordinate 在外) 并且,不得使用 IDEA 等具有自动补齐功能的 IDE == 首先,我们来玩个简单的文件排序游戏,因为 duangsuse 不是学算法的(我以后会学)所以不考虑算法层的东西,就快速排序吧(因为它不需要我花时间去记,皮一下哦)(反正怎么说我,交换、插入、选择排序里,我也只会快排、冒泡、简单选择,虽然…
虽说是简单,因为熬夜的缘故,居然花了 3 个小时,真是没效率。
This media is not supported in your browser
VIEW IN TELEGRAM
https://github.com/duangsuse/RandomPicture 我必须得感谢自己,因为我使我自己发现了无计划死拖死缠的坏处和自己能力、记忆的有限... #life #tech #dev
duangsuse::Echo
Photo
熬夜了一个通宵,真的是相当无聊,我感觉我学习的效率也很低下,差点连 Docker 都没学到!
校验:记下了一个名字:
真的不值得死磕
校验:记下了一个名字:
openjdk:8u171-jdk-alpine3.8
但是效果的确很差!而且熬夜导致身体变差真的不值得死磕
duangsuse::Echo
#Kotlin #backend #JVM #Java 但我肯定我的思路是没问题的,很清晰,但是我没有在可以接受的时间、可以接受的抄代码/记忆量内完成... 成功反驳了对脑力有限论调加以批判的年轻人 🐸 #Moha 好暴力
nextInt 是没有问题的,因为
Random.nextInt(100) 不会返回 100
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
列出目标文件夹里的所有文件,然后,对于每一文件:我是直接用了 Kotlin 的 FileTreeWalker
若它是目录,递归深度优先扫描
若它是文件并且是图像文件,则将它的绝对路径加入图像列表
否则(不可接受)logger 输出警告
啊,我感觉我老了,代码都不会写了... 😵
duangsuse::Echo
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 写了四行的功能(它利用扩展名判断一个…
花了一点时间继续重构... 无意义的事情,但是也有点可取
https://github.com/duangsuse/RandomPicture/blob/master/src/main/kotlin/org/duangsuse/fushion/RandomPicture.kt
修复,Random nextInt 的参数应该是 lastIndex(这个其实 Trumeet 也没有注意到,因为 Vertx HttpServer send 不存在的 File 时只是作为 logging 严重警告)
修复,Regex 匹配的方式不正确,现在能生成正确的 Regex 并且使用 joinToString 而不是 fold
[DuangSUSE@duangsuse]~/Projects/RandomPicture% RANDOM_PICTURE=/home/DuangSUSE/WallpapperSlide java -jar build/libs/server-1.0-SNAPSHOT.jar
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Checking with regex /^.*(\.png|\.jpg|\.jpeg|\.gif|\.webp|\.raw|\.bmp|\.img|\.svg)$/
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Begin indexing images
警告: 16 files ignored, 91 files added
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Finished indexing images size 91 costs 36 ms
Server started at http://localhost:8080
这个东西主要还是默写和熟悉 Kotlin/JVM 的一种方式,连异步/并行编程都没有,也没有反射 AOP 什么的... 虽然我各种技能栈都很垃圾。
待会有好玩的,这些权当做练习。
https://github.com/duangsuse/RandomPicture/blob/master/src/main/kotlin/org/duangsuse/fushion/RandomPicture.kt
修复,Random nextInt 的参数应该是 lastIndex(这个其实 Trumeet 也没有注意到,因为 Vertx HttpServer send 不存在的 File 时只是作为 logging 严重警告)
修复,Regex 匹配的方式不正确,现在能生成正确的 Regex 并且使用 joinToString 而不是 fold
[DuangSUSE@duangsuse]~/Projects/RandomPicture% RANDOM_PICTURE=/home/DuangSUSE/WallpapperSlide java -jar build/libs/server-1.0-SNAPSHOT.jar
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Checking with regex /^.*(\.png|\.jpg|\.jpeg|\.gif|\.webp|\.raw|\.bmp|\.img|\.svg)$/
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Begin indexing images
警告: 16 files ignored, 91 files added
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Finished indexing images size 91 costs 36 ms
Server started at http://localhost:8080
这个东西主要还是默写和熟悉 Kotlin/JVM 的一种方式,连异步/并行编程都没有,也没有反射 AOP 什么的... 虽然我各种技能栈都很垃圾。
待会有好玩的,这些权当做练习。
GitHub
duangsuse/RandomPicture
:framed_picture: A tiny vertx.HttpServer favorite list based random picture picker service: rewrite - duangsuse/RandomPicture
duangsuse::Echo
花了一点时间继续重构... 无意义的事情,但是也有点可取 https://github.com/duangsuse/RandomPicture/blob/master/src/main/kotlin/org/duangsuse/fushion/RandomPicture.kt 修复,Random nextInt 的参数应该是 lastIndex(这个其实 Trumeet 也没有注意到,因为 Vertx HttpServer send 不存在的 File 时只是作为 logging 严重警告) 修复,Regex…
因为摩天大楼不是从空想来的地基,这些『低级』『脑残』的事情总归还是要做的,必须学会了走路,学会好好开发才能走到更远的地方。
所以一方面必须继续学习计算机科学,另一方面,也得多做工程,很多人会去 Online Judge 写算法题,我没那个时间资本,只好做这些。
现在默写 Dockerfile:
所以一方面必须继续学习计算机科学,另一方面,也得多做工程,很多人会去 Online Judge 写算法题,我没那个时间资本,只好做这些。
现在默写 Dockerfile:
FROM openjdk:8u171-jdk-alpine3.6 AS builder现在默写 Gradle 的 Kotlin Options 和 shadowJar 配置:
ADD . /app
WORKDIR /app
###
RUN ./gradlew shadowJar && mv build/libs/*.jar /server.jar
###
FROM openjdk:8u171-jre-alpine3.6 AS env
COPY --from=builder /server.jar .
ENTRYPOINT java -jar server.jar
def expectedJvm = JavaVersion.JAVA_1_8
def manifestProp = ['Main-Verticle': 'org.duangsuse.fushion.RandomPicture']
compileKotlin {
KotlinOptions.languageVersion = '1.3'
KotlinOptions.jvmTarget = expectedJvm
// Kotlin reflect, metadata...
}
shadowJar {
basename 'server'
classifier null
version rootProject.version
manifest { attributes manifestProp }
}
duangsuse::Echo
因为摩天大楼不是从空想来的地基,这些『低级』『脑残』的事情总归还是要做的,必须学会了走路,学会好好开发才能走到更远的地方。 所以一方面必须继续学习计算机科学,另一方面,也得多做工程,很多人会去 Online Judge 写算法题,我没那个时间资本,只好做这些。 现在默写 Dockerfile: FROM openjdk:8u171-jdk-alpine3.6 AS builder ADD . /app WORKDIR /app ### RUN ./gradlew shadowJar && mv…
只要做到能默写的水平我就满足了。
现在能默写自动化构建系统脚本,以后就能默写更多诸如 JNDI、CI、OpenShift 的配置或脚本、Shellscripts,就能做到 DevOps 和更好的设计,理解跟宽阔的问题领域
这样写测试就不需要 Mock 对象了,思维风格在程序员和运维、打包者、用户之间可以灵活切换的话
就有可能熟悉诸如 AAPT2、Dexer 这种大型项目
希望全部都能默写下来,不能默写的话说明对模型不够熟悉,最好是用的时候也能联想到别人的抽象和实现方式,看到运行时的栈和堆、集合对象,能够看出分支控制流
甚至在需要的时候必须知道编译生成的字节码大概是怎么样的,需要像栈虚拟机一样执行程序
现在能默写自动化构建系统脚本,以后就能默写更多诸如 JNDI、CI、OpenShift 的配置或脚本、Shellscripts,就能做到 DevOps 和更好的设计,理解跟宽阔的问题领域
这样写测试就不需要 Mock 对象了,思维风格在程序员和运维、打包者、用户之间可以灵活切换的话
就有可能熟悉诸如 AAPT2、Dexer 这种大型项目
希望全部都能默写下来,不能默写的话说明对模型不够熟悉,最好是用的时候也能联想到别人的抽象和实现方式,看到运行时的栈和堆、集合对象,能够看出分支控制流
甚至在需要的时候必须知道编译生成的字节码大概是怎么样的,需要像栈虚拟机一样执行程序