Comments (6)
Syntax for lambda/closure is not supported but there is a workaround as one discussion thread shows: #436 it should be able to do what you want.
from evalex.
What about creating a LAMBDA function (similar to MS Excel)?
Something like FILTER(data_mapping_model, LAMBDA(item, item.map_type == '3'))
(not implemented).
from evalex.
@oswaldobapvicjr How would we know that the LAMBDA function first parameter has to be set to the actual array value?
The LAMBDA function does not know about its surrounding FILTER function?
from evalex.
Maybe it's not so easy (or even possible). But one first idea would be to accept the LAMBDA
function as a lazy parameter inside the FILTER
function.
The FILTER
function would be responsible for iterating through the array and preparing the calls to the LAMBDA
function on each iteration, assigning the current element as the actual value of the first token at the LAMBDA
function. This way, the LAMBDA
function can be agnostic about the surrounding operation.
The idea is to have the LAMBDA
function re-usable by other functions such as FILTER
, FIND_FIRST
, MAP
, REDUCE
, etc.
from evalex.
@oswaldobapvicjr then what's the difference between using LAMBDA
and the workaround in #436 ? Basically map / filter function like MAP(products, quantity, total * quantity)
can detect the variable name quantity
and then iterate through the array to prepare the necessary variables for the last lazy expression without the need for a new LAMBDA
function.
I didn't propose to add this as a new function to the repository because after running through the array, the variable list will have some kind of 'stray' value. In the case of MAP(products, quantity, total * quantity)
, there will be a new value called quantity
which equals to the last element in the array products
after evaluation. And I don't see any easy way to overcome this if we are to introduce LAMBDA
. But if we all agree this 'stray' value is not an issue, then we can add those functions.
from evalex.
In order to implement those high-order functions without leaving any 'stray' variables after evaluation (in my opinion, 'stray' variables are not only inconvenience but could potentially overwrite other variables if users are not careful), I think we can consider using a temporary map / dataAccessor when the function is iterating through the array performing evaluations one by one.
A modified map function may look like follows (I use map as an example but filter is essentially the same):
@FunctionParameter(name = "array")
@FunctionParameter(name = "placeholder", isLazy = true)
@FunctionParameter(name = "mapper", isLazy = true)
public class MapFunction extends AbstractFunction {
@Override
public EvaluationValue evaluate(
Expression expression, Token functionToken, EvaluationValue... parameterValues)
throws EvaluationException {
List<EvaluationValue> array = parameterValues[0].getArrayValue();
String placeHolder = parameterValues[1].getExpressionNode().getToken().getValue();
ASTNode mapper = parameterValues[2].getExpressionNode();
List<EvaluationValue> mapped = new ArrayList<>();
DataAccessorIfc tmp = expression.getConfiguration().getDataAccessorSupplier().get(); // get a tmp dataAccessor
for (EvaluationValue value : array) {
tmp.setValue(placeHolder, value);
mapped.add(expression.evaluateSubtree(mapper, tmp));
}
return EvaluationValue.arrayValue(mapped);
}
}
Here we need to add a new method for Expression: EvaluationValue evaluateSubtree(ASTNode startNode, DataAccessorIfc variables)
. This new API will pass in the variables
to the subsequent call to an updated getVariableOrConstant(Token token, DataAccessorIfc variables)
where the variable resolution will have the following precedence:
- Resolve from the
variable
parameter (new) - Resolve from
this.constants
(existing) - Resolve from
this.dataAccessor
(existing)
There are some other concerns though:
- Depending on the implementation (user may override this),
dataAccessorSupplier
may returns the same underlyingDataAccessorIfc
(i.e. user is reusing the same variable set for multiple expressions) where they will still see those stray variables in their variable table. Or worse, if they are not careful when using those map / filter functions and defined a placeholder variable name which collides with another one, then the other variable will be overwritten. - The new
evaluateSubtree
will need to be public and may make the API more complex and less clean. And also from an API consistency's perspective, if we allow user to optionally pass in aDataAccessorIfc
duringevaluateSubtree
, it would make more sense that they should be allowed to do the same forevaluate
method. - Once we have such APIs where user can pass in an 'extra variable table', things may become even more complex when the user wants to do something like the following:
var expression = new Expression("MAP(products, quantity, total * quantity)"); var dataAccessor = expression.getConfiguration().getDataAccessorSupplier().get(); // create dataAccessor on the fly dataAccessor.setValue("product", List.of(1, 2, 3)); expression.evaluate(dataAccessor); // Oops, "product" is not set in this.dataAccessor. Perhaps dataAccessor should go all the way into the AbstractFunction.evaluate()?
from evalex.
Related Issues (20)
- Support arrays as function parameters HOT 7
- Equality depends on input type in 3.1.0
- problem with placeholder HOT 1
- Add module-info (or at least Automatic-Module-Name) HOT 2
- SQRT function doesn't work HOT 3
- With introduction of null, it's better to allow short circuiting (lazy evaluation) for those boolean operators HOT 4
- Equals/NotEquals operator is now sometimes showing inconsistent behavior HOT 1
- Broken boolean expression evaluation in version 3.1.0
- Allow Java arrays as input parameters
- Restore compatibility with Java 8 HOT 2
- Determine evaluation result type without evaluating HOT 2
- Java exception generated when trying to calculate expressions (it worked before with the original EvalEX library in the previous GitHub) HOT 2
- Add a configurable locale to the configuration
- New Player Guidebook Naming rules HOT 1
- Incorrect behavior of ‘==’ operator in IF() expression for Array typed variable
- Management of the exception IndexOutOfBoundsException for Array variables
- Definition domain of the ACOS function
- does not evaluate if expression operand has a period, hyphen seperater HOT 2
- `SQRT` Javadoc issue.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from evalex.