Замена на петли для поиска в списке в котлине - PullRequest
0 голосов
/ 12 июня 2019

Я пытаюсь преобразовать мой код как можно более чистым, используя функции Kotlin's built-in Я сделал некоторую часть кода, используя for loops. Но я хочу знать эффективные встроенные функции, которые будут использоваться для этого приложения

У меня есть два array lists accounts и cards. Моя цель - search конкретная карта с помощью ее card-number в списке массивов с именем cards. Тогда я должен validate the pin. Если пин correct, то для получения gift card's customerId мне нужно search account в array list с именем accounts. Тогда я должен update balance из account.

Это класс, который я использовал

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

Ответы [ 2 ]

1 голос
/ 13 июня 2019

Оба класса не очень идиоматичны.Первичный конструктор класса 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/ Выбор Int, Long, черт возьми, даже BigDecimal будет лучше.Также кажется, что вы не хотите, чтобы баланс когда-либо падал ниже нуля, в этом случае рассмотрите UInt и ULong.

И последнее, но не менее важное: изменчивость вашего класса.Это может иметь смысл, но это также может быть опасно.Это зависит от вас, чтобы решить ваши потребности и требования.


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<Boolean, Boolean> на более идиоматический Boolean?, где Null означает, что мы не сделалинайдите что-нибудь (буквально истинное значение Null), false, что PIN-код не совпадает, и true, что подарочная карта была закрыта.Мы больше не создаем пару и, таким образом, избегаем дополнительного выделения объекта.

Boolean.andAlso() - это удобная функция расширения, которую я обычно держу под рукой, она как Any.also() из STD Котлина, но выполняет только action, если Boolean на самом деле true.

1 голос
/ 12 июня 2019

Вероятно, существует миллион различных способов сделать это, но вот тот, который, по крайней мере, имеет некоторые языковые особенности, которыми, на мой взгляд, стоит поделиться:

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

Первое, на что нужно обратить внимание, если оператор Элвиса - ?: - вычисляет правую часть выражения, если левая сторона равна null. В этом случае, если find вернет null, что эквивалентно отсутствию номера карты, соответствующей желаемому, мы немедленно вернем Pair(false, false). Это последний шаг в вашем коде.

Оттуда все довольно просто. Если контакты совпадают, вы просматриваете список accounts с помощью forEach и закрываете карту. Если контакты не совпадают, мы пойдем прямо к ветви else. В kotlin if может использоваться как выражение, поэтому мы можем просто поместить оператор return перед if и позволить ему возвращать результат последнего выражения в каждой ветви.

PS: Я не скажу, что это эффективнее, чем вы. Это всего лишь один из способов использования встроенных функций - find и forEach - как вы и просили, а также других языковых функций.

PPS: я настоятельно рекомендую попробовать и найти другой способ обновления списков без изменения объектов. Я не знаю ваших вариантов использования, но это не слишком потокобезопасно. Я не опубликовал никакого решения для этого, потому что это выходит за рамки этого вопроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...