نحوه استفاده از کوتلین برای حل مشکلات کدگذاری

عکس پروفایل نویسنده

@ساجولوفوادیمس ساجولوف

مهندس ارشد نرم افزار @ 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

و خیلی بیشتر!

برچسب ها

با هکر نون همراه باشید

حساب رایگان خود را ایجاد کنید تا قفل تجربه خواندن سفارشی خود را باز کنید.