How to pass State (and change it when I need) around while parsing text !?
https://www.metalevel.at/prolog/dcg
the example is doing counting..
Don't know how I'm supposed to pass the initial State. Do I have to do it as call-parameter or as a first element in the list ? Whenever I want to use the State, do I have to include state(S) as a first goal ?
======
Let say i have to parse the string "add item 5".
If the state is "list" , let say it should print "5=>list"
if "set" then "5=>set"
or may be more complex string "new list. add 5" ... where parsing "new list" should set the State=list, so that parsing the next part is aware of the context and the interpretation is "add 5 to a list" vs "add 5 to a set".
No need to use those examples, I'm just trying to figure when to use semi-context notation and how to pass the context as a parameter in the first/top rule.
As you can see I'm confused, but this was the only example on the internet I could find and prolog docs as always are dense, terse and not very helpful ;( with no examples.
To clarify :
sent([X,Y],Ctx) --> make(X,Ctx), add(Y,Ctx).
make(V,Ctx) --> [make,V], {say(">make ~w |ctx: ~w", [V,Ctx])}.
add(V,_Ctx) --> [add,V], {say(">add ~w", [V])}.
In this example I'm passing context at every level, even that I dont use it in add(). I can remove it for add(), but if there is subrule of I have to keep it.
I understood that using State threading need declaring Ctx only in the top rule and whenever it is used
Maybe this example helps. I will leave the semicontext trick out for now, as a discussion for later.
We want to count the occurences of
a
andb
andc
present in an atom. Any other characters are lumped into adropped
category (but also counted).The "state" is thus the current counts for
a
,b
,c
,dropped
. The state shall be implement using a map, in this case an SWI-Prolog dict.Three ways of doing that:
foldl/4
phrase/3
With the code below:
Code:
With
plunit
tests:A single state variable in DCGs
In the above, the DCGs take an "input state" at argument position 1 which they develop into an "output state" which is eventually returned at argument position 2.
This is completely analogous to functional programming whereby immutable structures are passed into functions and returned by functions. Here, the structures are "related" by the predicate, which is another way of looking at things (a way which sometimes bumps against realities of a machine that necessarily computes forward in time).
Note that above, the state variables are ground - no variables.
A single state variable is sufficient if that state variable is the name of an "empty cell" at the leaf of a larger structure. The larger structure "grows at its leaves", a DCG fills in the empty cell, but leaves another empty cell for the next call.
For example, here is a DCG which grows an "open list" at its end
Fin
.Fin
is always an unbound variable that shall be filled in by the rule:Replace
tag
by:
in an atom:The DCG code is traditionally written more compactly (this way also makes it amenable to tail-call optimization, which is not be disregarded):
In this case, every DCG call only see the empty cell at the far end of the open list, whereas
Collectecd
denotes its start:So, when encountering
y
the rulejust does its part on its
Fin
and grows the list by 1 listcell, but leaving it an "open list" for continuing appending:The rule
transforms the open list into a proper list, setting the empty cell to
[]
:This is of course exactly what happens in the tree example of the DCG Primer:
Here, the datastructure which grows at its leaves is not a list, but a binary tree.