元ネタ
シャッフルする文字をあらかじめ抜き出しておいて、残りの文字のテンプレートに埋め込む方法
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()
/続くかも