In our previous tutorials, we walked you through the [set] keyword, and pf.lambda expressions. In this tutorial, we will be diving further into expressions, in addition to taking a look at the [append] keyword.
The [append] statement in pf.lambda, allows you to append a list of children nodes, into an existing node. Consider the following code;
_destination append:@/../*/_destination/?node source foo1:bar1 foo2:bar2
If you execute the above code in your pf.lambda executor, you will see that it does exactly what it says it does. It appends all nodes from your source, into your destination. Notice how this semantically differs from the [set] statement, since the [append] keyword actually «creates» new nodes in your pf.lambda execution tree, where the [set] keyword, for the most parts, can only modify existing nodes. [append] can also take expressions as a source. Consider the following code;
_source foo1:bar1 foo2:bar2 _destination append:@/../*/_destination/?node source:@/../*/_source/*/?node
In the above code, [append] will append all nodes from its source expression, into its destination. [append] and [set] are similar in those regards, since they can take more or less the same type of parameters, and use their parameters in roughly the same way. In addition, you can also use a «relative source» with append, the same way you can with [set]. You can also use [append] with its source being a string value, or a node value. Consider the following code;
_destination append:@/-/?node source:node:"foo1:bar1" append:@/-2/?node source:@"foo2:bar2 foo3:bar3"
However, most of the times, you will use expressions, as both source, and destination. Which might make you interested in exactly what you can do with pf.lambda expressions.
So far, we have in fact only scratched the surface of pf.lambda expressions. However, if you can conceptualize a node set you wish to retrieve in your head, you can probably compose an expression, capable of retrieving that node set for you. pf.lambda expressions, are in fact, capable of using hyper-dimensional boolean algebraic operations, to retrieve a specific node set. That might seem like a mouthful, until you realize that’s exactly what it is. Consider the following code;
_source success:Howdy error:oops....! success:World _destination append:@/-/?node source:@/../*/_source/*/(!/error/)?node
To translate the above expression into English, would become something similar to this; «Retrieve the root node of my execution tree, then get all of the root node’s children node, then filter away all nodes who’s names are not ‘_source’, for then to retrieve all of its nodes children. Then create a grouping iterator, that filters away all nodes from its parent group, who’s names are ‘error’». The parentheses creates a «grouping iterator», while the exclamation mark is the boolean NOT operator.
All four basic boolean algebraic operators are supported in pf.lambda expressions. The NOT operator is declared using an exclamation mark (!), the OR operator is declared with a pipe-sign (|), AND declared using an ampersand (&), and XOR using the hat character (^).
Using this technique, you can OR together the results of two expressions, AND expressions, NOT expressions, and XOR expressions. This allows you to create really rich expressions, that substitutes several conventional functions, and methods, with multiple loops and inner loops, from traditional programming. Imagine you have a hierarchical tree structure of «mammals» for instance, and you wish to find all mammals with an IQ, close to that of humans, but not including humans.
_mammal sea dolphins smart salmon killer-whales smart land ape smart dogs humans smart donkey _result append:@/-/?node source:@/../*/_mammal/*/(/**/smart/./!/land/*/humans/)?node
The above expression basically says; «Give me all nodes beneath the root node of my tree, then filter away all nodes who’s names are not ‘_mammal’, for then to give me all of those nodes children. Then create a group, where you return all nodes from my previous result, flattening the hierarchy, for then to filter away all nodes who’s names are not ‘smart’. Then find the parent node to those nodes, and exclude all nodes from that result set, that can also be found inside of the ‘land, humans’ of this group’s parent group». Net effect, you retrieve all «smart» animals, except humans.
Notice how the «double asterix» iterator above, will fetch all nodes from the result of its previous iterator, flattening the hierarchy, into a «non-relational list of result nodes». It basically retrieves all nodes from where it’s at, including all of its children, its childrens’ children, and its childrens’ childrens’ children, and so on. When we after the double asterix iterator, filter on «smart», then we basically have retrieved all nodes who’s names are «smart», from inside «_mammal’s children». The «.» iterator afterwards, retrieves the «parent node», meaning at this point, we have fetched all «parent nodes to all nodes who’s names are ‘smart’» from within our «_mammal» node.
After the exclamation mark, which is our NOT operator, we say we wish to retrieve all nodes from within our outer group, who can also be found within the «humans node», inside of our «land» node. And since we started our sub-expression with a NOT operator, anything matching this result set, is removed from the previous result set, before the group is closed, and we return all nodes, because our expression has «node» as its type declaration.
If you were to use conventional programming constructs to retrieve the same nodes, in for instance C#, this would require multiple levels of recursion, several loops and inner loops, and temporary variables. With pf.lambda, it’s a simple expression, dynamic in nature.
In this exercise, I want you to change the source expression in the above code, such that it appends only the nodes of animals who are NOT «smart» into the [_result] node, and I want you to do so using at least one «grouping» iterator, in addition to at least one «boolean algebraic operator». If you can solve it in multiple different ways, bonus points to you.
At the bottom of this blog, is an example of how to solve the above exercise
As I promised, here’s a list of some of the common iterators you can use in pf.lambda
- /../ – retrieves the root node of your tree
- /./ – retrieves the parent nodes of the previous result
- /..xxx/ – retrieves the first ancestor node with the given ‘xxx’ name of the previous result
- /*/ – retrieves all children of nodes from previous result
- /**/ – retrieves all nodes, and descendants of nodes, from previous result
- /xxx/ – retrieves all nodes from previous result who’s names is ‘xxx’
- /=xxx/ – retrieves all nodes from previous result, who’s value is ‘xxx’
- /-n/ – retrieves the sibling which is ‘n’ generations younger. Meaning if ‘n’ is ‘2’, it will return the node two positions further up at the same level. You can use ‘+’ to retrieve ‘older siblings’. ‘n’ is optional, and if not given, defaults to ‘1’
- /n/ – (‘n’ is a number) retrieves the n’th children node from previous result
- /[n1,n2] – (n1, n2 are numbers) retrieves children from previous results, starting from ‘n1’ going to ‘n2’. Basically retrieves a “range of children”
- /”/regex/”/ – retrieves the nodes having a name matching the given regular expression
- /=”/regex/”/ – retrieves the nodes having a value matching the given regular expression
- /%n/ – (n is a number) retrieves all children nodes, matching the modulo of ‘n’. For instance, if ‘n’ is ‘2’, then it will retrieve all even children of the previous result
- /#/ – convert the value of the previous result set into a node, and return that node. Also known as the “reference iterator”
- /</ – “left shift” previous results, meaning return all results based upon previous result that are “one position earlier in the tree than the current result”
- />/ – “right shift” previous results, opposite of above
One example solution to the above exercise
_mammal sea dolphins smart salmon killer-whales smart land ape smart dogs humans smart donkey _result append:@/-/?node source:@/../*/_mammal/*/(/*/!/**/smart/./)?node
Thank you for reading, have a nice day 🙂