Discussions related to Visual Prolog
David Snook
Active Member
Posts: 36
Joined: 6 Feb 2003 0:01

Advanced List handling

Unread post 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
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post 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")] )
Regards Thomas Linder Puls
PDC
Paul Cerkez
VIP Member
Posts: 106
Joined: 6 Mar 2000 0:01

Unread post 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.
AI Rules!
P.
David Snook
Active Member
Posts: 36
Joined: 6 Feb 2003 0:01

Unread post by David Snook »

Perfect!

Thank you Thomas.

Regards,

David Snook
User avatar
Ferenc Nagy
VIP Member
Posts: 215
Joined: 24 Apr 2007 12:26

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

Unread post 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?
TIA, Regards,
Frank Nagy
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post 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.
Regards Thomas Linder Puls
PDC
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post 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.
Last edited by Thomas Linder Puls on 5 Dec 2012 10:25, edited 1 time in total.
Regards Thomas Linder Puls
PDC
User avatar
Ferenc Nagy
VIP Member
Posts: 215
Joined: 24 Apr 2007 12:26

Other solution of minimum and its index

Unread post 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?
TIA, Regards,
Frank Nagy
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

:lol:
Regards Thomas Linder Puls
PDC
User avatar
Ferenc Nagy
VIP Member
Posts: 215
Joined: 24 Apr 2007 12:26

Are findall and forall deprecated?

Unread post by Ferenc Nagy »

Are the old (defined 25 years age on Turbo Prolog) findall and the relative new list::forall predicates deprecated?
TIA, Regards,
Frank Nagy
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post 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.
Regards Thomas Linder Puls
PDC
User avatar
George
Active Member
Posts: 47
Joined: 19 Sep 2011 8:54

Unread post 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 ?
Kind Regards,
George Ananth. S | Prolog Developer
georgeananth.prolog@gmail.com
+91 9791499282
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post 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.
Regards Thomas Linder Puls
PDC
User avatar
George
Active Member
Posts: 47
Joined: 19 Sep 2011 8:54

Unread post 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:
Kind Regards,
George Ananth. S | Prolog Developer
georgeananth.prolog@gmail.com
+91 9791499282
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Re:

Unread post 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.
Post Reply