ATTENTION

This FlexSim Community Forum is read-only. Please post any new questions, ideas, or discussions to our new community (we call it Answers) at https://answers.flexsim.com/. Our new Question & Answer site brings a modern, mobile-friendly interface and more focus on getting answers quickly. There are a few differences between how our new Q&A community works vs. a classic, threaded-conversation-style forum like the one below, so be sure to read our Answers Best Practices.


flexsim.com

Go Back   FlexSim Community Forum > FlexSim Software > User Development
Downloads

User Development User developed add-ons to Flexsim

  #1  
Old 07-22-2013
Allister Wilson Allister Wilson is offline
Flexsim User
 
Join Date: Jul 2009
Posts: 17
Downloads: 8
Uploads: 0
Thanks: 22
Thanked 30 Times in 12 Posts
Rep Power: 151
Allister Wilson will become famous soon enoughAllister Wilson will become famous soon enough
Lightbulb Lambda functions in FlexScript

Hello everyone,

I thought this was probably worth sharing, it's an implementation of lambdas / anonymous functions / closures in FlexScript.

I've documented the various commands to the best of my abilities.
Included is a very simple demo model which uses a few lambdas.
I hope some of you will have a use for this, it's definitely going to make my life easier!

There's an explanation of what this is and how it works in the Lambda library node.
Instead of writing more, I'll just paste it here...


So what are lambdas?

A lambda function (also known as anonymous function) is according to Wikipedia,
"a function (or a subroutine) defined, and possibly called, without being bound to an identifier".


What are they for?

Have you ever found yourself writing the same code again and again with only minor differences?
For instance, finding the row in a table, the item in a container or the task sequence in a dispatcher,
that fulfills some given condition.
You often end up with several duplicates of prettty much the same code with only very minor differences.

Many of you have probably written a function which accepts a function-node callback at some point, I know I have...
But that solution brings along its own set of problems. How do you access data that your callback might need?
Do you put it in the tree and let the callback read it? Do you use some sort of argument forwarding system?

This library attemps to solve all of these issues by allowing the use of anonymous functions, defined in code.


How do they work?

When you call a lambda-compatible function, you give it the lambda source code as a string.
The function will then compile your lambda into a temporary function node and call it with whatever parameters
it defines.
Your lambda may be expected to return a value, or simply do something with the provided parameters.
These lambdas are also closures (http://en.wikipedia.org/wiki/Closure...ter_science%29),
they can capture part of their enclosing scope. This means that your lambda can use (read-only)
variables defined in the code which defines the lambda, by defining 'captures'.


What exactly are captures?

Captures are values which are compiled into a lambda.
For example, if you define a node capture called "myNode", your lambda will have access to the variable "myNode",
which will have the value you assigned to it when defining the capture.
The 3 data types allowed as captures are double, string and treenode.
You can define as many captures as you like, as long as they all have unique names.
Captures must be defined before a lambda function is created. Creating a lambda will use then destroy all
previously defined captures.


How do I use lambdas in my model?

To use the lambda library, all you need to do is copy "Tools/ModelLibraries/Lambda" into the ModelLibraries node
of your model.
You can also do the same with the lambda utilities library if desired.


How do I write my own lambda-compatible functions?

There are 3 things a lambda-compatible function needs to do:
- create and compile the lambda with the relevant argument list (lmbCreate)
- call the lambda one or more times with the relevant arguments (lmbCall)
- destroy the lambda function node (lmbDestroy)

I strongly suggest you take a look at some of the functions provided in the "Lambda utilities" model library.
A good place to start might be 'lmbOnTableCol'.
Attached Files
File Type: fsm lambda_functions.fsm (55.0 KB, 97 views)
The Following 4 Users Say Thank You to Allister Wilson For This Useful Post:
RalfGruber (07-25-2013)
  #2  
Old 07-23-2013
Anthony Johnson's Avatar
Anthony Johnson Anthony Johnson is offline
Manager of Product Development
 
Join Date: Jul 2007
Posts: 440
Downloads: 86
Uploads: 4
Thanks: 171
Thanked 899 Times in 288 Posts
Rep Power: 735
Anthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond reputeAnthony Johnson has a reputation beyond repute
Default

Allister,
For version 7 we've added several features directly to flexscript that incorporate lambda-esque constructs. These are implemented as several commands, as follows:
findmatch(nr, matchRequirement [, returnVal = count, reverseOrder = 0])

findmatch will traverse a defined number of iterations and evaluate matchRequirement for each iteration. As soon as it finds a match it will return the index of the match found (or a custom return value if you define the returnVal parameter). Within the matchRequirement (and within the returnVal parameter) you can use "count" to get the iteration you are on (1-based).

Examples:
int rowNr = findmatch(gettablerows("MyTable"), gettablenum("MyTable", count, 3) == 7);
int outPortNr = findmatch(nrop(current), content(outobject(current, count)) == 0);
int taskSeqRank = findmatch(content(gettasksequencequeue(current)), gettasktype(gettasksequence(current, count), 1) == TASKTYPE_TRAVEL);

findmatchintree(topNode, matchRequirement [, returnVal = a])

findmatchintree will search the sub-tree of a topNode finding a match based on the match expression. Within matchRequirement (and returnVal) you can use "a" to reference the current node being tested.

Example:
treenode myItem = findmatchintree(model(), getname(a) == "FlowItem805");

findmin(nr, valueExpr [, returnVal = minFound, criterion = true, reverseOrder = false)

findmin will traverse the defined number of iterations and return the minimum value found. Like in findmatch() you can use count to get the iteration number in the valueExpr, returnVal and criterion expressions.

Example:
double minCol3 = findmin(gettablerows("MyTable"), gettablenum("MyTable", count, 3));

findmax(nr, valueExpr [, returnVal = minFound, criterion = true, reverseOrder = false)

findmax is just like findmin except it returns the max value found.

getdatastat(stat, nrSamples, valueExpr)

getdatastat will evaluate a statistical attribute of a set of samples. You pass as the stat parameter the type of stat you want, one of: STAT_MEAN, STAT_MIN, STAT_MAX, STAT_VARIANCE, STAT_STD_DEV, STAT_SUM, STAT_CONF_INTERVAL_HALF_WIDTH. Then you provide the number of samples and the value expression for each sample. Again you use count for the iteration number.

Examples:
double col6Mean = getdatastat(STAT_MEAN, gettablerows("MyTable"), gettablenum("MyTable", count, 6));
double totalInput = getdatastat(STAT_SUM, nrop(current), getinput(outobject(current, count)))

This probably doesn't completely cover the full set of capabilities that your lambda expressions cover, but hopefully it will cover a large swath. Also the version 7 engine will include a quasi-sql parser/evaluator under the hood. We're still exploring the possibilities here, but the initial thoughts are that, in addition to doing standard sql-style queries on tables, it could be used for incredibly extensible filtering and prioritizing of queues such as task sequence queues, as well as any other instance where you are picking from a set of shared resources or waiting objects.
The Following 7 Users Say Thank You to Anthony Johnson For This Useful Post:
Steven Hamoen (07-23-2013)
  #3  
Old 07-25-2013
Allister Wilson Allister Wilson is offline
Flexsim User
 
Join Date: Jul 2009
Posts: 17
Downloads: 8
Uploads: 0
Thanks: 22
Thanked 30 Times in 12 Posts
Rep Power: 151
Allister Wilson will become famous soon enoughAllister Wilson will become famous soon enough
Default

Thanks for the clarifications on those functions Anthony.

I'd seen them and will definitely be using them in some cases.
I figured it was still worth writing the lambda library though, mainly for situations where you want to pass a callback into some model-specific function, happens to me quite often.

The SQL integration sounds extremely interesting, can't wait to try that out!


Thread Thread Starter Forum Replies Last Post
Questions about new drawsurrogate functions Steven Hamoen Q&A 3 07-05-2010 11:34 AM
How to create probability functions in patient tracks? Lionel Lim FlexSim HC: Q&A 9 11-20-2009 03:29 PM
about flexscript Kang Han Q&A 2 10-03-2008 09:21 AM
While functions Xavier Jackson Q&A 12 08-12-2008 01:32 PM
BasicFR Advanced Functions Cliff King Q&A 0 11-13-2007 08:02 PM


All times are GMT -6.
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.
Copyright 1993-2018 FlexSim Software Products, Inc.