ref #16 better processing of the last-block argument

+sample with national-characters vars
This commit is contained in:
Sergey Chernov 2025-06-11 10:54:06 +04:00
parent 194fc8aca6
commit 28b83f9892
5 changed files with 102 additions and 8 deletions

View File

@ -0,0 +1,37 @@
# Пример расчета суммы ряда
Рассмотрим как можно посчитать предел суммы ряда на lyng. Для наивной реализации
представим что у нас есть функция рассчитывающая n-й член ряда. Тогда мы можем
считать сумму до тех пор, пока отклонение при расчете следующего члена не станет
меньше чем заданная погрешность:
fun сумма_ряда(x, погрешность=0.0001, f) {
var сумма = 0
for( n in 1..100000) {
val следующая_сумма = сумма + f(x, n)
if( n > 1 && abs(следующая_сумма - сумма) < погрешность )
break следующая_сумма
сумма = следующая_сумма
}
else null
}
Для проверки можно посчитать на хорошо известном ряду Меркатора
$$ \ln(1+x)=x-{\dfrac {x^{2}}{2}}+{\dfrac {x^{3}}{3}}-\cdots =\sum \limits _{n=0}^{\infty }{\dfrac {(-1)^{n}x^{n+1}}{(n+1)}}=\sum \limits _{n=1}^{\infty }{\dfrac {(-1)^{n-1}x^{n}}{n}}
$$
Который в нашем случае для точки $x = 1$ можно записать так:
val x = сумма_ряда(1) { x, n ->
val sign = if( n % 2 == 1 ) 1 else -1
sign * pow(x, n) / n
}
Проверим:
assert( x - ln(2) < 0.001 )
Во многих случаях вычисление $n+1$ члена значительно проще cчитается от предыдущего члена, в нашем случае это можно было бы записать через итератор, что мы вскоре добавим.
(продолжение следует)

View File

@ -15,8 +15,7 @@ __Other documents to read__ maybe after this one:
- [OOP notes](OOP.md)
- [math in Lyng](math.md)
- Some class references: [List], [Real], [Range], [Iterable], [Iterator]
- Some samples: [combinatorics](samples/combinatorics.lyng.md)
See [samples folder](samples)
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
# Expressions

View File

@ -53,7 +53,11 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
val value = when {
i < callArgs.size -> callArgs[i]
a.defaultValue != null -> a.defaultValue.execute(context)
else -> context.raiseArgumentError("too few arguments for the call")
else -> {
println("callArgs: ${callArgs.joinToString()}")
println("tailBlockMode: ${arguments.tailBlockMode}")
context.raiseArgumentError("too few arguments for the call")
}
}
assign(a, value)
i++

View File

@ -140,7 +140,7 @@ class Compiler(
Token.Type.LPAREN -> {
cc.next()
// instance method call
val args = parseArgs(cc)
val args = parseArgs(cc).first
isCall = true
operand = Accessor { context ->
context.pos = next.pos
@ -552,7 +552,11 @@ class Compiler(
return result
}
private fun parseArgs(cc: CompilerContext): List<ParsedArgument> {
/**
* Parse arguments list during the call and detect last block argument
* _following the parenthesis_ call: `(1,2) { ... }`
*/
private fun parseArgs(cc: CompilerContext): Pair<List<ParsedArgument>, Boolean> {
val args = mutableListOf<ParsedArgument>()
do {
@ -578,6 +582,7 @@ class Compiler(
// block after?
val pos = cc.savePos()
val end = cc.next()
var lastBlockArgument = false
if (end.type == Token.Type.LBRACE) {
// last argument - callable
val callableAccessor = parseLambdaExpression(cc)
@ -588,14 +593,16 @@ class Compiler(
},
end.pos
)
lastBlockArgument = true
} else
cc.restorePos(pos)
return args
return args to lastBlockArgument
}
private fun parseFunctionCall(cc: CompilerContext, left: Accessor, blockArgument: Boolean): Accessor {
// insofar, functions always return lvalue
var detectedBlockArgument = blockArgument
val args = if (blockArgument) {
val blockArg = ParsedArgument(
parseExpression(cc)
@ -603,7 +610,9 @@ class Compiler(
)
listOf(blockArg)
} else {
parseArgs(cc)
val r = parseArgs(cc)
detectedBlockArgument = r.second
r.first
}
return Accessor { context ->
@ -611,7 +620,7 @@ class Compiler(
v.value.callOn(
context.copy(
context.pos,
args.toArguments(context, blockArgument)
args.toArguments(context, detectedBlockArgument)
// Arguments(
// args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
// ),

View File

@ -1640,4 +1640,49 @@ class ScriptTest {
""".trimIndent())
}
@Test
fun nationalCharsTest() = runTest {
eval("""
fun сумма_ряда(x, погрешность=0.0001, f) {
var сумма = 0
for( n in 1..100000) {
val следующая_сумма = сумма + f(x, n)
if( n > 1 && abs(следующая_сумма - сумма) < погрешность )
break следующая_сумма
сумма = следующая_сумма
}
else null
}
val x = сумма_ряда(1) { x, n ->
val sign = if( n % 2 == 1 ) 1 else -1
sign * pow(x, n) / n
}
assert( x - ln(2) < 0.001 )
""".trimIndent())
}
//
// @Test
// fun customIteratorTest() = runTest {
// eval("""
// fun сумма_ряда2(погрешность=0.0001, iterator) {
// var сумма = 0
// for( n in 1..100000) {
// if( !iterator.hasNext() ) break сумма
// val следующая_сумма = сумма + iterator.next()
// if( n > 1 && abs(следующая_сумма - сумма) < погрешность )
// break следующая_сумма
// сумма = следующая_сумма
// }
// else null
// }
// val
// val x = сумма_ряда2(1) { x, n ->
// val sign = if( n % 2 == 1 ) 1 else -1
// sign * pow(x, n) / n
// }
// assert( x - ln(2) < 0.001 )
// """.trimIndent())
// }
}