【问题标题】:Replacing for loops for searching list in kotlin在 kotlin 中替换用于搜索列表的 for 循环
【发布时间】:2019-06-12 18:20:19
【问题描述】:

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

我有两个array lists accountscards。 我的目标是 search 借助其 card-number 在名为卡的数组列表中的特定卡。 然后我必须validate the pin。如果 pin 是 correct,通过得到 gift card's customerId 我必须 search account 中的 account 命名为 accounts。然后我要updatebalanceaccount

这些是我用过的类

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)
    }

【问题讨论】:

    标签: for-loop kotlin apply inline


    【解决方案1】:

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

    // 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

    最后但并非最不重要的是你的类的可变性。这可能是有道理的,但它也可能是危险的。由您决定自己的需要和要求。


    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
    }
    

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

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

    【讨论】:

      【解决方案2】:

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

      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返回null,相当于没有找到与所需卡号匹配的卡号,我们将立即返回Pair(false, false)。这是代码的最后一步。

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

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

      PPS:我强烈建议尝试寻找另一种方法来更新列表而不改变对象。我不知道你的用例,但这感觉不太线程安全。我没有为此发布任何解决方案,因为它超出了这个问题的范围。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-16
        • 2017-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多