# Find occurrence of predicate anywhere it exists in a nested, compound object?

**URL:** <https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788>\
**Category:** Help!\
**Created:** [June 11, 2019, 4:33pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788 "2019-06-11T16:33:41Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![prodog](https://avatars.discourse-cdn.com/v4/letter/p/57b2e6/32.png) [@prodog](https://swi-prolog.discourse.group/u/prodog)\
**Post date:** [June 11, 2019, 4:33pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/1 "2019-06-11T16:33:41Z")

</div>

Let’s say I have a really complicated term that is any combination of lists, atoms, predicates, etc. Worse, the items have child items themselves of the same nature. So I have a single term, but one that is composed of a completely free form nature of items, with child items containing child items, recursively with (possibly) _interleaved_ lists, atomics, etc.

Is there an SWI-Prolog predicate that can take a desired _predicate name_, and scan the entire tree of a term and find occurences of the desried predicate anywhere it might exist in the parent term’s tree of items?

I think I can write such a function, but it will be messy and tricky given the interwined nature of the clauses needed to switch between lists, compound terms, and atomics as you descend recursively through the parent term’s structure. I’m hoping there might be something in SWI-Prolog or one of its packages that does this.

For example. Supposed I was looking for a predicate named `my_pred_to_find` in a complex term named `parent_term`:

```prolog
Target = 
    parent_term(
        a, b, c, 
        child_predicate(
            [1, 2, 3, 
                my_pred_to_find(
                    [dog, nested_cat([fluffy, furry, paw(has_toes)])])])),
find_predicate_anywhere(my_pred_to_find, Target, PredFound).

```

The hypothetical function named `find_predicate_anywhere` would bind `ValueOfPredFound` to:

```prolog
ValueOfPredFound = [dog, nested_cat([fluffy, furry, paw(has_toes)])].

```

Is there such a function/predicate in SWI-Prolog or one of its packages?

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [June 11, 2019, 4:45pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/2 "2019-06-11T16:45:48Z")

</div>

> [@prodog](#):
>
> predicate name

Maybe the word `functor` would be better here. I think of predicate as free standing clause(s).

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [June 11, 2019, 5:18pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/3 "2019-06-11T17:18:12Z")

</div>

This can help you: [compound\_name\_arguments/3](http://www.swi-prolog.org/pldoc/man?predicate=compound_name_arguments/3) and [nonvar](http://www.swi-prolog.org/pldoc/man?predicate=nonvar/1).

You might also take a look at [pprint](https://github.com/SWI-Prolog/swipl-devel/blob/1ccd75fd55e42d26ec7f6d7f6f295ad5cba6d4a9/library/listing.pl#L952) in [listing.pl](https://github.com/SWI-Prolog/swipl-devel/blob/1ccd75fd55e42d26ec7f6d7f6f295ad5cba6d4a9/library/listing.pl), which processes all the subterms recursively.

---

<div class="post-metadata">

**Author:** ![prodog](https://avatars.discourse-cdn.com/v4/letter/p/57b2e6/32.png) [@prodog](https://swi-prolog.discourse.group/u/prodog)\
**Post date:** [June 11, 2019, 6:07pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/4 "2019-06-11T18:07:20Z")

</div>

Hello again.

Here is the code that I came up with for `find_functor_anywhere`. Any comments would be welcome. It appears to work, but I have not exhaustively tested it yet:

```prolog
% ----->>>>> find_functor_anywhere

    % ----->>>>> find_functor_anywhere_list

    % Failed to find the desired functor anywhere in the list. This is
    % an overall failure.
    find_functor_anywhere_list([], _, _) :- !, fail.

    % Test the head of the list for a match.
    find_functor_anywhere_list(DesiredFunctor, [H | _], ValueInTarget) :-
        find_functor_anywhere(DesiredFunctor, H, ValueInTarget),
        !.

    % Process the rest of the list.
    find_functor_anywhere_list(DesiredFunctor, [_ | T], ValueInTarget) :-
        find_functor_anywhere_list(DesiredFunctor, T, ValueInTarget),
        !.

    % ----->>>>> find_functor_anywhere_list

    % An atom is not a match.
    find_functor_anywhere_mux(_, Target, _) :-
        atom(Target),
        !,
        fail.

    % MATCH: Found a compound term with the desired functor name.
    find_functor_anywhere_mux(DesiredFunctor, Target, ValueInTarget) :-
        Target =.. [DesiredFunctor | ValueInTarget],
        !.

    % A list has to be iterated.
    find_functor_anywhere_mux(DesiredFunctor, Target, ValueInTarget) :-
        is_list(Target),
        !,
        find_functor_anywhere_list(DesiredFunctor, Target, ValueInTarget).

    % A compound term must have each argument inspected. This is a
    % compound term without the desired functor name.
    find_functor_anywhere_mux(DesiredFunctor, Target, ValueInTarget) :-
        Target =.. [_ | Args],
        !,
        find_functor_anywhere_list(DesiredFunctor, Args, ValueInTarget).

% ----->>>>> find_functor_anywhere_list

% Find the first occurrence of a compound term whose functor matches the desired functor name.
% Unify ValueInTarget with the arguments of that functor occurrence.
find_functor_anywhere(DesiredFunctor, Target, ValueInTarget) :-
    nonvar(DesiredFunctor),
    atom(DesiredFunctor),
    nonvar(Target),
    find_functor_anywhere_mux(DesiredFunctor, Target, ValueInTarget).

```

Sample query:

```prolog
48 ?- find_functor_anywhere( cuddly, [dog, cat, cuddlyx(duck, [a,b,c], cuddly(frog, mog))], ValueInTarget).
ValueInTarget = [frog, mog].

```

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [June 11, 2019, 7:06pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/5 "2019-06-11T19:06:54Z")

</div>

You should consider variations that has parameters to choose the level or the in\_order walk location. Currently you are defaulting to the first one it finds, but what if you are searching a tree where there are lots of functors with the same name e.g. `leaf` `(leaf(1,leaf(2,nil,nil),leaf(3,(leaf(4,nil,nil),nil))))`.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [June 11, 2019, 8:06pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/6 "2019-06-11T20:06:03Z")

</div>

Does this do what you want?

```prolog
use_module(library(apply)).

find_in_term(Functor, Within, FoundArgs) :-
    Within =.. [Functor|FoundArgs].
find_in_term(Functor, Within, FoundArgs) :-
    Within =.. [_|WithinArgs],
    convlist(find_in_term(Functor), WithinArgs, FoundArgsList),
    member(FoundArgs, FoundArgsList).

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [June 11, 2019, 8:53pm UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/7 "2019-06-11T20:53:30Z")

</div>

(This is better code … no need for `convlist/2`).

```prolog
find_in_term(FunctorName, Within, FoundArgs) :-
    Within =.. [FunctorName|FoundArgs].
find_in_term(FunctorName, Within, FoundArgs) :-
    Within =.. [_|WithinArgs],
    member(WithinArg, WithinArgs),
    find_in_term(FunctorName, WithinArg, FoundArgs).

```

The code can also be refactored to avoid doing `=../2` twice, if the tiny bit of speed-up is important.

---

<div class="post-metadata">

**Author:** ![prodog](https://avatars.discourse-cdn.com/v4/letter/p/57b2e6/32.png) [@prodog](https://swi-prolog.discourse.group/u/prodog)\
**Post date:** [June 12, 2019, 12:07am UTC](https://swi-prolog.discourse.group/t/find-occurrence-of-predicate-anywhere-it-exists-in-a-nested-compound-object/788/8 "2019-06-12T00:07:20Z")

</div>

Thanks Peter. I fall so often into the habit of using list dissassembly and recursion ([H | T]) to inspect list elements, that I forget you can use `member()` with backtracking in a case like this where you don’t need to copy over the original list elements to an `output` variable. Your code is definitely more succinct and efficient.
