From dc837e20953ba7cea4461ca9e15524eabc6df732 Mon Sep 17 00:00:00 2001 From: sergeych Date: Sun, 24 Aug 2025 00:09:38 +0300 Subject: [PATCH] +shiffle +List.binarySearch --- docs/Array.md | 37 +++++++++++++++++++ docs/Iterable.md | 1 + docs/List.md | 7 ++-- .../kotlin/net/sergeych/lyng/obj/Obj.kt | 2 - .../kotlin/net/sergeych/lyng/obj/ObjArray.kt | 21 +++++++++++ .../net/sergeych/lyng/obj/ObjIterable.kt | 1 - .../kotlin/net/sergeych/lyng/obj/ObjList.kt | 4 ++ .../lyng/stdlib_included/root_lyng.kt | 4 ++ lynglib/src/commonTest/kotlin/ScriptTest.kt | 37 +++++++++++++++++-- lynglib/src/jvmTest/kotlin/BookTest.kt | 5 +++ 10 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 docs/Array.md diff --git a/docs/Array.md b/docs/Array.md new file mode 100644 index 0000000..d828768 --- /dev/null +++ b/docs/Array.md @@ -0,0 +1,37 @@ +# Array + +It's an interface if the [Collection] that provides indexing access, like `array[3] = 0`. +Array therefore implements [Iterable] too. The well known implementatino of the `Array` is +[List]. + +Array adds the following methods: + +## Binary search + +When applied to sorted arrays, binary search allow to quicly find an index of the element in the array, or where to insert it to keep order: + + val coll = [1,2,3,4,5] + assertEquals( 2, coll.binarySearch(3) ) + assertEquals( 0, coll.binarySearch(1) ) + assertEquals( 4, coll.binarySearch(5) ) + + val src = (1..50).toList().shuffled() + val result = [] + for( x in src ) { + val i = result.binarySearch(x) + assert( i < 0 ) + result.insertAt(-i-1, x) + } + assertEquals( src.sorted(), result ) + >>> void + +So `binarySearch(x)` returns: + +- index of `x`, a non-negative number +- negative: `x` not found, but if inserted at position `-returnedValue-1` will leave array sorted. + +To pre-sort and array use `Iterable.sorted*` or in-place `List.sort*` families, see [List] and [Iterable] docs. + +[Collection]: Collection.md +[Iterable]: Iterable.md +[List]: List.md diff --git a/docs/Iterable.md b/docs/Iterable.md index fd7686e..66f07b3 100644 --- a/docs/Iterable.md +++ b/docs/Iterable.md @@ -95,6 +95,7 @@ These, again, does the thing: | sortedBy(predicate) | sort by comparing results of the predicate function | | joinToString(s,t) | convert iterable to string, see (2) | | reversed() | create a list containing items from this in reverse order | +| shuffled() | create a listof shiffled elements | (1) : throws `NoSuchElementException` if there is no such element diff --git a/docs/List.md b/docs/List.md index 2e48f97..d7ea8ba 100644 --- a/docs/List.md +++ b/docs/List.md @@ -128,8 +128,9 @@ List could be sorted in place, just like [Collection] provide sorted copies, in | `[Range]` | get slice of the array (copy) | Range | | `+=` | append element(s) (2) | List or Obj | | `sort()` | in-place sort, natural order | void | -| 'sortBy(predicate)` | in place sort bu `predicate` call result (3) | void | -| `SortWith(comparator) | in place sort using `comarator` function (4) | void | +| `sortBy(predicate)` | in-place sort bu `predicate` call result (3) | void | +| `sortWith(comparator)` | in-place sort using `comarator` function (4) | void | +| `shiffle()` | in-place shiffle contents | | (1) : optimized implementation that override `Array` one @@ -143,7 +144,7 @@ instance is appended. If you want to append an Iterable object itself, use `add` order, e.g. is same as `list.sortWith { a,b -> predicate(a) <=> predicate(b) }` (4) -: comparator callable takes tho arguments and must return: negative value when first is less, +: comparator callable takes tho arguments and must return: negative value when first is less, positive if first is greater, and zero if they are equal. For example, the equvalent comparator for `sort()` will be `sort { a, b -> a <=> b } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt index 3608efe..6e58c1b 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt @@ -324,10 +324,8 @@ open class Obj { addFn("apply") { val body = args.firstAndOnly() (thisObj as? ObjInstance)?.let { - println("apply in ${thisObj is ObjInstance}, ${it.instanceScope}") body.callOn(ApplyScope(this, it.instanceScope)) } ?: run { - println("apply on non-instance $thisObj") body.callOn(this) } thisObj diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArray.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArray.kt index 324bb23..b06a828 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArray.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArray.kt @@ -50,5 +50,26 @@ val ObjArray by lazy { addFn("indices") { ObjRange(0.toObj(), thisObj.invokeInstanceMethod(this, "size"), false) } + + addFn("binarySearch") { + val target = args.firstAndOnly() + var low = 0 + var high = thisObj.invokeInstanceMethod(this, "size").toInt() - 1 + + while (low <= high) { + val mid = (low + high) / 2 + val midVal = thisObj.getAt(this, ObjInt(mid.toLong())) + + val cmp = midVal.compareTo(this, target) + when { + cmp == 0 -> return@addFn (mid).toObj() + cmp > 0 -> high = mid - 1 + else -> low = mid + 1 + } + } + + // Элемент не найден, возвращаем -(точка вставки) - 1 + (-low - 1).toObj() + } } } \ No newline at end of file diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjIterable.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjIterable.kt index 422c48b..c2c7962 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjIterable.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjIterable.kt @@ -141,6 +141,5 @@ val ObjIterable by lazy { list.list.reverse() list } - } } \ No newline at end of file diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjList.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjList.kt index ed97867..d2dcd47 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjList.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjList.kt @@ -269,6 +269,10 @@ class ObjList(val list: MutableList = mutableListOf()) : Obj() { thisAs().quicksort { a, b -> comparator.call(this, a, b).toInt() } ObjVoid } + addFn("shuffle") { + thisAs().list.shuffle() + ObjVoid + } } } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt index ebdcf48..536ac43 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt @@ -131,6 +131,10 @@ fun Iterable.sortedBy(predicate) { sortedWith { a, b -> predicate(a) <=> predicate(b) } } +fun Iterable.shuffled() { + toList().apply { shuffle() } +} + fun List.toString() { "[" + joinToString(",") + "]" } diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index edc82ad..435b0d6 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -3188,7 +3188,8 @@ class ScriptTest { @Test fun testSort() = runTest { - eval(""" + eval( + """ val coll = [5,4,1,7] assertEquals( [1,4,5,7], coll.sortedWith { a,b -> a <=> b }) assertEquals( [1,4,5,7], coll.sorted()) @@ -3200,7 +3201,8 @@ class ScriptTest { @Test fun testListSortInPlace() = runTest { - eval(""" + eval( + """ val l1 = [6,3,1,9] l1.sort() assertEquals( [1,3,6,9], l1) @@ -3212,6 +3214,35 @@ class ScriptTest { // 1 3 2 1 // we hope we got it also stable: assertEquals( [1,9,6,3], l1) - """) + """ + ) } + + @Test + fun binarySearchTest() = runTest { + eval( + """ + val coll = [1,2,3,4,5] + assertEquals( 2, coll.binarySearch(3) ) + assertEquals( 0, coll.binarySearch(1) ) + assertEquals( 4, coll.binarySearch(5) ) + """.trimIndent() + ) + } + + @Test + fun binarySearchTest2() = runTest { + eval( + """ + val src = (1..50).toList().shuffled() + val result = [] + for( x in src ) { + val i = result.binarySearch(x) + assert( i < 0 ) + result.insertAt(-i-1, x) + } + assertEquals( src.sorted(), result ) + """.trimIndent()) + } + } \ No newline at end of file diff --git a/lynglib/src/jvmTest/kotlin/BookTest.kt b/lynglib/src/jvmTest/kotlin/BookTest.kt index 65f7eb4..07e2b65 100644 --- a/lynglib/src/jvmTest/kotlin/BookTest.kt +++ b/lynglib/src/jvmTest/kotlin/BookTest.kt @@ -327,4 +327,9 @@ class BookTest { runDocTests("../docs/serialization.md") } + @Test + fun testArray() = runBlocking { + runDocTests("../docs/Array.md") + } + } \ No newline at end of file