Compare commits

...

2 Commits

Author SHA1 Message Date
87060c7eb7 +inference bugs
+List.ensureCapacity
optimized List.fill
2026-04-20 13:56:47 +03:00
6ba128f7ba fixed some more inference bugs 2026-04-20 12:23:17 +03:00
5 changed files with 64 additions and 12 deletions

View File

@ -175,6 +175,8 @@ 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 |
| `List.fill(size, block)` | build a new list from indices `0..<size` | Int, Callable |
| `List.fill(size,capacity,block)` | same, pre-allocating capacity slots | Int, Int, Callable |
| `ensureCapacity(count)` | pre-allocate storage for at least `count` elements without reallocation (5) | Int |
| `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 |
@ -197,6 +199,11 @@ order, e.g. is same as `list.sortWith { a,b -> predicate(a) <=> predicate(b) }`
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 }
(5)
: if the current capacity is already ≥ `count`, this is a no-op. Otherwise the internal storage
is reallocated to hold at least `count` elements. Use this before a bulk `+=` loop to avoid
repeated reallocations. `List.fill(size, capacity, block)` calls this automatically.
It inherits from [Iterable] too and thus all iterable methods are applicable to any list.
## Observable list hooks

View File

@ -2096,12 +2096,19 @@ class Compiler(
plan.captureOwners[name] = slotLoc
plan.captures += capture
if (!plan.slotPlan.slots.containsKey(name)) {
val newSlot = plan.slotPlan.nextIndex
plan.slotPlan.slots[name] = SlotEntry(
plan.slotPlan.nextIndex,
newSlot,
isMutable = slotLoc.isMutable,
isDelegated = slotLoc.isDelegated
)
plan.slotPlan.nextIndex += 1
slotTypeByScopeId[slotLoc.scopeId]?.get(slotLoc.slot)?.let { cls ->
slotTypeByScopeId.getOrPut(plan.slotPlan.id) { mutableMapOf() }[newSlot] = cls
}
slotTypeDeclByScopeId[slotLoc.scopeId]?.get(slotLoc.slot)?.let { decl ->
slotTypeDeclByScopeId.getOrPut(plan.slotPlan.id) { mutableMapOf() }[newSlot] = decl
}
}
}
@ -3417,6 +3424,10 @@ class Compiler(
index < expectedParams.size
) {
expectedParams[index]
} else if (param.type == TypeDecl.TypeAny && index == 0 && argsDeclaration.params.size == 1 &&
implicitItType != null
) {
implicitItType
} else {
param.type
}
@ -10346,6 +10357,9 @@ class Compiler(
if (initClass != null) {
classFieldTypesByName.getOrPut(declaringClassNameCaptured) { mutableMapOf() }[name] = initClass
}
if (!isDelegate && varTypeDecl is TypeDecl.Generic) {
classMemberTypeDeclByName.getOrPut(declaringClassNameCaptured) { mutableMapOf() }[name] = varTypeDecl
}
}
// Emit MiniValDecl for this declaration (before execution wiring), attach doc if any

View File

@ -528,6 +528,22 @@ open class ObjList(initialList: MutableList<Obj> = mutableListOf()) : Obj() {
} else self.list.removeLast()
self
}
addFnDoc(
name= "ensureCapacity",
doc = """
ensure the list capacity allows storing specified amount if items without reallocation.
If current capacity is greater or equal to `count`, does nothing. Note that possible reallocation
could be a costly operation,
""".trimIndent(),
params = listOf(ParamDoc("count", type("lyng.Int"))),
moduleName = "lyng.stdlib"
) {
val self = thisAs<ObjList>()
val list = self.list as ArrayList
val count = requireOnlyArg<ObjInt>().value.toInt()
list.ensureCapacity(count)
self
}
addFnDoc(
name = "removeRange",

View File

@ -436,4 +436,12 @@ class StdlibTest {
assertEquals(2, x)
""".trimIndent())
}
@Test
fun testListFill() = runTest {
eval("""
val x = List.fill(5) { i -> i * 10 }
assertEquals([0, 10, 20, 30, 40], x)
""".trimIndent())
}
}

View File

@ -432,11 +432,19 @@ fun List<T>.sort(): void {
sortWith { a, b -> a <=> b }
}
/* Build a new list of `size` elements by calling `block(index)` for each index. */
static fun List<T>.fill(size: Int, block: (Int)->T): List<T> {
val result = List<T>()
for( i in 0..<size ) result += block(i)
result
/*
Build a new list of `size` elements by calling `block(index)` for each index.
`capacity` less size is ignored (size will be used as capacity).
*/
static fun List<T>.fill(size: Int, capacity = -1, block: (Int)->T): List<T> {
require(size >= 0, "size must not be negative")
val capacity = capacity < size ? size : capacity
List<T>().also {
if( size > 0 ) {
it.ensureCapacity(capacity)
for( i in 0..<size ) it += block(i)
}
}
}
/* Print this exception and its stack trace to standard output. */
@ -545,8 +553,7 @@ class StackTraceEntry(
}
// Private helper: starts one LaunchPool worker coroutine for the given queue.
// Defined outside LaunchPool so the global `launch` is not shadowed by the method.
private fun _launchPoolWorker(q) {
val ch = q as Channel
private fun _launchPoolWorker(ch: Channel): Deferred {
launch {
var task = ch.receive()
while (task != null) {
@ -588,7 +595,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
mu.withLock {
if (isClosed) throw IllegalStateException("LaunchPool is closed")
}
val d = CompletableDeferred()
val d = CompletableDeferred()
val wrapper = {
try { d.complete(lambda()) }
catch(e) { d.completeExceptionally(e) }
@ -618,7 +625,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
*/
fun cancel() {
closeQueue()
workers.forEach { (it as Deferred).cancel() }
workers.forEach { it.cancel() }
}
/*
@ -628,7 +635,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
*/
fun cancelAndJoin() {
closeQueue()
workers.forEach { (it as Deferred).cancel() }
workers.forEach { it.cancel() }
}
/*
@ -639,7 +646,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
fun closeAndJoin() {
closeQueue()
for (w in workers) {
(w as Deferred).await()
w.await()
}
}
}