कोटलिन में एकाधिक चर


127

क्या कोटलिन में एकाधिक अशक्त चर के लिए कई एकाधिक श्रृंखलाओं को देने का कोई तरीका है?

fun example(first: String?, second: String?) {
    first?.let {
        second?.let {
            // Do something just if both are != null
        }
    }
}

मेरा मतलब है, कुछ इस तरह:

fun example(first: String?, second: String?) {
    first?.let && second?.let { 
        // Do something just if both are != null
    }
}

1
क्या आपको एन आइटम चाहिए, सिर्फ 2 नहीं? क्या सभी वस्तुओं को एक ही प्रकार, या विभिन्न प्रकारों की आवश्यकता है? क्या सभी मानों को सूची में, या व्यक्तिगत मापदंडों के रूप में पारित किया जाना चाहिए? क्या वापसी मूल्य एकल आइटम या इनपुट के रूप में समान संख्या में आइटम का समूह होना चाहिए?
जैसन मिनार्ड

मुझे सभी तर्कों की आवश्यकता है, इस मामले के लिए दो हो सकते हैं, लेकिन अधिक के लिए ऐसा करने का एक तरीका जानना चाहते हैं, जिसमें स्विफ्ट इतना आसान है।
डैनियल गोमेज़ रिको

क्या आप नीचे दिए गए उत्तरों की तुलना में कुछ अलग देख रहे हैं, यदि ऐसा है तो टिप्पणी करें कि आप क्या अंतर चाहते हैं।
जैसन मिनार्ड

दूसरे लेट ब्लॉक के भीतर पहले "इट" को कैसे देखें?
जेवियर मेंडोंका

जवाबों:


48

यदि यहां रुचि है तो इसे हल करने के लिए मेरे दो कार्य हैं।

inline fun <T: Any> guardLet(vararg elements: T?, closure: () -> Nothing): List<T> {
    return if (elements.all { it != null }) {
        elements.filterNotNull()
    } else {
        closure()
    }
}

inline fun <T: Any> ifLet(vararg elements: T?, closure: (List<T>) -> Unit) {
    if (elements.all { it != null }) {
        closure(elements.filterNotNull())
    }
}

उपयोग:


// Will print
val (first, second, third) = guardLet("Hello", 3, Thing("Hello")) { return }
println(first)
println(second)
println(third)

// Will return
val (first, second, third) = guardLet("Hello", null, Thing("Hello")) { return }
println(first)
println(second)
println(third)

// Will print
ifLet("Hello", "A", 9) {
 (first, second, third) ->
 println(first)
 println(second)
 println(third)
}

// Won't print
ifLet("Hello", 9, null) {
 (first, second, third) ->
 println(first)
 println(second)
 println(third)
}

यह बहुत अच्छा है, लेकिन मैं अभी भी एक मामले को याद कर रहा हूं, जहां मैं दूसरे में पहले इनपुट का उपयोग कर सकता हूं। उदाहरण: ifLet ("ए", लॉवर (प्रथम)) {// पहला = "ए", दूसरा = "ए"}
ओटज़िएई

IfLet स्टेटमेंट में पहला तर्क अभी तक अलिखित नहीं है, आपके जैसा कोई फ़ंक्शन संभव नहीं है। क्या मैं गार्डलेट का उपयोग करने का सुझाव दे सकता हूं? यह बहुत सीधा है। वैल (पहला) = गार्डलेट (100) {रिटर्न} वैल (दूसरा) = गार्डलेट (101) {रिटर्न} वैल एवरेज = औसत (पहला, दूसरा) मुझे पता है कि वह नहीं है जो आपने पूछा है, लेकिन आशा है कि यह मदद करता है।
डारियो पेलेग्रिनी

धन्यवाद। मेरे पास इसे हल करने के कई तरीके हैं, कहने का कारण यह है कि स्विफ्ट के लिए संभव है कि कई ifLets एक दूसरे के बाद कॉमा द्वारा अलग हो जाएं और वे पिछले चेक के चर का उपयोग कर सकें। मेरी इच्छा है कि कोटलिन में भी यह संभव था। :)
ओटज़ीई

1
यह उत्तर स्वीकार किया जा सकता है, लेकिन हर कॉल पर उपरि है। क्योंकि vm सबसे पहले फंक्शन ऑब्जेक्ट बनाता है। डेक्स लिमिटेशन पर विचार करते हुए, यह प्रत्येक अद्वितीय जांच के लिए 2 विधि संदर्भों के साथ फ़ंक्शन क्लास घोषणा को जोड़ देगा।
ओलेक्ज़ेंडर अल्बुल

146

यहां कुछ विविधताएं हैं, इस बात पर निर्भर करता है कि आप किस शैली का उपयोग करना चाहते हैं, यदि आपके पास एक ही या अलग-अलग प्रकार की चीजें हैं, और यदि सूची में अज्ञात संख्या है ...

मिश्रित प्रकार, सभी को एक नए मूल्य की गणना करने के लिए अशक्त नहीं होना चाहिए

मिश्रित प्रकारों के लिए आप प्रत्येक पैरामीटर गणना के लिए फ़ंक्शंस की एक श्रृंखला बना सकते हैं जो मूर्खतापूर्ण लग सकती है, लेकिन मिश्रित प्रकारों के लिए अच्छी तरह से काम करें:

inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
    return if (p1 != null && p2 != null) block(p1, p2) else null
}
inline fun <T1: Any, T2: Any, T3: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, block: (T1, T2, T3)->R?): R? {
    return if (p1 != null && p2 != null && p3 != null) block(p1, p2, p3) else null
}
inline fun <T1: Any, T2: Any, T3: Any, T4: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, p4: T4?, block: (T1, T2, T3, T4)->R?): R? {
    return if (p1 != null && p2 != null && p3 != null && p4 != null) block(p1, p2, p3, p4) else null
}
inline fun <T1: Any, T2: Any, T3: Any, T4: Any, T5: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, p4: T4?, p5: T5?, block: (T1, T2, T3, T4, T5)->R?): R? {
    return if (p1 != null && p2 != null && p3 != null && p4 != null && p5 != null) block(p1, p2, p3, p4, p5) else null
}
// ...keep going up to the parameter count you care about

उदाहरण उपयोग:

val risk = safeLet(person.name, person.age) { name, age ->
  // do something
}   

जब सूची में कोई अशक्त आइटम न हो तो कोड का निष्पादन ब्लॉक करें

दो जायके यहाँ, पहले कोड के ब्लॉक को निष्पादित करने के लिए जब एक सूची में सभी गैर अशक्त आइटम होते हैं, और दूसरा ऐसा करने के लिए जब सूची में कम से कम एक शून्य आइटम नहीं होता है। दोनों मामले कोड के ब्लॉक में गैर-शून्य वस्तुओं की एक सूची देते हैं:

कार्य:

fun <T: Any, R: Any> Collection<T?>.whenAllNotNull(block: (List<T>)->R) {
    if (this.all { it != null }) {
        block(this.filterNotNull()) // or do unsafe cast to non null collectino
    }
}

fun <T: Any, R: Any> Collection<T?>.whenAnyNotNull(block: (List<T>)->R) {
    if (this.any { it != null }) {
        block(this.filterNotNull())
    }
}

उदाहरण उपयोग:

listOf("something", "else", "matters").whenAllNotNull {
    println(it.joinToString(" "))
} // output "something else matters"

listOf("something", null, "matters").whenAllNotNull {
    println(it.joinToString(" "))
} // no output

listOf("something", null, "matters").whenAnyNotNull {
    println(it.joinToString(" "))
} // output "something matters"

फ़ंक्शन के लिए एक मामूली परिवर्तन वस्तुओं की सूची प्राप्त करता है और एक ही संचालन करता है:

fun <T: Any, R: Any> whenAllNotNull(vararg options: T?, block: (List<T>)->R) {
    if (options.all { it != null }) {
        block(options.filterNotNull()) // or do unsafe cast to non null collection
    }
}

fun <T: Any, R: Any> whenAnyNotNull(vararg options: T?, block: (List<T>)->R) {
    if (options.any { it != null }) {
        block(options.filterNotNull())
    }
}

उदाहरण उपयोग:

whenAllNotNull("something", "else", "matters") {
    println(it.joinToString(" "))
} // output "something else matters"

इन विविधताओं को बदले मूल्यों जैसे बदला जा सकता है let()

पहले गैर-शून्य आइटम (Coalesce) का उपयोग करें

SQL Coalesce फ़ंक्शन के समान, पहला नॉन नल आइटम लौटाएं। समारोह के दो स्वाद:

fun <T: Any> coalesce(vararg options: T?): T? = options.firstOrNull { it != null }
fun <T: Any> Collection<T?>.coalesce(): T? = this.firstOrNull { it != null }

उदाहरण उपयोग:

coalesce(null, "something", null, "matters")?.let {
    it.length
} // result is 9, length of "something"

listOf(null, "something", null, "matters").coalesce()?.let {
    it.length
}  // result is 9, length of "something"

अन्य बदलाव

... अन्य विविधताएं हैं, लेकिन अधिक विनिर्देशन के साथ इसे कम किया जा सकता है।


1
तुम भी इस whenAllNotNullतरह से विनाशकारी के साथ जोड़ सकते हैं listOf(a, b, c).whenAllNotNull { (d, e, f) -> println("$d $e $f"):।
dumptruckman

10

आप इसके लिए अपना स्वयं का फ़ंक्शन लिख सकते हैं:

 fun <T, U, R> Pair<T?, U?>.biLet(body: (T, U) -> R): R? {
     val first = first
     val second = second
     if (first != null && second != null) {
         return body(first, second)
     }
     return null
 }

 (first to second).biLet { first, second -> 
      // body
 }

7

आप एक arrayIfNoNullsसमारोह बना सकते हैं :

fun <T : Any> arrayIfNoNulls(vararg elements: T?): Array<T>? {
    if (null in elements) {
        return null
    }
    @Suppress("UNCHECKED_CAST")
    return elements as Array<T>
}

तब आप इसका उपयोग मानों की एक चर संख्या के लिए कर सकते हैं let:

fun example(first: String?, second: String?) {
    arrayIfNoNulls(first, second)?.let { (first, second) ->
        // Do something if each element is not null
    }
}

यदि आपके पास पहले से ही एक सरणी है तो आप एक takeIfNoNullsफ़ंक्शन (द्वारा प्रेरित takeIfऔर requireNoNulls) बना सकते हैं :

fun <T : Any> Array<T?>.takeIfNoNulls(): Array<T>? {
    if (null in this) {
        return null
    }
    @Suppress("UNCHECKED_CAST")
    return this as Array<T>
}

उदाहरण:

array?.takeIfNoNulls()?.let { (first, second) ->
    // Do something if each element is not null
}

3

केवल दो मानों की जाँच के मामले में और सूचियों के साथ काम नहीं करने के लिए:

fun <T1, T2> ifNotNull(value1: T1?, value2: T2?, bothNotNull: (T1, T2) -> (Unit)) {
    if (value1 != null && value2 != null) {
        bothNotNull(value1, value2)
    }
}

उपयोग उदाहरण:

var firstString: String?
var secondString: String?
ifNotNull(firstString, secondString) { first, second -> Log.d(TAG, "$first, $second") }

2

वास्तव में, आप बस यह कर सकते हैं, आप जानते हैं? ;)

if (first != null && second != null) {
    // your logic here...
}

Kotlin में एक सामान्य अशक्त-जांच का उपयोग करने में कुछ भी गलत नहीं है।

और यह उन सभी के लिए कहीं अधिक पठनीय है जो आपके कोड को देखेंगे।


36
एक उत्परिवर्ती वर्ग के सदस्य के साथ व्यवहार करते समय यह पर्याप्त नहीं होगा।
मिशैल के

3
इस तरह के उत्तर देने की आवश्यकता नहीं है, प्रश्न का उद्देश्य इसे संभालने का अधिक "उत्पादक तरीका" खोजना है, क्योंकि भाषा letइन जांचों को करने के लिए शॉर्टकट प्रदान करती है
एलेजांद्रो मोया

1
स्थिरता के संदर्भ में, यह मेरी पसंद है, भले ही टिस सुरुचिपूर्ण रूप में न हो। यह स्पष्ट रूप से एक मुद्दा है जिसे हर समय हर कोई चलाता है, और भाषा से निपटना चाहिए।
ब्रिल पप्पिन 16

2

मैं वास्तव में निम्नलिखित सहायक कार्यों का उपयोग करके इसे हल करना पसंद करता हूं:

fun <A, B> T(tuple: Pair<A?, B?>): Pair<A, B>? =
    if(tuple.first == null || tuple.second == null) null
    else Pair(tuple.first!!, tuple.second!!)

fun <A, B, C> T(tuple: Triple<A?, B?, C?>): Triple<A, B, C>? =
    if(tuple.first == null || tuple.second == null || tuple.third == null) null
    else Triple(tuple.first!!, tuple.second!!, tuple.third!!)


fun <A, B> T(first: A?, second: B?): Pair<A, B>? =
    if(first == null || second == null) null
    else Pair(first, second)

fun <A, B, C> T(first: A?, second: B?, third: C?): Triple<A, B, C>? =
        if(first == null || second == null || third == null) null
        else Triple(first, second, third)

और यहां बताया गया है कि आपको उनका उपयोग कैसे करना चाहिए:

val a: A? = someValue
val b: B? = someOtherValue
T(a, b)?.let { (a, b) ->
  // Shadowed a and b are of type a: A and b: B
  val c: C? = anotherValue
  T(a, b, c)
}?.let { (a, b, c) ->
  // Shadowed a, b and c are of type a: A, b: B and c: C
  .
  .
  .
}

1

मैंने इसे कुछ ऐसे फंक्शन्स बनाकर हल किया जो कमोबेश उसी के व्यवहार को दोहराते हैं, लेकिन कई मापदंडों को लेते हैं और केवल सभी मापदंडों के कार्य को गैर-अशक्त मानते हैं।

fun <R, A, B> withNoNulls(p1: A?, p2: B?, function: (p1: A, p2: B) -> R): R? = p1?.let { p2?.let { function.invoke(p1, p2) } }
fun <R, A, B, C> withNoNulls(p1: A?, p2: B?, p3: C?, function: (p1: A, p2: B, p3: C) -> R): R? = p1?.let { p2?.let { p3?.let { function.invoke(p1, p2, p3) } } }
fun <R, A, B, C, D> withNoNulls(p1: A?, p2: B?, p3: C?, p4: D?, function: (p1: A, p2: B, p3: C, p4: D) -> R): R? = p1?.let { p2?.let { p3?.let { p4?.let { function.invoke(p1, p2, p3, p4) } } } }
fun <R, A, B, C, D, E> withNoNulls(p1: A?, p2: B?, p3: C?, p4: D?, p5: E?, function: (p1: A, p2: B, p3: C, p4: D, p5: E) -> R): R? = p1?.let { p2?.let { p3?.let { p4?.let { p5?.let { function.invoke(p1, p2, p3, p4, p5) } } } } }

फिर मैं इसे इस तरह उपयोग करता हूं:

withNoNulls("hello", "world", Throwable("error")) { p1, p2, p3 ->
    p3.printStackTrace()
    p1.plus(" ").plus(p2)
}?.let {
    Log.d("TAG", it)
} ?: throw Exception("One or more parameters was null")

इसके साथ स्पष्ट मुद्दा यह है कि मुझे प्रत्येक मामले (चर की संख्या) के लिए एक फ़ंक्शन को परिभाषित करना होगा, लेकिन कम से कम मुझे लगता है कि कोड का उपयोग करते समय साफ दिखता है।


1

आप भी ऐसा कर सकते हैं

if (listOfNotNull(var1, var2, var3).size == 3) {
        // All variables are non-null
}

कंपाइलर अभी भी शिकायत करेगा कि यह गारंटी नहीं दे सकता कि संस्करण शून्य नहीं हैं
पीटर ग्राहम

1

मैंने अपेक्षित उत्तर को थोड़ा उन्नत किया है:

inline fun <T: Any, R: Any> ifLet(vararg elements: T?, closure: (List<T>) -> R): R? {
    return if (elements.all { it != null }) {
        closure(elements.filterNotNull())
    } else null
}

यह संभव बनाता है:

iflet("first", "sconed") {
    // do somehing
} ?: run {
    // do this if one of the params are null
}

थॉट्स शांत हैं, लेकिन मापदंडों का नाम नहीं है और उन्हें प्रकार साझा करना चाहिए।
डैनियल गोमेज़ रिको

0

किसी भी मान की जाँच के लिए आप इसका उपयोग कर सकते हैं:

    fun checkNulls(vararg elements: Any?, block: (Array<*>) -> Unit) {
        elements.forEach { if (it == null) return }
        block(elements.requireNoNulls())
    }

और इसका उपयोग इस तरह किया जाएगा:

    val dada: String? = null
    val dede = "1"

    checkNulls(dada, dede) { strings ->

    }

ब्लॉक में भेजे गए तत्व वाइल्डकार्ड का उपयोग कर रहे हैं, यदि आप मूल्यों को एक्सेस करना चाहते हैं, तो आपको उन प्रकारों की जांच करने की आवश्यकता है, यदि आपको केवल एक ही प्रकार का उपयोग करने की आवश्यकता है, तो आप इसे जेनेरिक में बदल सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.