How to iterate over a structure?
If I have a list like: [atm(abd,bubu,ha), atm(aei),atm(xyz,huhu), atm(aabb,a,e,x)]
how can I "iterate over" the elements from one of the structures atm
?
For example, for atm(abd, bubu, ha)
I would like write
abd
, bubu
and ha
.
The problem is that structures are variable in length.
Is there a way to convert the struct to a list? Thank.
source to share
Sure.
If First
there is atm(abd,bubu,ha)
(for example), this code will split it into a list that you can go through.
First =.. List.
Then it List
will be [atm, abd, bubu, ha]
.
IDK if it works in your specific version of PROLOG. I am using SWI-PROLOG. If not, your version may have a similar predicate.
For more information see http://www.swi-prolog.org/pldoc/doc_for?object=(%3D..)/2 .
source to share
Using (=..)/2
@TopologicalSort has already given a good answer, using (=..)/2
to convert the term to functor and argument list .
This, obviously, very often solves the immediate problem.
However, it also has its drawbacks: first, and most importantly, (=..)/2
it is not a general attitude. For example, we have:
? - X = .. Y. ERROR: Arguments are not sufficiently instantiated
This means that we cannot use this construct to generate solutions. It only works if its arguments are sufficiently instantiated.
Second, the use (=..)/2
also includes the time and memory overhead for building and presenting a list in addition to a term that already exists in a different form (And, mutatis & mutandis, in the other direction too, of course.)
So it might be worth asking: are there different ways to accomplish this task? Do they fit better?
Alternative 1: Manual execution
How do I convert? Let me count the paths.
In the example you cite, we should be able to process & mdash in the order they appear - terms of the following forms:
-
atm/3
-
atm/1
-
atm/2
-
atm/4
The point here is that the number of cases shown is finite , so we can easily deal with all of these:
atm_list (atm (A), [A]). atm_list (atm (A, B), [A, B]). atm_list (atm (A, B, C), [A, B, C]). atm_list (atm (A, B, C, D), [A, B, C, D]).
To convert a list of such terms, you can use maplist/2
:
? - Ls = [atm (abd, bubu, ha), atm (aei), atm (xyz, huhu), atm (aabb, a, e, x)], maplist (atm_list, Ls, Lists). Ls = [atm (abd, bubu, ha), atm (aei), atm (xyz, huhu), atm (aabb, a, e, x)], Lists = [[abd, bubu, ha], [aei], [xyz, huhu], [aabb, a, e, x]].
The main advantage is that this relationship is very general and can also be used to generate responses:
? - atm_list (A, Ls). A = atm (_27464, _27466, _27468), Ls = [_27464, _27466, _27468]; A = atm (_27464), Ls = [_27464]; A = atm (_27464, _27466), Ls = [_27464, _27466]; A = atm (_27464, _27466, _27468, _27470), Ls = [_27464, _27466, _27468, _27470].
It is also more efficient than using it (=..)/2
. It is clear that this can be done only if the number of arising cases is finite. ( Exercise : Write a Prolog program that generates sentences for all integers. 1..N).
Alternative 2: Using Lists
There are several well-known criteria for judging whether lists are an appropriate data structure. For example:
- Does an empty list make sense in your use case?
- Are there reasonable cases for all possible lengths?
- and etc.
Only you can answer this question for your specific use case, so I am only showing how it might look: Suppose you represent your entire initial list like this:
[[abd, bubu, ha], [aei], [xyz, huhu], [aab, a, e, x]]
Then the whole problem doesn't even arise , because the elements are already specified as lists. So there is no need to convert anything anymore.
source to share