The easiest solution would be to convert this to a list, filter the list with the predicate, and then construct a new assoc.
:- use_module(library(assoc)).
:- use_module(library(apply)).
filter_assoc_l(Filter, In, Out) :-
assoc_to_list(In, List),
include(Filter, List, OutList),
list_to_assoc(OutList, Out).
You apparently cannot do this. But you still need a way to iterate all the items of the assoc without entering a failure-driven loop which will undo your bindings after each fail. gen_assoc
will give you all the keys, but you'll either have to use findall/3
(making it the same as converting it to a list) or you'll have to fail after each binding (undoing your work).
Another approach here is to get the list of keys only, and then use the removal predicate to get rid of values when they don't match the filter. First we need to get the keys and then call a helper predicate to walk the list of keys:
filter_assoc_unlist(Filter, In, Out) :-
assoc_to_keys(In, Keys),
filter_assoc_keys(Filter, In, Keys, Out).
Now we can retrieve the current key/value (the head of the keys list) and then call your filter predicate to check it. If it passes, we just recur; if it fails, we remove it and pass the result of removing it into the recursive step.
filter_assoc_keys(Filter, In, [Key|Keys], Out) :-
min_assoc(In, Key, Value),
(call(Filter, Key-Value) ->
filter_assoc_keys(Filter, In, Keys, Out)
;
del_assoc(Key, In, _, Next),
filter_assoc_keys(Filter, Next, Keys, Out)
).
When we run out of keys, the input is the output:
filter_assoc_keys(_, Out, [], Out).
I don't know if this fulfills your criteria or not, but I hope it does.
Wynik
? – Daniel Lyons