4
votes

Qu'est-ce que l'équivalent Kotlin de Java "assign and check"?

En Java, j'écris parfois le code comme suit:

 String obj = null;
 while ((obj = getObject()) != null) {
    // do smth with obj
 }

Dans Kotlin, une erreur de compilation est affichée:

Les attributions ne sont pas des expressions, et seules les expressions sont autorisées dans ce contexte

Quel est le meilleur équivalent de Kotlin?


11 commentaires

(Je sais que vous avez dit de ne pas suggérer de «meilleures» façons, mais c'est comme ça :))


@FedericoklezCulloca Je pense que OP voulait dire la readLine comme exemple, et non comme un moyen de lire des lignes


@Lino Oui, c'était juste un exemple de "assigner et vérifier". Mise à jour de la question.


De: discuter.kotlinlang.org/t/ … , vous pouvez utiliser: while ({line = readLine (); line} ()! = Null)


@ 4ntoine l'a compris. A voté pour la réouverture.


@Lino, ça marche mais je suppose que c'est mauvais pour des raisons de performances car c'est juste un appel lambda qui renvoie un objet


@ 4ntoine par contre, si quelque chose n'est pas idiomatique à la langue, c'est une mauvaise forme (et, comme tu l'as dit, un possible problème de performances) d'essayer de le forcer. Ce serait comme essayer de forcer le style GOTO dans java. En règle générale, choisissez simplement ce que propose la langue.


generateSequence {getObject ()} .forEach {obj -> ...} peut vous aider ici ... l'a ajouté comme réponse à la question dupliquée ...


Je pense que c'est toujours un doublon ... juste que readLine a attiré trop d'attention là-bas ... mais la question est la même ... quel est l'équivalent approprié de var x: Any ?; while ((x = someFunction ())! = null) { dans Kotlin?


À mon humble avis, chaque situation de ce type est mieux servie par une fonction d'assistance personnalisée telle que Reader.eachLine {} . Le général est plus impliqué que toi. A voté pour la réouverture.


quelle réponse vous voulez donner à cette question qui n'est pas déjà dans Affectation non autorisée dans l'expression while ?


5 Réponses :


3
votes

La solution ad-hock la plus simple est probablement

reader.forEachLine {
    println(it)
}

Cependant, les cas spéciaux sont mieux servis par l'OMI par des fonctions d'assistance spécialisées. Par exemple, la lecture d'un fichier ligne par ligne peut être effectuée avec un assistant readLines comme expliqué dans une réponse a > à une question similaire:

while(true) {
    val obj = getObj() ?: break
}


2 commentaires

Je n'aime pas vraiment le temps (vrai) avec une pause. Je ne pense pas que ce soit une bonne pratique car c'est contre-intuitif et difficile à déboguer. Cela peut être utile parfois pour des situations peu courantes, mais c'est inhabituel ...


remplacez getObj () par reader.readLine () et vous avez la même réponse que dans la question précédemment dupliquée, ... (stackoverflow.com/a/43557893/6202869 ) (mais: pas de vote négatif de ma part)



2
votes

J'ai bricolé et j'ai mis au point une fonction d'assistance générale intéressante:

var result = null
var assignment = { result = getObject(); result };
while (assignment() != null){
    // do something with "result"
}

qui peut être appelée comme ceci:

var result = null
while ({ result = getObject(); result }() != null){
    // do something with "result"
}

Vous pouvez bien sûr ne pas utiliser cette fonction d'assistance et rester avec le pendant

while(true){
    val result = getObject() ?: break
    // do something with "result"
}

Une autre solution serait de créer un inline lambda, puis invoquez immédiatement cela:

::getObject.untilNull { /* do something with "it" */ }

Cela pourrait probablement être optimisé si vous "sauvegardiez" d'abord le lambda:

inline fun <T> (() -> T?).untilNull(action: (T) -> Unit) { 
    while (true) action(this() ?: break) 
}


10 commentaires

hmm ... sauf du untilNull J'ai vu toutes les variantes de cette réponse aussi ici ... et pour être honnête ... que untilNull -usage me semble un peu étrange ...


@Roland Oui, c'est étrange, car il combine beaucoup de choses différentes, en ligne, génériques, fonctions d'extension, lambdas et bien sûr l'opérateur elvis avec un break . Il pourrait être écrit de manière plus verbeuse, mais je pense que c'est principalement un style de code que l'on aimerait suivre


toujours je ne vois aucun avantage (ni en readabilty, ni autre chose) à utiliser :: getObject (). tillNull quand il existe generateSequence {getObject ()} qui fait essentiellement le pareil (enfin ... vous obtenez une séquence là-bas que je privilégierai dans presque tous les cas ;-))


@Roland Je regarde Sequence comme je regarde les Stream java 8, ils expriment parfaitement ce que vous voulez faire (bien sûr seulement si vous les utilisez correctement) , mais ils génèrent des tonnes de frais généraux, beaucoup et beaucoup d'objets créés qui sont pour la plupart simplement jetés. La même chose est vraie pour toutes ces utilisations de Collections.forEach () , pourquoi ne pas utiliser une boucle for normale? Encore une fois, je pense que c'est principalement basé sur l'opinion, quelle peut être la meilleure approche.


en parlant d'impact: quel est l'impact de :: getObject (). untilNull ... untilNull est inline , donc le bloc est probablement ok ... mais qu'en est-il de :: getObject () lui-même? ;-) la surcharge que vous avez mentionnée concernant Stream / Sequence est (selon le cas d'utilisation bien sûr) souvent négligeable ... Les avantages que vous obtenez: lisibilité, court- les circuits, etc. l'emportent souvent sur les inconvénients ...


@Lino Je pense qu'une meilleure alternative à votre dernière serait while (run {result = getObject (); result! = Null}) ... . Sans enregistrer le lambda. Ensuite, il sera intégré (et IMO plus lisible).


@Roland :: getObject est également un argument de fonction pour un fun inline , il sera également intégré.


@AlexeyRomanov que voulez-vous dire exactement avec votre dernier commentaire? Sera-t-il également intégré en douceur s'il s'agit en fait d'une fonction d'extension de fonction?


@Roland Le récepteur d'une fonction d'extension n'est qu'un autre argument, donc il peut être inséré (s'il a un type de fonction et qu'il s'agit d'une référence lambda ou de méthode, pas par exemple une variable).


@AlexeyRomanov n'est-il pas l'avantage d'avoir une fonction en ligne qui est incorporée dans le code où elle est appelée? Ce que vous écrivez est certainement vrai pour les fonctions d'extension non intégrées, mais est-ce également vrai pour la fonction d'extension de fonction ~ intégrée? Je suppose (mais je ne sais pas) que l'inlining de la fonction d'extension de fonction ne mettra pas en ligne le récepteur lui-même, mais cela pourrait le faire pour les paramètres .. et si tel est le cas, quel serait alors l'avantage (ou l'inconvénient) de en utilisant une telle construction? Si je me trompe, cela vaut peut-être même la peine de poser sa propre question.



5
votes

Je préfère abandonner la fantaisie et le faire à l'ancienne, ce qui est plutôt intuitif.

 var obj = getObject();
 while (obj != null) {
    // do smth with obj
    obj = getObject();
 }


1 commentaires

le seul dommage est que vous écrivez le même code essentiellement deux fois alors ... et plus vous en faites dans la boucle while , plus il est caché ...: - /



3
votes

Si vous souhaitez simplement remplacer while ((x = y.someFunction ())! = null) , vous pouvez utiliser ce qui suit à la place:

generateSequence { y.someFunction().takeIf { /* yourCondition... */ } }

generateSequence vous extraira toutes les valeurs une par une jusqu'à ce que le premier null soit atteint. Vous pouvez remplacer le .forEach code > avec un réduire code > ou fold (ou toute autre chose qui semble appropriée ;-)) si vous voulez conserver la dernière valeur ou additionner les valeurs à autre chose.

Si vous avez besoin de vérifier quelque chose d'autre, vous pouvez ajoutez simplement quelque chose comme takeIf , par exemple:

generateSequence { y.someFunction() }
          .forEach { x -> /* what you did in your while */ }

essentiellement en répétant ce que j'ai également mentionné ici .


2 commentaires

Pourquoi? J'ai ajouté quelque chose à propos de useLines , etc. là-bas, ce qui n'est pas vraiment pertinent ici ... mais je pense aussi que c'est une question en double de la question liée ...


Ah oui, je viens de le voir plus tôt et me ressemblait presque , c'est pourquoi je l'ai mentionné



0
votes

Que diriez-vous de:

while (getObject().apply { obj = this } != null) {
    // ...
}


0 commentaires