Page 1 of 2

Advanced List handling

Posted: 30 Nov 2012 9:43
by David Snook
I've noticed there are some advanced list handling routines available but I haven't been able to find any explanation or example on how to use them.

A very elegant use of these in VP version 7.4 ODBCDemo example, tableForm class is presented although I'm finding it difficult to follow. Specifically the "formatTable" predicate.

Is there a description on how to use list::map, list::fold and list::zip_nd that I can't seem to find?

Regards,

David Snook

Posted: 30 Nov 2012 14:37
by Thomas Linder Puls
Well the code in ODBCDemo is not "beginner code".

But in brief:

map applies a function to all elements in a list, returning a new list containing then resulting values:

Code: Select all

map([1,2,3], { (X) = X+1} ) = [2,3,4]
fold calculates a value from the elements in a list, given a start value:

Code: Select all

fold(1,2,3], { (Element, Sum) = Element+Sum }, 0) = fold([1,2], { (Element, Sum) = Element+Sum }, 3+0 ) = fold([1], { (Element, Sum) = Element+Sum }, 2+(3+0) ) = fold([], { (Element, Sum) = Element+Sum }, 1+(2+(3+0)) ) = 1+(2+(3+0)) = 6
zip is a function that pairs two lists in to a single list:

Code: Select all

zip([1,2,3], ["a", "b", "c"]) = [tuple(1, "a"), tuple(2, "b"), tuple(3, "c")]
zip_nd nondeterministically returns the elements of zipping the lists:

Code: Select all

zip_nd([1,2,3], ["a", "b", "c"]) = getMember_nd(zip([1,2,3], ["a", "b", "c"])) = getMember_nd( [tuple(1, "a"), tuple(2, "b"), tuple(3, "c")] )

Posted: 30 Nov 2012 16:32
by Paul Cerkez
Thomas,
I can see a lot of uses for zip and zip_nd for my projects.

Map and fold not so much for my work but I csan see where it would be real useful to some.

thanks.
p.

Posted: 1 Dec 2012 4:07
by David Snook
Perfect!

Thank you Thomas.

Regards,

David Snook

How can I select the value and the index of the smallest element in a list?

Posted: 5 Dec 2012 7:12
by Ferenc Nagy
Please add some more examples to the new list manipulating commands:
E. g.: How can I select the value and the index of the smallest element in a list?

Posted: 5 Dec 2012 9:43
by Thomas Linder Puls
That kind of list operations are in many respects the opposite of index operations.

The first step in the code below is absolutely not in the spirit of the operations we consider here.
  • L is the input list.
  • I varM_integer used to count the indexes.
  • IL is a list of indexes ([0, 1, 2, 3, ...]), it is made by mapping a "function" that counts (it is not a function in mathematical sense).
  • LIL is the result of zipping the elements together with their indexes [tuple("w", 0), tuple("q", 1), ...]
  • In the if-condition I take the first element of the sorted LIL list, which is tha smallest

Code: Select all

clauses     run() :-         L = ["w", "q", "a", "s", "d"],         I = varM_integer::new(-1),         IL = map(L, { (_) = I:value :- I:add(1) }),         LIL = zip(L, IL),         if [tuple(M,MI)|_] = sort(LIL) then             writef("The smallest element is '%' and its index is % (control nth(%, L) = %)\n", M, MI, MI, nth(MI, L))         else             write("The list must be empty")         end if.

Posted: 5 Dec 2012 10:14
by Thomas Linder Puls
The formatTable from the ODBC demo program example is an extensive use of these predicates:

Code: Select all

constants     separator = "|".   predicates     formatTable : (string** Table) -> string TableAsText. clauses     formatTable(RowsList) = TableAsText :-         ColumnsLenths = list::map(RowsList, {(Row) = list::map(Row, string::length)}),         if [Head | Tail] = ColumnsLenths then             MaxLengths = list::fold(Tail, {(A, B) = [math::max(Aj, Bj) || tuple(Aj, Bj) = list::zip_nd(A, B)]}, Head),             NewRows = list::map(RowsList,                 {(Columns) = [string::format("% % ", separator, Col) ||                     tuple(C, Length) = list::zip_nd(Columns, MaxLengths),                     SpaceAc = Length - string::length(C),                     Col = string::concat(C, string::create(SpaceAc, " "))] }),             NewRowsAsStrings = list::map(NewRows, {(ColList) = string::concat(string::concatList(ColList), separator)}),             TableAsText = string::concatWithDelimiter(NewRowsAsStrings, "\n")         else             TableAsText = ""         end if.
If is quite advanced because it works on a two level list. A Table is a list of rows, each of which is a list of strings (i.e. the values in the columns).

The ColumnsLenghts is a similar shaped "table" containing the length of the strings insted of the strings themselves. It is a two level map, because the table is a two level list.

The fold in the if is the most complex operation in the code. If you understand that you understand everything ;-).

The purpose of the fold is to calculate a list containg the (for each column) largest string length in that column. The fold runs over the rows in Tail and uses Head as initial value. If Tail is empty Head contains the longest lenghts. The function in the fold recieves a row A and an accumulated result B, and it must calculates the next accumulated result.

First it zip the two list together, i.e. the current row and the acumulated result, then it creates a list by selecting the largest of each of these two elements.

Got that :-)?.

The next step is to create a table of formatted values prefixed with the initial table seperator "|". For each row it zip string values together with the max-width os the corresponding column, and then it format each of these pairs into the desired result.

NewRowsAsStrings is the result of concatenating all the values in each row together with terminating separator. Finally TableAsText is the result of contanating the rows separated by new-lines.

Other solution of minimum and its index

Posted: 5 Dec 2012 10:24
by Ferenc Nagy
Thomas,
you were overzealous.
The list::sort order the whole list which is expensive in case of a long list.
I have another idea:

Code: Select all

MinValue=fold(List, { (Element, Min) = math::min(Element,Min)}, upperBound(<domain of Element>)
calculates the minimal value.
The next step is the getting of the index:

Code: Select all

MinsIndex=list::trygetIndex(MinValue,LiIst)
.

Can I put an

Code: Select all

 if <do something>  endif
within the {...} of the above code?

Posted: 5 Dec 2012 10:35
by Thomas Linder Puls
:lol:

Are findall and forall deprecated?

Posted: 7 Dec 2012 9:24
by Ferenc Nagy
Are the old (defined 25 years age on Turbo Prolog) findall and the relative new list::forall predicates deprecated?

Posted: 7 Dec 2012 10:21
by Thomas Linder Puls
findall is a list comprehension, and we reccomend using the list comprehension. So in that respect I consider findall deprecated.

But forall is not deprecated. There are a lot of different ways write a certain list routine, and sometimes one method is prefarable to another, but at other times it may be different.

Posted: 4 Mar 2014 5:54
by George
Is there any way we can terminate the forAll loop in middle of the processing ?

Ex :

Code: Select all

class predicates     testForAllLoop : (). clauses     testForAllLoop():-         MyList = ["A", "B", "C", "D", "E", "F", "G"],         list::forAll(MyList, {(Head):-             if Head = "D" then                 !  %Need to end the loop here - please suggest me..             end if             }).
When the Head is "D", it should end the loop and it should not proceed further to check "E", "F" etc..

How can we do this on the forAll loop. ??

Ex:

Code: Select all

class predicates     loopThrough : (string, string*). clauses     loopThrough(_,[]):- !.     loopThrough(Head, [Head|_]):- !.     loopThrough(Head, [_|Rest]):-         loopThrough(Head, Rest).
How to do the above on forAll loop ?

Posted: 4 Mar 2014 8:41
by Thomas Linder Puls
Why use a predicate against its obvious intention? I suggest that you don't use forAll when you don't want to do it for all elements.

Posted: 4 Mar 2014 10:14
by George
Why use a predicate against its obvious intention?
I agree with your point, Mr. Thomas Linder Puls..

I felt like code minimization - Instead of using too many predicates for doing small Job.. I could use "forAll/.." predicate to loop through..

When we have a option to break through - foreach loop : why don't we bring option to break "forAll/.." loop..

If you bring a option to setup a boundary condition inside forAll/.. loop that will be great..


I suggest that you don't use forAll when you don't want to do it for all elements.
Sure, I'll follow this Standard, Mr. Thomas Linder Puls.
:roll:

Re:

Posted: 6 Mar 2014 14:58
by Harrison Pratt
Two little predicates and your job is done.

%-- a member is "before" the Chop
%-- as long as the Tail of the list contains the Chop string
before_nd( Chop, [S|T], S ):- member( Chop, T ).
before_nd( S, [_|T], BF ):- before_nd( S, T, BF ).

member( S, [S|_] ):- !.
member( S, [_|T] ):- member(S,T).


QED:

Code: Select all

PREDICATES    testForAllLoop      member( STRING, SLIST ) -(i,i)    nondeterm before_nd( STRING, SLIST, STRING ) -(i,i,o)   CLAUSES before_nd( Chop, [S|T], S ):- member( Chop, T ). before_nd( S, [_|T], BF ):- before_nd( S, T, BF ).   member( S, [S|_] ):- !. member( S, [_|T] ):- member(S,T).     testForAllLoop:-    MyList = ["A","B","C","X","D","E","F"],    StopStr = "X",    findall( S, before_nd( StopStr, MyList, S ), SS ),    write( "Front list: ", SS, "\n" ),    fail. testForAllLoop:-    MyList = ["A","B","C","X","D","E","F"],    StopStr = "X",    before_nd(StopStr,MyList,S),  % operate on S and backtrack       %-- do something here:       write( "Backtrack result: ", S, "\n" ),       fail. testForAllLoop:-    MyList = ["A","B","C","X","D","E","F"],    StopStr = "Q",    before_nd(StopStr,MyList,S),  % FAIL if StopStr not in MyList       %-- do something here:       write( "Backtrack result: ", S, "\n" ),       fail. testForAllLoop:-    MyList = ["X"],    StopStr = "X",    before_nd(StopStr,MyList,S),  % FAIL if no strings before StopStr       %-- do something here:       write( "Backtrack result: ", S, "\n" ),       fail. testForAllLoop.