Compare commits

..

No commits in common. "0ec0ed96ee85acf1e5de10d3b42c67418b6594e8" and "cf791638029b75f7027dce382bd6e8e82f05d0a7" have entirely different histories.

12 changed files with 75 additions and 335 deletions

View File

@ -6,12 +6,6 @@ Is a [Iterable] with known `size`, a finite [Iterable]:
val size val size
} }
| name | description |
|------------------------|------------------------------------------------------|
(1)
: `comparator(a,b)` should return -1 if `a < b`, +1 if `a > b` or zero.
See [List], [Set] and [Iterable] See [List], [Set] and [Iterable]
[Iterable]: Iterable.md [Iterable]: Iterable.md

View File

@ -1,13 +1,9 @@
# Iterable interface # Iterable interface
The interface for anything that can be iterated, e.g. finite or infinite ordered set of data that can be accessed The interface for anything that can be iterated, e.g. finite or infinite ordered set of data that can be accessed sequentially. Almost any data container in `Lyng` implements it: `List`, `Set`, `Buffer`, `RingBuffer`, `BitBuffer`, `Range` and many others are `Iterable`, also `Collection` and `Array` interfaces inherit it.
sequentially. Almost any data container in `Lyng` implements it: `List`, `Set`, `Buffer`, `RingBuffer`, `BitBuffer`,
`Range` and many others are `Iterable`, also `Collection` and `Array` interfaces inherit it.
`Map` and `String` have `Iterable` members to access its contents too. `Map` and `String` have `Iterable` members to access its contents too.
Please see also [Collection] interface: many iterables are also collections, and it adds important features.
## Definition: ## Definition:
Iterable is a class that provides function that creates _the iterator_: Iterable is a class that provides function that creates _the iterator_:
@ -27,8 +23,7 @@ Iterator itself is a simple interface that should provide only to method:
Just remember at this stage typed declarations are not yet supported. Just remember at this stage typed declarations are not yet supported.
Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions available, for example
available, for example
val r = 1..10 // Range is Iterable! val r = 1..10 // Range is Iterable!
assertEquals( [9,10], r.takeLast(2).toList() ) assertEquals( [9,10], r.takeLast(2).toList() )
@ -39,13 +34,12 @@ available, for example
## joinToString ## joinToString
This methods convert any iterable to a string joining string representation of each element, optionally transforming it This methods convert any iterable to a string joining string representation of each element, optionally transforming it and joining using specified suffix.
and joining using specified suffix.
Iterable.joinToString(suffux=' ', transform=null) Iterable.joinToString(suffux=' ', transform=null)
- if `Iterable` `isEmpty`, the empty string `""` is returned. - if `Iterable` `isEmpty`, the empty string `""` is returned.
- `suffix` is inserted between items when there are more than one. - `suffix` is inserted between items when there are more than one.
- `transform` of specified is applied to each element, otherwise its `toString()` method is used. - `transform` of specified is applied to each element, otherwise its `toString()` method is used.
Here is the sample: Here is the sample:
@ -55,56 +49,34 @@ Here is the sample:
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30") assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
>>> void >>> void
## `sum` and `sumBy`
These, again, does the thing:
assertEquals( 6, [1,2,3].sum() )
assertEquals( 12, [1,2,3].sumOf { it*2 } )
// sum of empty collections is null:
assertEquals( null, [].sum() )
assertEquals( null, [].sumOf { 2*it } )
>>> void
## Instance methods: ## Instance methods:
| fun/method | description |
|------------------------|---------------------------------------------------------------------------------| | fun/method | description |
| toList() | create a list from iterable | |-------------------|---------------------------------------------------------------------------|
| toSet() | create a set from iterable | | toList() | create a list from iterable |
| contains(i) | check that iterable contains `i` | | toSet() | create a set from iterable |
| `i in iterator` | same as `contains(i)` | | contains(i) | check that iterable contains `i` |
| isEmpty() | check iterable is empty | | `i in iterator` | same as `contains(i)` |
| forEach(f) | call f for each element | | isEmpty() | check iterable is empty |
| toMap() | create a map from list of key-value pairs (arrays of 2 items or like) | | forEach(f) | call f for each element |
| map(f) | create a list of values returned by `f` called for each element of the iterable | | toMap() | create a map from list of key-value pairs (arrays of 2 items or like) |
| indexOf(i) | return index if the first encounter of i or a negative value if not found | | map(f) | create a list of values returned by `f` called for each element of the iterable |
| associateBy(kf) | create a map where keys are returned by kf that will be called for each element | | indexOf(i) | return index if the first encounter of i or a negative value if not found |
| first | first element (1) | | associateBy(kf) | create a map where keys are returned by kf that will be called for each element |
| last | last element (1) | | first | first element (1) |
| take(n) | return [Iterable] of up to n first elements | | last | last element (1) |
| taleLast(n) | return [Iterable] of up to n last elements | | take(n) | return [Iterable] of up to n first elements |
| drop(n) | return new [Iterable] without first n elements | | taleLast(n) | return [Iterable] of up to n last elements |
| dropLast(n) | return new [Iterable] without last n elements | | drop(n) | return new [Iterable] without first n elements |
| sum() | return sum of the collection applying `+` to its elements (3) | | dropLast(n) | return new [Iterable] without last n elements |
| sumOf(predicate) | sum of the modified collection items (3) | | joinToString(s,t) | convert iterable to string, see (2) |
| sorted() | return [List] with collection items sorted naturally |
| sortedWith(comparator) | sort using a comparator that compares elements (1) |
| 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 |
(1) (1)
: throws `NoSuchElementException` if there is no such element : throws `NoSuchElementException` if there is no such element
(2) (2)
: `joinToString(suffix=" ",transform=null)`: suffix is inserted between items if there are more than one, trasnfom is : `joinToString(suffix=" ",transform=null)`: suffix is inserted between items if there are more than one, trasnfom is optional function applied to each item that must return result string for an item, otherwise `item.toString()` is used.
optional function applied to each item that must return result string for an item, otherwise `item.toString()` is used.
(3)
: sum of empty collection is `null`
fun Iterable.toList(): List fun Iterable.toList(): List
fun Iterable.toSet(): Set fun Iterable.toSet(): Set
@ -114,6 +86,7 @@ optional function applied to each item that must return result string for an ite
fun Iterable.forEach(block: (Any?)->Void ): Void fun Iterable.forEach(block: (Any?)->Void ): Void
fun Iterable.map(block: (Any?)->Void ): List fun Iterable.map(block: (Any?)->Void ): List
fun Iterable.associateBy( keyMaker: (Any?)->Any): Map fun Iterable.associateBy( keyMaker: (Any?)->Any): Map
## Abstract methods: ## Abstract methods:
@ -129,12 +102,7 @@ Creates a list by iterating to the end. So, the Iterator should be finite to be
- [List], [Range], [Buffer](Buffer.md), [BitBuffer], [Buffer], [Set], [RingBuffer] - [List], [Range], [Buffer](Buffer.md), [BitBuffer], [Buffer], [Set], [RingBuffer]
[Collection]: Collection.md
[List]: List.md [List]: List.md
[Range]: Range.md [Range]: Range.md
[Set]: Set.md [Set]: Set.md
[RingBuffer]: RingBuffer.md [RingBuffer]: RingBuffer.md

View File

@ -92,44 +92,22 @@ Open end ranges remove head and tail elements:
assert( [1, 2, 3] !== [1, 2, 3]) assert( [1, 2, 3] !== [1, 2, 3])
>>> void >>> void
## In-place sort
List could be sorted in place, just like [Collection] provide sorted copies, in a very like way:
val l1 = [6,3,1,9]
l1.sort()
assertEquals( [1,3,6,9], l1)
l1.sortBy { -it }
assertEquals( [1,3,6,9].reversed(), l1)
l1.sort() // 1 3 6 9
l1.sortBy { it % 4 }
// 1,3,6,9 gives, mod 4:
// 1 3 2 1
// we hope we got it also stable:
assertEquals( [1,9,6,3], l1)
>>> void
## Members ## Members
| name | meaning | type | | name | meaning | type |
|-------------------------------|----------------------------------------------|-------------| |-------------------------------|---------------------------------------|-------------|
| `size` | current size | Int | | `size` | current size | Int |
| `add(elements...)` | add one or more elements to the end | Any | | `add(elements...)` | add one or more elements to the end | Any |
| `insertAt(index,elements...)` | insert elements at position | Int, Any | | `insertAt(index,elements...)` | insert elements at position | Int, Any |
| `removeAt(index)` | remove element at position | Int | | `removeAt(index)` | remove element at position | Int |
| `remove(from,toNonInclusive)` | remove range from (incl) to (nonincl) | Int, Int | | `remove(from,toNonInclusive)` | remove range from (incl) to (nonincl) | Int, Int |
| `remove(Range)` | remove range | Range | | `remove(Range)` | remove range | Range |
| `removeLast()` | remove last element | | | `removeLast()` | remove last element | |
| `removeLast(n)` | remove n last elements | Int | | `removeLast(n)` | remove n last elements | Int |
| `contains(element)` | check the element is in the list (1) | | | `contains(element)` | check the element is in the list (1) | |
| `[index]` | get or set element at index | Int | | `[index]` | get or set element at index | Int |
| `[Range]` | get slice of the array (copy) | Range | | `[Range]` | get slice of the array (copy) | Range |
| `+=` | append element(s) (2) | List or Obj | | `+=` | append element(s) | 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 |
(1) (1)
: optimized implementation that override `Array` one : optimized implementation that override `Array` one
@ -138,16 +116,7 @@ List could be sorted in place, just like [Collection] provide sorted copies, in
: `+=` append either a single element, or all elements if the List or other Iterable : `+=` append either a single element, or all elements if the List or other Iterable
instance is appended. If you want to append an Iterable object itself, use `add` instead. instance is appended. If you want to append an Iterable object itself, use `add` instead.
(3) It inherits from [Iterable] too.
: predicate is called on each element, and the returned values are used to sort in natural
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,
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 }
It inherits from [Iterable] too and thus all iterable methods are applicable to any list.
## Member inherited from Array ## Member inherited from Array
@ -165,5 +134,4 @@ It inherits from [Iterable] too and thus all iterable methods are applicable to
[Range]: Range.md [Range]: Range.md
[Iterable]: Iterable.md [Iterable]: Iterable.md

View File

@ -16,15 +16,9 @@ fun naiveCountHappyNumbers() {
count count
} }
import lyng.time
// //
// After all optimizations it takes ~120ms. // After all optimizations it takes ~120ms.
// //
//for( r in 1..100 ) { val found = naiveCountHappyNumbers()
// val start = Instant.now() println("Found happy numbers: "+found)
val found = naiveCountHappyNumbers() assert( found == 55252 )
// println("Found happy numbers: %d time %s"(found, Instant.now() - start))
assert( found == 55252 )
// delay(0.01)
//}

View File

@ -764,15 +764,15 @@ thrown.
Typical builtin types that are containers (e.g. support `contains`): Typical builtin types that are containers (e.g. support `contains`):
| class | notes | | class | notes |
|--------------|------------------------------------------------| |------------|------------------------------------------------|
| [Collection] | contains an element (1) | | Collection | contains an element (1) |
| Array | faster maybe that Collection's | | Array | faster maybe that Collection's |
| List | faster than Array's | | List | faster than Array's |
| String | character in string or substring in string (3) | | String | character in string or substring in string (3) |
| Range | object is included in the range (2) | | Range | object is included in the range (2) |
| Buffer | byte is in buffer | | Buffer | byte is in buffer |
| RingBuffer | object is in buffer | | RingBuffer | object is in buffer |
(1) (1)
: Iterable is not the container as it can be infinite : Iterable is not the container as it can be infinite
@ -1361,6 +1361,4 @@ See [math functions](math.md). Other general purpose functions are:
[parallelism]: parallelism.md [parallelism]: parallelism.md
[RingBuffer]: RingBuffer.md [RingBuffer]: RingBuffer.md
[Collection]: Collection.md

View File

@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych" group = "net.sergeych"
version = "0.8.15-SNAPSHOT" version = "0.8.14-SNAPSHOT"
buildscript { buildscript {
repositories { repositories {

View File

@ -225,7 +225,7 @@ class Compiler(
// very special case chained calls: call()<NL>.call2 {}.call3() // very special case chained calls: call()<NL>.call2 {}.call3()
Token.Type.NEWLINE -> { Token.Type.NEWLINE -> {
val saved = cc.savePos() val saved = cc.savePos()
if (cc.peekNextNonWhitespace().type == Token.Type.DOT) { if( cc.peekNextNonWhitespace().type == Token.Type.DOT) {
// chained call continue from it // chained call continue from it
continue continue
} else { } else {
@ -492,11 +492,10 @@ class Compiler(
// if it is an open end range, then the end of line could be here that we do not want // if it is an open end range, then the end of line could be here that we do not want
// to skip in parseExpression: // to skip in parseExpression:
val current = cc.current() val current = cc.current()
val right = val right = if( current.type == Token.Type.NEWLINE || current.type == Token.Type.SINLGE_LINE_COMMENT)
if (current.type == Token.Type.NEWLINE || current.type == Token.Type.SINLGE_LINE_COMMENT) null
null else
else parseExpression()
parseExpression()
operand = Accessor { operand = Accessor {
ObjRange( ObjRange(
left?.getter?.invoke(it)?.value ?: ObjNull, left?.getter?.invoke(it)?.value ?: ObjNull,
@ -1384,13 +1383,7 @@ class Compiler(
loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak) loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
} else { } else {
val size = runCatching { sourceObj.invokeInstanceMethod(forContext, "size").toInt() } val size = runCatching { sourceObj.invokeInstanceMethod(forContext, "size").toInt() }
.getOrElse { .getOrElse { throw ScriptError(tOp.pos, "object is not enumerable: no size", it) }
throw ScriptError(
tOp.pos,
"object is not enumerable: no size in $sourceObj",
it
)
}
var result: Obj = ObjVoid var result: Obj = ObjVoid
var breakCaught = false var breakCaught = false

View File

@ -18,8 +18,8 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
/** /**
* Collection is an iterator with `size` * Collection is an iterator with `size`]
*/ */
val ObjCollection = ObjClass("Collection", ObjIterable).apply { val ObjCollection = ObjClass("Collection", ObjIterable).apply {
}
}

View File

@ -119,6 +119,12 @@ val ObjIterable by lazy {
ObjList(result) ObjList(result)
} }
// addFn("drop" ) {
// var n = requireOnlyArg<ObjInt>().value.toInt()
// if( n < 0 ) raiseIllegalArgument("drop($n): should be positive")
// val it = callMethod<>()
// }
addFn("isEmpty") { addFn("isEmpty") {
ObjBool( ObjBool(
thisObj.invokeInstanceMethod(this, "iterator") thisObj.invokeInstanceMethod(this, "iterator")
@ -126,21 +132,5 @@ val ObjIterable by lazy {
.not() .not()
) )
} }
addFn("sortedWith") {
val list = thisObj.callMethod<ObjList>(this, "toList")
val comparator = requireOnlyArg<Statement>()
list.quicksort { a, b ->
comparator.call(this, a, b).toInt()
}
list
}
addFn("reversed") {
val list = thisObj.callMethod<ObjList>(this, "toList")
list.list.reverse()
list
}
} }
} }

View File

@ -18,7 +18,6 @@
package net.sergeych.lyng.obj package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.statement import net.sergeych.lyng.statement
import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonEncoder
@ -128,33 +127,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
return list.map { it.toKotlin(scope) } return list.map { it.toKotlin(scope) }
} }
suspend fun quicksort(compare: suspend (Obj, Obj) -> Int) = quicksort(compare, 0, list.size - 1)
suspend fun quicksort(compare: suspend (Obj, Obj) -> Int, left: Int, right: Int) {
if (left >= right) return
var i = left
var j = right
val pivot = list[left]
while (i < j) {
// Сдвигаем j влево, пока элемент меньше pivot
while (i < j && compare(list[j], pivot) >= 0) {
j--
}
// Сдвигаем i вправо, пока элемент больше pivot
while (i < j && compare(list[i], pivot) <= 0) {
i++
}
if (i < j) {
list.swap(i, j)
}
}
// После завершения i == j, ставим pivot на своё место
list.swap(left, i)
// Рекурсивно сортируем левую и правую части
quicksort(compare, left, i - 1)
quicksort(compare, i + 1, right)
}
override fun hashCode(): Int { override fun hashCode(): Int {
// check? // check?
return list.hashCode() return list.hashCode()
@ -263,23 +235,8 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
} }
self self
} }
addFn("sortWith") {
val comparator = requireOnlyArg<Statement>()
thisAs<ObjList>().quicksort { a, b -> comparator.call(this, a, b).toInt() }
ObjVoid
}
} }
} }
} }
// Расширение MutableList для удобного обмена элементами
fun <T>MutableList<T>.swap(i: Int, j: Int) {
if (i in indices && j in indices) {
val temp = this[i]
this[i] = this[j]
this[j] = temp
}
}

View File

@ -77,8 +77,9 @@ fun Iterable.dropLast(n) {
} }
fun Iterable.takeLast(n) { fun Iterable.takeLast(n) {
val list = this
val buffer = RingBuffer(n) val buffer = RingBuffer(n)
for( item in this ) buffer += item for( item in list ) buffer += item
buffer buffer
} }
@ -103,46 +104,10 @@ fun Iterable.all(predicate): Bool {
!any { !predicate(it) } !any { !predicate(it) }
} }
fun Iterable.sum() {
val i = iterator()
if( i.hasNext() ) {
var result = i.next()
while( i.hasNext() ) result += i.next()
result
}
else null
}
fun Iterable.sumOf(f) {
val i = iterator()
if( i.hasNext() ) {
var result = f(i.next())
while( i.hasNext() ) result += f(i.next())
result
}
else null
}
fun Iterable.sorted() {
sortedWith { a, b -> a <=> b }
}
fun Iterable.sortedBy(predicate) {
sortedWith { a, b -> predicate(a) <=> predicate(b) }
}
fun List.toString() { fun List.toString() {
"[" + joinToString(",") + "]" "[" + joinToString(",") + "]"
} }
fun List.sortBy(predicate) {
sortWith { a, b -> predicate(a) <=> predicate(b) }
}
fun List.sort() {
sortWith { a, b -> a <=> b }
}
class StackTraceEntry( class StackTraceEntry(
val sourceName: String, val sourceName: String,
val line: Int, val line: Int,
@ -160,7 +125,6 @@ fun Exception.printStackTrace() {
println("\tat "+entry) println("\tat "+entry)
} }
} }
""".trimIndent() """.trimIndent()

View File

@ -3095,12 +3095,10 @@ class ScriptTest {
@Test @Test
fun testOverridenListToString() = runTest { fun testOverridenListToString() = runTest {
eval( eval("""
"""
val x = [1,2,3] val x = [1,2,3]
assertEquals( "[1,2,3]", x.toString() ) assertEquals( "[1,2,3]", x.toString() )
""".trimIndent() """.trimIndent())
)
} }
@Test @Test
@ -3125,93 +3123,9 @@ class ScriptTest {
println(e.toString()) println(e.toString())
println("-------------------- dee") println("-------------------- dee")
println(decoded.toString()) println(decoded.toString())
assertEquals( e.toString(), decoded.toString() ) // assertEquals( e.toString(), decoded.toString() )
} }
""".trimIndent() """.trimIndent()
) )
} }
@Test
fun testThisInClosure() = runTest {
eval(
"""
fun Iterable.sum2by(f) {
var acc = null
for( x in this ) {
println(x)
println(f(x))
acc = acc?.let { acc + f(x) } ?: f(x)
}
}
class T(val coll, val factor) {
fun sum() {
// here we use ths::T and it must be available:
coll.sum2by { it * factor }
}
}
assertEquals(60, T([1,2,3], 10).sum())
""".trimIndent()
)
}
@Test
fun testThisInFlowClosure() = runTest {
eval(
"""
class T(val coll, val factor) {
fun seq() {
flow {
for( x in coll ) {
emit(x*factor)
}
}
}
}
assertEquals([10,20,30], T([1,2,3], 10).seq().toList())
""".trimIndent()
)
}
@Test
fun testSum() = runTest {
eval(
"""
assertEquals(1, [1].sum())
assertEquals(null, [].sum())
assertEquals(6, [1,2,3].sum())
assertEquals(30, [3].sumOf { it * 10 })
assertEquals(null, [].sumOf { it * 10 })
assertEquals(60, [1,2,3].sumOf { it * 10 })
""".trimIndent()
)
}
@Test
fun testSort() = runTest {
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())
assertEquals( [7,5,4,1], coll.sortedBy { -it })
assertEquals( [1,4,5,7], coll.sortedBy { -it }.reversed())
""".trimIndent()
)
}
@Test
fun testListSortInPlace() = runTest {
eval("""
val l1 = [6,3,1,9]
l1.sort()
assertEquals( [1,3,6,9], l1)
l1.sortBy { -it }
assertEquals( [1,3,6,9].reversed(), l1)
l1.sort()
l1.sortBy { it % 4 }
// 1,3,6,9
// 1 3 2 1
// we hope we got it also stable:
assertEquals( [1,9,6,3], l1)
""")
}
} }