Skip to content

Instantly share code, notes, and snippets.

@CattenLinger
Created February 13, 2020 03:21
Show Gist options
  • Save CattenLinger/fdc935448828a51d19f9222c055f4170 to your computer and use it in GitHub Desktop.
Save CattenLinger/fdc935448828a51d19f9222c055f4170 to your computer and use it in GitHub Desktop.
CustomBaseNumberKt
package net.catten.demo
import java.util.*
import kotlin.math.abs
private const val dummy = '_'
private val codec = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toSet().toCharArray()
private val base = codec.size
private val max = Long.MAX_VALUE.toCustomBase()
private val maxLength = max.length
fun Long.toCustomBase(): String {
if (this == 0L) return codec[0].toString()
var number = abs(this)
var r = ""
while (number > 0) {
r += codec[(number % base).toInt()]
number /= base
}
return r.reversed()
}
private val random = Random()
fun Long.toCustomBase(length: Int): String {
val l = if (length > maxLength) maxLength else length
return toCustomBase().let { s ->
if (s.length >= l)
s
else
"$dummy$s".let { ls ->
if (ls.length == l)
ls
else
(l - ls.length)
.downTo(1)
.map { codec[random.nextInt(base)] }
.joinToString(separator = "") { "$it" }
.plus(ls)
}
}
}
fun String.parseCustomBaseNumber(): Long {
var n = 0L
var p = 1L
for (c: Char in reversed()) {
if (c == dummy) break
n += codec.indexOf(c) * p
p *= base
}
return n
}
fun main() {
val bigNumber = abs(random.nextLong())
val encoded = bigNumber.toCustomBase(11)
val decoded = encoded.parseCustomBaseNumber()
println("""
Base : $base
Max : $max
Max Length : ${max.length}
------------------------------------
Origin : $bigNumber
Encoded : $encoded
Length : ${encoded.length}
Decoded : $decoded
""".trimIndent())
}
@CattenLinger
Copy link
Author

CattenLinger commented Feb 13, 2020

来自 @duangsuse 的点评:
我也 重复了一下

个人建议:下次

  • iter.joinToString("") { "$it" } 这类可读性不高的还是用 iter.joinToString("", Char::toString) 代替好了,尤其是 dollar string 不能滥用。
  • 切忌把 Kotlin IO 写回 Haskell 式 IO,一旦发现代码太“空”,要及时调整,let 什么的是用来提升可读性的,不要用它降低代码密集度。
  • n, p 这样的名字可以说是 meaningless…… n 叫 accumulator 吧。尤其是那个 p,怎么能叫 p 呢……

你写了 10 行的 parseCustomNumber() 核心思想其实就两行:

fun toFold(str: String): Long = generateSequence(1L) { it * base/*k*/ }.zip(str.reversed().asSequence()) /*d*/
  .sumBy { it.first * radix.indexOf(it.second) } 

用 Kotlin 官方风格写是这样:

fun parseCustomNumber(str: String): Long {
  var accumulator = 0L
  var k = 1
  for (c in string) {
    accumulator += k * radix.indexOf(c)
    k *= base
  }
  return accumulator
}

我说的另一种顺序是指直接连着 accumulator 去 shift

fun parseCustomNumber(str: String): Long {
  var accumulator = 0L
  for (c in string) {
    accumulator = accumulator*base + radix.indexOf(c)
  return accumulator
}

当然这个也可以写到一行内

fun parseCustomNumber(str: String) = str.fold(0L) { acc, c -> acc * base + radix.indexOf(c) }

个人建议:算法相关的逻辑不要放到 extension fun 里,
算法和框架的扩展一定严格区分开,不然有点怪怪的……

fun Long.toCustomBase(length: Int): String {
    val l = if (length > maxLength) maxLength else length

    return toCustomBase().let { s ->
        if (s.length >= l)
            s
        else
            "$dummy$s".let { ls ->
                if (ls.length == l)
                    ls
                else
                    (l - ls.length)
                            .downTo(1)
                            .map { codec[random.nextInt(base)] }
                            .joinToString(separator = "") { "$it" }
                            .plus(ls)
            }
    }
}

忍不住想到了 readLn >>= \ls -> putStrLn ls

这种代码我不说炫技,但难看是坐实的。
提升 indent level 在这里是很浪费的行为,因为它的缩进已经很深了

Kotlin 的确是强大,但简单却是它不同于其他语言的重要特征

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment