替换用于在 kotlin 中搜索列表的循环

Replacing for loops for searching list in kotlin

我正在尝试使用 Kotlin's built-in 函数尽可能干净地转换我的代码。我已经使用 for loops 完成了部分代码。但我想知道用于此应用程序的高效内置函数

我有两个 array lists accountscards。 我的目标是 search 一张特定的卡片,在其 card-number 的帮助下,在名为卡片的数组列表中。 那我得validate the pin。如果引脚是 correct,通过获取 gift card's customerId 我必须 search array list 中的 account 命名为 accounts .然后我要update balanceaccount.

这些是我用过的class

class Account{
    constructor( )
    var id : String = generateAccountNumber()
    var name: String? = null
        set(name) = if (name != null) field = name.toUpperCase() else { field = "Unknown User"; println("invalid details\nAccount is not Created");}
    var balance : Double = 0.0
        set(balance) = if (balance >= 0) field = balance else { field = 0.0 }
    constructor(id: String = generateAccountNumber(), name: String?,balance: Double) {
        this.id = id
        this.balance = balance
        this.name = name
    }
}

class GiftCard {
    constructor( )
    var cardNumber : String = generateCardNumber()
    var pin: String? = null
        set(pin) = if (pin != null) field = pin else { field = "Unknown User"; println("Please set the pin\nCard is not Created");}
    var customerId : String = ""
        set(customerId) = if (customerId != "") field = customerId else { field = "" }
    var cardBalance : Double = 0.0
        set(cardBalance) = if (cardBalance > 0) field = cardBalance else { field = 0.0; println("Card is created with zero balance\nPlease deposit") }
    var status = Status.ACTIVE
    constructor(cardNumber: String = generateCardNumber(),
                pin: String,
                customerId: String,
                cardBalance: Double = 0.0,
                status: Status = Status.ACTIVE){
        this.cardNumber = cardNumber
        this.pin = pin
        this.customerId = customerId
        this.cardBalance = cardBalance
        this.status = status
    }
}

这是代码的一部分,我必须更改:


override fun closeCard(cardNumber: String, pin: String): Pair<Boolean, Boolean> {
        for (giftcard in giftcards) {
            if (giftcard.cardNumber == cardNumber) {
                if (giftcard.pin == pin) {
                    giftcard.status = Status.CLOSED
                    for (account in accounts)
                        account.balance = account.balance + giftcard.cardBalance
                    giftcard.cardBalance = 0.0
                    return Pair(true,true)
                }
                \invalid pin
                return Pair(true,false)
            }
        }
        \card is not present
        return Pair(false,false)
    }

可能有上百万种不同的方法可以做到这一点,但这里至少有一些我认为值得分享的语言特性:

fun closeCard(cardNumber: String, pin: String): Pair<Boolean, Boolean> {
  val giftCard = giftcards.find { it.cardNumber == cardNumber }
                    ?: return Pair(false, false)

  return if (giftCard.pin == pin) {
      giftCard.status = Status.CLOSED
      accounts.forEach {
        it.balance += giftCard.cardBalance
      }
      Pair(true, true)
  } else 
      Pair(true, false)
}

首先要注意的是 Elvis 运算符 - ?: - 如果左侧为 null,则计算表达式的右侧。在这种情况下,如果find returns null,相当于没有找到符合要求的卡号,我们会立即return Pair(false, false).这是您代码中的最后一步。

从那里开始就很简单了。如果引脚匹配,则使用 forEach 循环遍历 accounts 列表并关闭卡片。如果引脚不匹配,那么我们将直接转到 else 分支。在 kotlin 中,if 可以用作表达式,因此我们可以简单地将 return 语句放在 if 之前,并让它 return 每个表达式的最后一个表达式的结果分支.

PS:我不会说这比你的方式更有效率。这只是使用 built-in 函数的一种方式 - findforEach - 如您所问,以及其他语言功能。

PPS:我强烈建议尝试寻找另一种方法来更新列表而不改变对象。我不知道你的用例,但这感觉不太thread-safe。我没有post解决这个问题,因为它超出了这个问题的范围。

两个class都不是很地道。 Kotlin class 的主构造函数是隐式的,不需要定义,但是,您显式定义了一个构造函数,因此您添加了另一个空的构造函数。

// good
class C

// bad
class C {
    constructor()
}

更进一步,Kotlin 具有命名参数和默认值,因此请充分利用它们。

class Account(
    val id: String = generateAccountNumber(),
    val name: String = "Unknown User",
    val balance: Double = 0.0
)

Double 由于其缺点,基本上对于任何事情来说都是一个非常糟糕的选择,例如 https://www.floating-point-gui.de/ 选择 IntLong,甚至 BigDecimal 会更好。似乎您也不希望余额低于零,在这种情况下请考虑 UIntULong.

最后但同样重要的是 class 的可变性。这是有道理的,但也可能很危险。您可以根据自己的需要和要求来决定。


enum class Status {
    CLOSED
}

@ExperimentalUnsignedTypes
class Account(private var _balance: UInt) {
    val balance get() = _balance

    operator fun plusAssign(other: UInt) {
        _balance += other
    }
}

@ExperimentalUnsignedTypes
class GiftCard(
    val number: String,
    val pin: String,
    private var _status: Status,
    private var _balance: UInt
) {
    val status get() = _status
    val balance get() = _balance

    fun close() {
        _status = Status.CLOSED
        _balance = 0u
    }
}

@ExperimentalUnsignedTypes
class Main(val accounts: List<Account>, val giftCards: List<GiftCard>) {
    fun closeCard(cardNumber: String, pin: String) =
        giftCards.find { it.number == cardNumber }?.let {
            (it.pin == pin).andAlso {
                accounts.forEach { a -> a += it.balance }
                it.close()
            }
        }
}

inline fun Boolean.andAlso(action: () -> Unit): Boolean {
    if (this) action()
    return this
}

我们将 return 类型从 Pair<Boolean, Boolean> 更改为更惯用的 Boolean? ,其中 Null 表示我们没有找到任何东西(字面意思是 Null)、false PIN 不匹配,true 礼品卡已关闭。我们不再创建一对,因此避免了额外的对象分配。

Boolean.andAlso() 是一个方便的扩展函数,我通常会随身携带它,它类似于 Kotlin 的 STD 中的 Any.also(),但仅在 Boolean 是时才执行 action实际上 true.