Maybe add `vectorial_nth0/3`, `vectorial_replace/4` to library(lists)?

I did some practice exercise on a problem at Rosetta Code. This code is likely heavier than it needs to be:

Sort Disjoint Sublist

Given a list of values and a set of integer indices into that value list, the task is to sort
the values at the given indices, but preserving the values at indices outside the set of those
to be sorted.

Make your example work with the following list of values and set of indices: ``

  values: [7, 6, 5, 4, 3, 2, 1, 0]
  indices: {6, 1, 7}

Where the correct result would be:

[7, 0, 5, 4, 3, 2, 1, 6]

Rosettacode is a bit disshelved in that it needs a better filing principle than a wiki, online code-exercising test harnesses and a code-markup-and-commenting system. Some people also write “coding golf” exercises which misses the point.

Anyway, the above has made me notice that one might want to consider the following additions to library(list):

vectorial_nth0(IndexList,List,Elements)

The integer indexes listed in IndexList (not necessarily ordered or disjoint) indicate which elements from List to unify with elements from Elements, in order of appearance of the pairs of elements from IndexList and Elements. IndexList and Elements must be of the same length.

For example:

vectorial_nth0([1,0,1,3,2],List,[A,B,C,D,E])

performs:

nth_0(1,List,A),
nth_0(0,List,B),
nth_0(1,List,C),
nth_0(3,List,D),
nth_0(2,List,E).

Heroic programming could enlargen semantics to accept nonground elements in IndexList.

Similarly:

vectorial_replace(IndexList,Elements,ListIn,ListOut)

The integer indexes listed in IndexList (necessarily disjoint but necessarily ordered) indicate which elements from ListIn to replace by element from Elements (which could be fresh variables), in order of appearance of the pairs of elements from IndexList and Elements, giving ListOut. IndexList and Elements must be of the same length.

One could then write things like this solution in R

values=c(7,6,5,4,3,2,1,0)
indices=c(7,2,8)
values[sort(indices)]=sort(values[indices])
print(values)
7 0 5 4 3 2 1 6

Similar to D, Groovy, (maybe APL, I can’t read it), Perl & Raku. The various LISPs still are weirdly verbose and opaque.

Note that you can also use:

   ?- maplist([Index,Element]>>nth0(Index,[a,b,c,d],Element), [1,0,1,3,2], [A,B,C,D,E]).
    A = C, C = b,
    B = a,
    D = d,
    E = c.
1 Like

I have not tried this but have you considered using arg/3?

A no, that’s for deconstructing terms. I just need parallel list access.

After a rewrite this morning, I have:

% ===
% Main predicate
% ===
 
sort_disjoint_sublist(Values,Indexes,ValuesSorted) :-
   sort(Indexes,IndexesSorted),
   insert_fresh_vars_by_splintering(IndexesSorted,Values,FreshVars,ValsToSort,ValuesFreshened),
   msort(ValsToSort,ValsSorted),  % this is the "sorting of values"
   % The next two lines could be left out with suitable naming, 
   % but they make explicit what happens:
   FreshVars = ValsSorted,         % fresh variables are unified with sorted variables
   ValuesSorted = ValuesFreshened. % ValuesFreshend is automatically the sought output 
 
% ===
% Helpers
% ===
 
insert_fresh_vars_by_splintering([I|Is],Values,[Fresh|FreshVars],[ValAtI|ValsToSort],ValsFreshyFinal) :-
   splinter(Values,I,ValAtI,ValsFront,ValsBack),         % splinter  Values  --> ValsFront + ValAtI + ValsBack
   append([ValsFront,[Fresh],ValsBack],ValsFreshyNext),  % recompose ValsFront + Fresh + ValsBack --> ValuesFreshyNext
   insert_fresh_vars_by_splintering(Is,ValsFreshyNext,FreshVars,ValsToSort,ValsFreshyFinal).
 
insert_fresh_vars_by_splintering([],V,[],[],V).
 
% "splinter" a list into a frontlist, the element at position N and a backlist
 
splinter(List, N, Elem, Front, Back) :-
    length(Front, N),
    append(Front, [Elem|Back], List).

This works perfectly well, but it’s hard to read.

It occurs to me that one of the reasons it is hard to read is that everyone knows what append is but nobody knows what splinter is. There is not enough “vocabulary”. Said vocabulary comes implicitly in expressions like the R expression

values[sort(indices)]=sort(values[indices])

Although it takes a bit to prove to oneself that this does what it should.

Ok, here is the code for vector_nth0(Indexes,List,Elements):

The code is less interesting than the test cases.

To my great amazement, this did search for fitting index vectors “out of the box” because nth0/3 is already doing search. Still took a bit to test this.

https://www.swi-prolog.org/pldoc/doc_for?object=same_length/2

I didn’t know that one. OTOH, I need to fail if both variables-intended-to-be-lists are fresh, but same_length/2 merrily generates away in that case.