@ساجولوفوادیمس ساجولوف
مهندس ارشد نرم افزار @ Facebook
من با بسیاری از توسعه دهندگان اندروید صحبت کرده ام و اکثر آنها از کوتلین هیجان زده شده اند. من نیز هستم. هنگامی که من تازه یادگیری کوتلین را شروع کردم ، من در حال حل کردن کوتلین کوانس بودم ، و همراه با سایر ویژگی های عالی ، از قدرت توابع برای انجام عملیات در مجموعه ها تحت تأثیر قرار گرفتم. از آن زمان ، من سه سال را صرف نوشتن کد کوتلین کردم اما به ندرت از تمام پتانسیل زبان استفاده کردم.
در طول این سال ، من بیش از صد مشکل کدگذاری در Leetcode در جاوا داشتم. من به Kotlin تغییر نكردم ، زیرا نحو جاوا 6 را به خوبی می دانم ، به طوری كه بدون زحمت می توانم كد را بدون اتمام خودكار و برجسته سازی نحو بنویسم. اما من ویژگی های جدید جاوا را پیگیری نکردم ، زیرا پشتیبانی از Android SDK جاوا از بسیاری نسخه ها عقب مانده است. من برای حل سریع مشکلات به کوتلین مراجعه نکردم.
گرچه چندین سال بود که کد کوتلین را می نوشتم ، اما احساس کردم که برای شناخت صحیح نحو و ساخت زبان نیاز به تلاش شناختی دیگری دارم. حل مشکلات الگوریتمی ، به ویژه تحت فشار زمان ، بسیار متفاوت از توسعه برنامه Android است. با این وجود ، هرچه بیشتر درباره کوتلین یاد بگیرم ، بیشتر فهمیدم که چه تعداد ویژگی قدرتمند را از دست داده ام و چه مقدار کد دیگ بخار را برای نوشتن نیاز دارم.
یک روز ، من تصمیم گرفتم که نیاز به ادامه کار دارم ، بنابراین جلسه جدیدی را در Leetcode شروع کردم و کامپایلر را به Kotlin تغییر دادم. من فقط چند مشکل آسان را حل کردم ، اما احساس می کنم چیزی برای اشتراک گذاری دارم.
حلقه ها
بیایید با حلقه شروع کنیم. بیایید بگوییم ، شما یک
IntArray
از 10 عنصر
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
و می خواهید چاپ کنید
123456789
.
val array = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
for(index in (1 until array.size)) {
print(array[index])
}
(1 until array.size)
هست یک
IntRange
، کلاسی که طیف وسیعی از مقادیر را نشان می دهد
Int
. اولین عنصر در این محدوده است
1
و آخرین مورد است
9
همانطور که ما استفاده کردیم
until
برای حذف آخرین مقدار ما نمی خواهیم بدست آوریم
ArrayIndexOutOfBoundsException
درست؟
اما اگر بخواهیم همه عناصر آرایه را چاپ کنیم ، به جز عنصر در شاخص 5 ، چه می شود؟ مثل این
012346789
. بیایید کمی بیشتر کوتلینی و سپس نوشتن یک مقاله را بدست آوریم
if
بیانیه در حلقه
val array = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
for(index in array.indices - 5) {
print(array[index])
}
array.indices
دامنه شاخص های معتبر آرایه را برمی گرداند. در این مورد
array.indices
نشان دادن
IntRange
از
(0..9)
. ساخت
(0..9) - 5
نتیجه می دهد
[0, 1, 2, 3, 4, 6, 7, 8, 9]
. این دقیقاً همان چیزی است که ما به آن نیاز داریم.
کوتلین همچنین توانایی تکرار از تعداد بیشتر به تعداد کمتر را با استفاده از آن فراهم می کند
downTo
. اندازه گام تکرار را نیز می توان با استفاده از تغییر داد
step
.
val array = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
for(index in array.size - 1 downTo 1 step 2) {
print(array[index])
}
کد بالا با نتیجه در
97531
.
مصوت ها را از یک رشته حذف کنید
این شماره شماره 1119 در Leetcode است.
با توجه به یک رشته S ، واکه های “a” ، “e” ، “i” ، “o” و “u” را از آن جدا کرده و رشته جدید را برگردانید.
حتی در جاوا یک راه حل regex 1 خط وجود دارد ، اما شهود من به شرح زیر بود:
1. StringBuilder ایجاد کنید
2. تکرار نویسه ها ، و اگر کاراکتر فعلی مصوت نیست ، آن را به StringBuilder اضافه کنید
3. رشته را از StringBuilder برگردانید
public String removeVowels(String S) {
StringBuilder sb = new StringBuilder();
for(char s: S.toCharArray()) {
if(s != 'a' && s != 'e' && s != 'i' && s !='o' && s != 'u') {
sb.append(s);
}
}
return sb.toString();
}
کوتلین چطور؟ روش اصطلاحی بیشتر استفاده از آن است
filter()
یا
filterNot()
.
fun removeVowels(S: String): String {
val vowels = setOf('a', 'e', 'i', 'o', 'u')
return S.filter { it !in vowels }
}
filter {predicate: (Char) -> Boolean}
رشته ای را شامل می کند که فقط شامل آن نویسه ها از رشته اصلی باشد که با گزاره داده شده مطابقت داشته باشد.
اما به جای وارونه کردن
!in
بیایید استفاده کنیم
filterNot()
fun removeVowels(S: String): String {
val vowels = setOf('a', 'e', 'i', 'o', 'u')
return S.filterNot { it in vowels }
}
این حتی برای یک مبتدی ساده بود. بیایید به چیزی کمی پیچیده تر برویم.
جمع آرایه 1d در حال اجرا است
این یک مشکل آسان دیگر از Leetcode است. شماره 1480.
با توجه به تعداد آرایه. ما یک مقدار در حال اجرا از یک آرایه را به عنوان runningSum تعریف می کنیم[i] = جمع (تعداد)[0]… شماره[i]) جمع در حال اجرا را بازگردانید.
ورودی: nums = [1,2,3,4]
خروجی: [1,3,6,10]
توضیح: مبلغ جاری به شرح زیر بدست می آید: [1, 1+2, 1+2+3, 1+2+3+4].
بنابراین باید با تکرار آرایه ، مقدار موجود در شاخص فعلی را به مجموع در حال اجرا اضافه کنیم و آن را در آرایه نتیجه به همان شاخص قرار دهیم.
آیا در کوتلین چیزی برای کمک به ما در رابطه با مبلغ جاری وجود دارد؟ خوب ، تغییرات مختلفی وجود دارد
fold()
و
reduce()
عملیات
در اینجا یک توضیح خوب از این توابع است. اما از زمان کوتلین 1.4 حتی موارد بیشتری وجود دارد:
runningFold()
و
runningReduce()
. همانطور که می خواهیم با اولین عنصر شروع کنیم و یک آرایه را برگردانیم ، به نظر می رسد
runningReduce()
همان چیزی است که ما به آن نیاز داریم بیایید امضای آن را بررسی کنیم.
/**
* Returns a list containing successive accumulation values generated by applying [operation] from left to right
* to each element and current accumulator value that starts with the first element of this array.
*
* @param [operation] function that takes current accumulator value and an element, and calculates the next accumulator value.
*
* @sample samples.collections.Collections.Aggregates.runningReduce
*/
@SinceKotlin("1.4")
@kotlin.internal.InlineOnly
public inline fun IntArray.runningReduce(operation: (acc: Int, Int) -> Int): List<Int>
کمی پیچیده به نظر می رسد ، اما وقتی نمونه ای را مشاهده کنید منطقی خواهد بود.
fun runningSum(nums: IntArray): IntArray {
return nums.runningReduce { sum, element -> sum + element }.toIntArray()
}
این کل راه حل برای حل مشکل جمع با استفاده از کوتلین است
runningReduce()
تابع.
sum
با اولین شروع می شود
element
در آرایه ، عنصر بیانگر عنصر فعلی است. در لامبدا ، مقدار بعدی را محاسبه می کنیم
sum
. آه … من حدس می زنم توضیح من روشن تر نیست که یک سند. بیایید فقط مقادیر را چاپ کنیم
sum
و
element
در هر مرحله:
sum: 1; element: 2; sum + element: 3
sum: 3; element: 3; sum + element: 6
sum: 6; element: 4; sum + element: 10
sum: 10; element: 5; sum + element: 15
و آرایه ای که برمی گردانیم ، می باشد
[1, 3, 6, 10, 15]
. وجود ندارد
sum + element: 1
، خط را از دست ندادم. مسئله این است که
runningReduce
، همانطور که در سند مشاهده می کنیم ، اولین مقدار را به عنوان جمع کننده اولیه می گیرد.
متأسفانه ، Leetcode هنوز از Kotlin 1.4 پشتیبانی نمی کند ، بنابراین کد بالا ممکن است کامپایل نشود.
رایج ترین کلمه
مشکل Leetcode ، شماره 819.
با توجه به یک پاراگراف و لیستی از کلمات ممنوع ، متداول ترین کلمه ای را که در لیست کلمات ممنوع نیست ، برگردانید. تضمین شده است حداقل یک کلمه وجود دارد که ممنوع نیست ، و پاسخ منحصر به فرد است.
ورودی:
پاراگراف = “باب به یک توپ برخورد کرد ، ضربه BALL خیلی بعد از ضربه برخورد کرد.”
ممنوع = [“hit”]
خروجی: “توپ”
مراحل حل آن چیست؟
1. رشته را به حروف کوچک تبدیل کرده و با کلمات تقسیم کنید.
[bob, hit, a, ball, the, hit, ball, flew, far, after, it, was, hit]
2. مجموعه ای از کلمات ممنوع ایجاد کنید.
[hit]
3-به استثنای کلمات ممنوع ، نقشه ای از کلمات را برای وقوع آنها ایجاد کنید.
{bob=1, a=1, ball=2, the=1, flew=1, far=1, after=1, it=1, was=1}
4. کلمه ای را با بیشترین تعداد وقوع از نقشه برگردانید.
ball
بیایید این 4 مرحله را در جاوا پیاده سازی کنیم.
public String mostCommonWord(String paragraph, String[] banned) {
// 1. Covert string to lower case and split by words.
String[] words = paragraph.replaceAll("[^a-zA-Z0-9 ]", " ").toLowerCase().split("\s+");
// 2. Create a set of banned words.
Set bannedWords = new HashSet();
for (String word : banned)
bannedWords.add(word);
// 3. Create a map of words to their occurrence, excluding the banned words
Map wordCount = new HashMap();
for (String word : words) {
if (!bannedWords.contains(word))
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
// 4. Return word with the highest number of occurrences from the map.
return Collections.max(wordCount.entrySet(), Map.Entry.comparingByValue()).getKey();
}
و همین 4 مرحله در کوتلین.
fun mostCommonWord(paragraph: String, banned: Array<String>): String {
// 1. Covert string to lower case and split by words.
val words = paragraph.toLowerCase().split("\W+|\s+".toRegex())
// 2. Create a set of banned words.
val bannedSet = banned.toHashSet()
// 3. Create a map of words to their occurrence, excluding the banned words
val wordToCount = words.filterNot { it in bannedSet }.groupingBy { it }.eachCount()
// 4. Return word with the highest number of occurrences from the map.
return wordToCount.maxBy { it.value }!!.key
}
حالا بیایید توابع را مرور کنیم تا ببینیم در اینجا چه اتفاقی می افتد.
1. رشته را به دو قسمت تقسیم می کنیم
words: List
. نوع استنباط می شود.
2. تبدیل کردن
banned: Array
به
HashSet
ساختن
in
چک در زمان O (1)
3. در این مرحله 3 تماس تابع را زنجیره می کنیم. اول ، ما استفاده می کنیم
filterNot()
برای فیلتر کردن کلمات ممنوع
filterNot { it in banned }
باز خواهد گشت
List
که فقط شامل آن رشته هایی است که در
banned
آرایه. بر خلاف
groupBy()
که نقشه را برمی گرداند ،
groupingBy()
یک شی returns را برمی گرداند
Grouping
نوع ، که بعداً می تواند با یکی از عملیات های گروه بندی و استفاده شود. ما از آن در استفاده می کنیم
groupingBy()
لامبدا این بدان معنی است که ما بر اساس عنصر فعلی (کلمه) مجموعه گروه بندی می شویم. به عبارت دیگر – ما یک نقشه ایجاد می کنیم ، جایی که کلید یک کلمه است و مقدار آن تعداد وقایع کلمه است. برای بدست آوردن تعداد وقایع مورد استفاده ما
eachCount()
در
Grouping
.
4. ما استفاده می کنیم
maxBy()
تابع برای بدست آوردن اولین عنصر بزرگ در نقشه ، توسط
value
. این یک شی object را به ما برمی گرداند
Map.Entry?
، به عنوان مثال
ball = 2
. و ما یک کلید برمی گردانیم ، که رایج ترین کلمه در جمله است.
ترتیب عناصر
وقتی مجموعه ای را با استفاده از ایجاد می کنید
setOf(“a”, “b”, “c”)
یا تبدیل آرایه به مجموعه با استفاده از
arrayOf(“a”, “b”, “c”).toSet()
مجموعه برگشتی است
LinkedHashSet
و بنابراین ترتیب تکرار عناصر حفظ می شود.
در مورد نقشه ها نیز همین مسئله وجود دارد
mapOf(Pair(“a”, 1), Pair(“b”, 2))
arrayOf(Pair(“a”, 1), Pair(“b”, 2)).toMap()
هر دو عملکرد نمونه ای از را برمی گردانند
LinkedHashMap
که نظم عنصر اصلی را حفظ می کند. دانستن آن ممکن است هنگام حل مشکلات مفید باشد.
من فقط برخی از توابع مجموعه موجود در کوتلین را پوشش داده ام. وجود دارد
map
،
flatMap
،
count
،
find
،
sum
،
partition
و خیلی بیشتر!
برچسب ها
حساب رایگان خود را ایجاد کنید تا قفل تجربه خواندن سفارشی خود را باز کنید.