Again, the basic execution strategy of languages like Step makes it easy to ask questions about whether some execution path exists. We can ask questions about all possible paths too, but we have to use higher-order tasks to do that.
Every/Implies
We talked about Every earlier. The predicate:
[Every ?p ?q]
is so named because, if it's true, then it means ?q is true in all situations where ?p is true. Or to put it another way, there are no situations where ?p is true (i.e. no solutions to ?p) where ?q is false (where ?q fails). In fact, if it weren't built in, we could define Every ourselves:
Every ?p ?q: [Not [And ?p [Not ?q]]]
Every is also called Implies, because there are some situations where that's a more natural reading of it's meaning. Both variables are set to the same predicate; you can use whichever name best captures the intent of your code.
Under the hood, this is going to work by finding all solutions to ?p and then for each one, testing if ?q is true. For example, if we use our food preferences example:
[predicate]
Likes tanya sushi.
Likes tanya burgers.
Likes tanya mexican.
Likes jayden burgers.
Likes jayden ethiopean.
# Kimiko likes everything
Likes kimiko ?.
# Everyone likes pizza.
Likes ? pizza.
we might ask "does everyone who likes burgers like pizza?" That would be:
[Every [Likes ?who burgers] [Likes ?who pizza]]
You can try it out:
# Try: [Every [Likes ?who burgers] [Likes ?who pizza]]
[predicate]
Likes tanya sushi.
Likes tanya burgers.
Likes tanya mexican.
Likes jayden burgers.
Likes jayden ethiopean.
# Kimiko likes everything
Likes kimiko ?.
# Everyone likes pizza.
Likes ? pizza.
You get the answer “yes”, but it doesn't have a binding for the variable ?who. That makes sense because we weren't asking for a specific person who liked burgers and pizza, we were asking if everyone who liked burgers liked pizza. So there's no one ?who to report back.
Why is there no Any task?
Since we can say [Every [Likes ?who burgers] [Likes ?who pizza]] to ask if everyone who likes burgers also likes pizza, we might think there would we could write [Any [Likes ?who burgers] [Likes ?who pizza]] to ask if anyone who likes burgers also likes pizza. But we don't need a separate Any task; if we just run them in sequence:
[Likes ?who burgers] [Likes ?who pizza]
that has the same effect: it will find a ?who who likes both burgers and pizza.
ForEach
Every/Implies is useful when calling predicates. But what if we wanted to ask it to print everyone who liked burgers? You could write it this way:
# Try: [WhoEats burgers]
WhoEats ?x: [Implies [Likes ?who ?x] [Write ?who]]
[predicate]
Likes tanya sushi.
Likes tanya burgers.
Likes tanya mexican.
Likes jayden burgers.
Likes jayden ethiopean.
# Kimiko likes everything
Likes kimiko ?.
# Everyone likes pizza.
Likes ? pizza.
But it reads a little strangely to say "implies Write." Here it reads a little better to use ForEach:
# Try: [WhoEats burgers]
WhoEats ?x: [ForEach [Likes ?who ?x] [Write ?who]]
[predicate]
Likes tanya sushi.
Likes tanya burgers.
Likes tanya mexican.
Likes jayden burgers.
Likes jayden ethiopean.
# Kimiko likes everything
Likes kimiko ?.
# Everyone likes pizza.
Likes ? pizza.
For these purposes, ForEach is identical to Every/Implies, it just has a more useful title. However, there is one difference, which is that [Implies ?p ?q] will fail if ?q ever fails when Implies calls it. ForEach actually ignores whether q fails or not.
State updates in loops
[ForEach ?p ?q] and [Every ?p ?q] will collect all the output generated by the different calls to ?q. It will also accumulate any changes to global variables or fluents that ?q makes. However, it will ignore any output or changes made by ?p.
Niche loops (ignore this if you aren't a power-user)
DoAll
[Every ?p ?q] and [ForEach ?p ?q] consider all the possible execution paths of ?p but for each of those paths, only finds one solution of ?q. DoAll takes a series of calls, and finds all the solutions to the sequence and gives you all their outputs:
[DoAll ?call1 ?call2 ...]
Note that this only accumulates output; it ignores all state changes.
AccumulateOutputWithSeparators
This is mostly used for printing comma-separated lists of things
[AccumulateOutputWithSeparators ?generator ?printer ?separator ?finalSeparator]
This is mostly like saying [ForEach ?generator ?printer] except rather than just printing the outputs of ?printer, it separates them by ?separator, putting ?finalSeparator before the end. If we use it for our WhoEats example:
# Try: [WhoEats burgers]
WhoEats ?x: [AccumulateOutputWithSeparators [Likes ?who ?x] [WriteCapitalized ?who] "," "and"]
[predicate]
Likes tanya sushi.
Likes tanya burgers.
Likes tanya mexican.
Likes jayden burgers.
Likes jayden ethiopean.
# Kimiko likes everything
Likes kimiko ?.
# Everyone likes pizza.
Likes ? pizza.
we get “Tanya, Jayden and Kimiko”, which looks a little nicer than if we just had spaced between them.
|
Previous:
Negation is (necessary) evil
|
Next:
All-solutions predicates
|