Daha önce hiç varsayılan harita veya varsayılan dict kullandınız mı? Python’u biraz tanıyorsanız, muhtemelen bir defaultdict
noktada eylemde olduğunu gördünüz . Kotlin de bu küçük makalede göstermek istediğim benzer bir araçla geliyor.
Python defaultdict
belgelerini ve bazı örnekleri burada bulabilirsiniz, ancak temel kullanım durumu aşağıdaki kod parçasında gösterilmektedir:
from collections import defaultdict d = defaultdict(int) print(d["someKey"]) //0
Diğer defaultdict
tiplerle de kullanılabilir ve KeyError
kodunuzu çalıştırırken bir alamadığınızdan emin olun . Bunun yerine, bilinmeyen anahtarlar için varsayılan bir değer sağlar; bu, aşağıdakiler gibi algoritmaları gruplamak ve saymak için gerçekten yararlı olabilir:
from collections import defaultdict data = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)] d = defaultdict(set) for k, v in data: d[k].add(v) print(d.items()) # dict_items([('red', {1, 3}), ('blue', {2, 4})])
Bu yazıda size Kotlin’in varsayılan harita değerlerini nasıl kullandığını ve diğer alternatiflerin neler olduğunu göstermek istiyorum.
Basit bir problem
Bir koleksiyon alan ve orijinal koleksiyondaki öğeleri içeren bir harita döndüren ve bunların sayısıyla ilişkili genel bir işlev yazmak istediğimizi varsayalım:
fun <T> countOccurrences(values: Collection<T>): Map<T, Int>
Elbette, Kotlin’de bunu yapmanın doğal ve kolay bir işlevsel stil yolu olmasına rağmen, önce bazı yinelemeli yaklaşımları düşünmek istiyoruz. Aşağıdaki ile başlayalım.
Varsayılansız yinelemeli çözüm
fun <T> countOccurrences(values: Collection<T>): Map<T, Int> { val countMap = mutableMapOf<T, Int>() for (e in values) { val current = countMap[e] if (current == null) countMap[e] = 1 else countMap[e] = current + 1 } return countMap }
MutableMap
Bu algoritma için bir kullanabiliriz . Tüm değerleri tekrarlarken, daha önce görülüp görülmediğini görmek için haritanın içine bakarız. Öyleyse, sayacı artırırız; aksi takdirde, 1
haritanın ilk sayısını koyarız . Benzer, ancak geliştirilmiş bir çözüm bir sonraki bölümde gösterilmektedir.
Açık varsayılanlarla yinelemeli çözüm
//sampleStart countMap.putIfAbsent(e, 0) // getValue, as an alternative to index operator, assures that a non-nullable type is returned countMap[e] = countMap.getValue(e) + 1
Önceki çözümün bir alternatifi olarak, putIfAbsent
elemanın 0 ile başlatıldığından emin olmak için faydalanabiliriz, böylece sayacı aşağıdaki satırda güvenle artırabiliriz. Bu durumda yaptığımız şey açık bir varsayılan değer sağlamaktır. Başka bir benzer araç getOrDefault
:
countMap[e] = countMap.getOrDefault(e, 0) + 1
Açık varsayılanları sağlayarak putIfAbsent
veya getOrDefault
kolay ve anlaşılır çözümlerle yönlendirir, ancak daha iyisini yapabiliriz.
Kapalı varsayılanlarla yinelemeli çözüm
Python’a benzer şekilde defaultdict
, Kotlin denilen temiz bir uzantıyla gelir MutableMap::withDefault
ve şöyle görünür:
fun <K, V> MutableMap<K, V>.withDefault(defaultValue: (key: K) -> V): MutableMap<K, V>
Bu uzantı, haritada ilişkilendirilmiş bir değeri olmayan anahtarlar için bir başlatıcı sunalım. Bizim çözümümüzde kullanalım:
fun <T> countOccurrences(values: Collection<T>): Map<T, Int> { val countMap = mutableMapOf<T, Int>().withDefault { 0 } for (e in values) { countMap[e] = countMap.getValue(e) + 1 } return countMap }
Artık, yinelemede bilinmeyen değerlerle uğraşmamız gerekmediği için kodu daha da kolaylaştırır. Varsayılan bir harita kullandığımızdan, ondan güvenle değer alabilir ve kullandığımız sayacı artırabiliriz.
Önemli Not
Eklentiyi kullanırken, withDefault
dokümantasyonunun bir parçası olarak bilmeniz gereken önemli bir şey var :
[…] Bu örtük varsayılan değer, orijinal harita belirtilen anahtar için bir değer içermediğinde ve [Map.getValue] işlevi […] ile bir değer elde edildiğinde kullanılır
Varsayılan değer yalnızca kullanırsanız Map::getValue
, haritaya burada gözlemleyebileceğiniz gibi indeks operatörleri ile erişilirken geçerli değildir (çalıştır düğmesine basın):
fun main(){ val map = mutableMapOf<String, Int>().withDefault { 0 } println(map["hello"]) println(map.getValue("hello")) }
Bunun nedeni, Map
arayüzün sözleşmesidir :
Verilen [tuşa] karşılık gelen değeri ya
null
da böyle bir tuş haritada yoksa değeri döndürür .
Varsayılan haritalar bu sözleşmeyi yerine getirmek istediğinden null
, daha önce Kotlin forumunda tartışılan mevcut olmayan anahtarlar durumunda hiçbir şey iade edemezler .
Deyimsel gidelim
Pekâlâ, Kotlin’in gerçekten haritalardaki varsayılan değerlerle birlikte geldiğini gördüm, bu harika. Ancak yine de yukarıdaki örneklerde yaptığımız gibi kod yazmak aptalca mı? Bence buna bağlı. Kotlin’in yerleşik gruplama ve sayma işlevselliklerine sahip üstün fonksiyonel API’leri kullanarak çoğu durumda daha kolay çözülebilir. Bununla birlikte, alternatifleri bilmek iyidir, çünkü bazı durumlarda yinelemeli bir yaklaşımla gitmeyi tercih edebilirsiniz. Size kodumuzun basitleştirilmiş bir versiyonunu vereyim:
fun <T> countOccurrences(values: Collection<T>): Map<T, Int> = mutableMapOf<T, Int>().withDefault { 0 }.apply { for (e in values) { put(e, getValue(e) + 1) } }
Hala açık bir for
döngü kullanıyor ancak kullanımı apply
tek bir ifadeyle haritayı başlatmamıza olanak veren kullanımıyla sadeleşti .
Orada KOTLIN verilen sorunu çözmek için onlarca yol vardır, ama muhtemelen en basit bir olan fonksiyonel bir yaklaşım:
fun <T> countOccurrences(values: Collection<T>): Map<T, Int> = values.groupingBy { it }.eachCount()
Sonuç
Bu küçük makalede gösterildiği gibi, basit algoritmaları Kotlin’i kullanarak birçok farklı şekilde çözebilirsiniz. Python’a benzer şekilde, varsayılan bir değer sağlayıcılı bir haritayı başlatabilirsiniz, ancak düzenli dizin işlem erişiminin beklediğiniz gibi çalışmadığını bilmeniz gerekir. Belgelendiği gibi, getValue
fonksiyon kullanılmalıdır. Kotlin çok kullanışlı bir standart kütüphane ile birlikte geldiğinden, mümkün olduğunca kullanmayı düşünmek istersiniz. Bu, gruplama veya sayma gibi yaygın şeylerin uygulanmasının, çoğu durumda sıfırdan uygulanmaması, aksine standart kütüphanedeki mevcut işlevlerle yapılması gerektiği anlamına gelir.
GIPHY App Key not set. Please check settings