元ネタ
シャッフルする文字をあらかじめ抜き出しておいて、残りの文字のテンプレートに埋め込む方法
fun main() {
"マクザカバグ"
.toList()
.shuffled()
.let { "${it[0]}ー${it[1]}・${it[2]}ッ${it[3]}ー${it[4]}ー${it[5]}" }
.also { println(it) }
}
半手動という感じ。
偶数インデックスの文字だけシャッフルする方法
fun main() {
"マーク・ザッカーバーグ"
.map { "$it" } // CharSequence を List<String> に変換する。
.let { it + "" } // 要素数が偶数になるように、List<String> の末尾に空文字列を加える。
.withIndex()
.partition { (index, _) -> index % 2 == 0 } // 偶数インデックスの要素と奇数インデックスの要素に分ける。
.let { (evens, odds) -> evens.shuffled() zip odds }
.flatMap { it.toList() }
.joinToString("") { it.value }
.also { println(it) }
}
要素数を偶数にしないと zip
で最後の1つが無視されてしまうため、
最初に各文字(Char
)を文字列(String
)にして最後に空文字を追加している。
(奇数の場合だけ追加、とすべきだろうが、今回は省略した。)
withIndex
するのはいいが、IndexedValue
になった値から元の値を取り出す(it.value
)のが後の方になっているのでわかりにくい(partition
の直後にやってもいいが、2つの List
に対して行わなければならないので冗長になる)。
partition
関数にも filterIndexed
関数のような、predicate にインデックスがついてくるものが欲しい。
partition
ではなく filterIndexed
を使うと次のようになる。
fun main() {
"マーク・ザッカーバーグ"
.map { "$it" }
.let { it + "" }
.let {
it.filterIndexed { index, c -> index % 2 == 0 } to
it.filterIndexed { index, c -> index % 2 != 0 }
}
.let { (even, odd) -> even.shuffled() zip odd }
.flatMap { it.toList() }
.joinToString("")
.also { println(it) }
}
filterIndexed
がほぼ同じ引数で2回出てくるのが冗長だが、partition
版より読みやすいか。
文字列のスワップを使う方法
fun main() {
val original = "マーク・ザッカーバーグ"
val replacingIndex = original.indices.step(2).toList() // -> [0, 2, 4, 6, 8, 10]
buildString {
append(original)
(replacingIndex zip replacingIndex.shuffled()).forEach { (from, to) ->
val fromChar = this[from]
val toChar = this[to]
this[from] = toChar
this[to] = fromChar
}
}.also { println(it) }
}
ワンライナーにはできないのでこんな感じで。
String
に、指定されたインデックスの文字を入れ替える関数さえあれば、一応ワンライナー(1文)にできる。
fun main() {
"マーク・ザッカーバーグ".let {
it to it.indices.step(2).toList()
}.let { (original, replacingIndexes) ->
(replacingIndexes zip replacingIndexes.shuffled())
.fold(original) { str, (from, to) ->
str.swapped(from, to)
}
}.also { println(it) }
}
/** 指定されたインデックスの文字を入れ替える。 */
fun String.swapped(atA: Int, atB: Int): String =
StringBuilder(this).also {
val charA = it[atA]
val charB = it[atB]
it[atA] = charB
it[atB] = charA
}.toString()
/続くかも