[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3
[1, 2, 3, -1, -2].lazy.filter({ $0 > 0 }).count // => 3
Quel est l'avantage d'ajouter lazy
à la deuxième instruction. Selon ma compréhension, lorsque la variable lazy
est utilisée, la mémoire est initialisée à cette variable au moment où elle est utilisée. Comment cela a-t-il un sens dans ce contexte?
Essayer de comprendre l'utilisation de LazySequence
en détail. J'avais utilisé les fonctions map
, reduce
et filter
sur les séquences, mais jamais sur la séquence lazy
. Besoin de comprendre pourquoi utiliser cela?
lazy
change la façon dont le tableau est traité. Lorsque lazy
n'est pas utilisé, filter
traite l'ensemble du tableau et stocke les résultats dans un nouveau tableau. Lorsque lazy
est utilisé, les valeurs de la séquence ou de la collection sont produites à la demande à partir des fonctions en aval. Les valeurs ne sont pas stockées dans un tableau; ils sont juste produits en cas de besoin.
Considérez cet exemple modifié dans lequel j'ai utilisé reduce
au lieu de count
afin que nous puissions imprimer ce qui se passe:
Ne pas utiliser lazy
:
Dans ce cas, tous les éléments seront filtrés en premier avant que quoi que ce soit ne soit compté.
[1, 2, 3, -1, -2].filter({ print("filtered one"); return $0 > 0 })
.reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 }
filtered one filtered one filtered one filtered one filtered one counted one counted one counted one
Utilisation de lazy
:
Dans ce cas, reduce
demande qu'un élément compte, et filter
fonctionnera jusqu'à ce qu'il en trouve un, puis reduce
en demandera un autre et filter
fonctionnera jusqu'à ce qu'il en trouve un autre.
[1, 2, 3, -1, -2].lazy.filter({ print("filtered one"); return $0 > 0 })
.reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 }
filtered one counted one filtered one counted one filtered one counted one filtered one filtered one
Quand utiliser lazy
:
option-cliquer sur lazy
donne cette explication:
À partir de la discussion pour lazy
:
Utilisez la propriété paresseuse lors du chaînage des opérations:
pour empêcher les opérations intermédiaires d'allouer du stockage
ou
lorsque vous n'avez besoin que d'une partie de la collection finale pour éviter les calculs inutiles
J'ajouterais un troisième:
lorsque vous souhaitez que les processus en aval démarrent plus tôt et que vous n'ayez pas à attendre que les processus en amont effectuent tout leur travail en premier
Ainsi, par exemple, vous voudriez utiliser lazy
avant filter
si vous recherchiez le premier Int
positif, car la recherche s'arrêterait dès que vous en auriez trouvé un et cela éviterait à filter
d'avoir à filtrer tout le tableau et cela éviterait d'allouer de l'espace au tableau filtré.
Pour le 3ème point, imaginez que vous avez un programme qui affiche des nombres premiers dans la plage 1...10_000_000
en utilisant filter
sur cette plage. Vous préférez afficher les nombres premiers tels que vous les avez trouvés plutôt que d'attendre de les calculer tous avant de montrer quoi que ce soit.
Je n'avais jamais vu cela auparavant alors j'ai fait quelques recherches et je l'ai trouvé.
La syntaxe que vous publiez crée une collection paresseuse. Une collection paresseuse évite de créer toute une série de tableaux intermédiaires pour chaque étape de votre code. Ce n'est pas très pertinent lorsque vous n'avez qu'une instruction de filtrage, cela aurait beaucoup plus d'effet si vous faisiez quelque chose comme filter.map.map.filter.map
, car sans la collection paresseuse, un nouveau tableau est créé à chaque étape.
Consultez cet article pour plus d'informations:
https://medium.com/developermind/lightning-read-1-lazy-collections-in-Swift-fa997564c1a
J'ai fait quelques analyses comparatives, et une série de fonctions d'ordre supérieur comme les cartes et les filtres est en fait un peu plus lente sur une collection paresseuse que sur une collection "régulière".
Il semble que les collections paresseuses vous offrent une plus petite empreinte mémoire au prix de performances légèrement plus lentes.
@discardableResult func timeTest() -> Double {
let start = Date()
let array = 1...1000000
let random = array
.map { (value) -> UInt32 in
let random = arc4random_uniform(100)
//print("Mapping", value, "to random val \(random)")
return random
}
let result = random.lazy //Remove the .lazy here to compare
.filter {
let result = $0 % 100 == 0
//print(" Testing \($0) < 50", result)
return result
}
.map { (val: UInt32) -> NSNumber in
//print(" Mapping", val, "to NSNumber")
return NSNumber(value: val)
}
.compactMap { (number) -> String? in
//print(" Mapping", number, "to String")
return formatter.string(from: number)
}
.sorted { (lhv, rhv) -> Bool in
//print(" Sorting strings")
return (lhv.compare(rhv, options: .numeric) == .orderedAscending)
}
let elapsed = Date().timeIntervalSince(start)
print("Completed in", String(format: "%0.3f", elapsed), "seconds. count = \(result.count)")
return elapsed
}
Dans le code ci-dessus, si vous modifiez la ligne
let result = random.lazy //Remove the .lazy here to compare
à
let result = random //Removes the .lazy here
Ensuite, il s'exécute plus rapidement. Avec paresseux, mon benchmark a pris environ 1,5 fois plus de temps avec la collection .lazy par rapport à un tableau droit.