如何在Kotlin中无限懒惰地循环列表?

时间:2016-12-02 18:24:37

标签: clojure kotlin lazy-sequences

我有export default function UserAvatar(props) { const { size, src, name, style // <- Need to pull from props, ListItem put it here with React.cloneElement } = props; return ( <Avatar style={style} // <- Pass it onto the actual material-ui Avatar size={size || 40} src={src || null} > {src ? null : name.charAt(0)} </Avatar> ); } 的列表,想要在右转或左转时找到下一个方向。这是我的工作代码:

directions
  1. 但是,如果我可以采用enum class Turn { R, L } enum class Direction { N, E, S, W } val directionsInRightTurnOrder = listOf(Direction.N, Direction.E, Direction.S, Direction.W) private fun calculateNextHeading(heading: Direction, turn: Turn): Direction { val currentIndex = directionsInRightTurnOrder.indexOf(heading) var nextIndex = currentIndex + if (turn == Turn.R) 1 else -1 if (nextIndex >= directionsInRightTurnOrder.size) nextIndex = directionsInRightTurnOrder.size - nextIndex if (nextIndex < 0) nextIndex += directionsInRightTurnOrder.size return directionsInRightTurnOrder.get(nextIndex) } 列表并无限地(懒惰地)循环播放,那么这将更加简单易读。在Clojure中,我可以使用clojure.core/cycle
  2. 来做到这一点
    directionsInRightTurnOrder
    1. 另一件有用的事情是,如果我可以使用负索引在列表中查找,比如在Ruby或Python中:

    2. 问题:

      • 我可以通过Kotlin的列表/集合进行(take 5 (cycle ["a" "b"])) # ("a" "b" "a" "b" "a") 吗?
      • 在Kotlin中是否有一种惯用的方法来进行负索引查找?

4 个答案:

答案 0 :(得分:7)

此处cycle

fun <T : Any> cycle(vararg xs: T): Sequence<T> {
    var i = 0
    return generateSequence { xs[i++ % xs.size] }
}

cycle("a", "b").take(5).toList() // ["a", "b", "a", "b", "a"]

以下是如何实施转弯申请:

enum class Turn(val step: Int) { L(-1), R(1) }

enum class Direction {
    N, E, S, W;

    fun turned(turn: Turn): Direction {
        val mod: (Int, Int) -> Int = { n, d -> ((n % d) + d) % d }
        return values()[mod(values().indexOf(this) + turn.step, values().size)]
    }
}

听起来modulo就是您正在寻找的 - 负面索引环绕。无法在Kotlin的stdlib中找到它,所以我带了自己的。

Direction.N
    .turned(Turn.R) // E
    .turned(Turn.R) // S
    .turned(Turn.R) // W
    .turned(Turn.R) // N
    .turned(Turn.L) // W

Enum#values()Enum#valueOf(_)可让您以编程方式访问枚举成员。

答案 1 :(得分:3)

自定义序列,无限期地重复给定的序列或列表,可以很容易地写成flatten

fun <T> Sequence<T>.repeatIndefinitely(): Sequence<T> = 
    generateSequence(this) { this }.flatten()

fun <T> List<T>.repeatIndefinitely(): Sequence<T> =
    this.asSequence().repeatIndefinitely()

答案 2 :(得分:3)

您可以通过生成重复返回列表/集合然后展平它的序列来遍历Kotlin中的列表/集合。 e.g:

generateSequence { listOf("a", "b") }.flatten().take(5).toList()
// [a, b, a, b, a]

您可以定义自己的模数函数,用于将负数和正数强制转换为有效索引以访问列表中的元素(另请参阅Google Guava的IntMath.mod(int, int)):

infix fun Int.modulo(modulus: Int): Int {
    if (modulus <= 0) throw ArithmeticException("modulus $modulus must be > 0")
    val remainder = this % modulus
    return if (remainder >= 0) remainder else remainder + modulus
}

val list = listOf("a", "b", "c", "d")
list[-1 modulo list.size] // last element
list[-2 modulo list.size] // second to last element
list[+9 modulo list.size] // second element
list[-12 modulo list.size] // first element

答案 3 :(得分:0)

解释kotlin Slack上的讨论:

  • 使用List#modulo会使这更简单,但不如cycle更优雅,因为仍然需要处理负面索引。

  • 实施循环列表的一个选项是Sequence。但是,需要使用Sequence编写或生成自定义generateSequence。我们认为这种情况有点过头了。

最终我去了:

  1. Direction了解nextprevious
  2. enum class Direction {
        N, E, S, W;
    
        private val order by lazy { listOf(N, E, S, W) }
    
        fun add(turns: Int): Direction {
            val currentIndex = order.indexOf(this)
    
            var nextIndex = (currentIndex + turns) % order.size
            return order.possiblyNegativeLookup(nextIndex)
        }
    
        fun subtract(turns: Int) = add(-1 * turns)
        fun next(): Direction = add(1)
        fun previous(): Direction = subtract(1)
    }
    
    1. 使用List
    2. 扩展possiblyNegativeLookup
      fun <E> List<E>.possiblyNegativeLookup(i: Int): E {
          return if (i < 0) this[this.size + i] else this[i]
      }
      

      所以最终的代码变成了:

      val nextHeading = if (move.turn == Turn.R) heading.next() else heading.previous()