<div dir="ltr"><div class="gmail_default" style="font-family:monospace,monospace">I'd like to second Zoltan's point.</div><div class="gmail_default" style="font-family:monospace,monospace">I've solved that particular Rosetta Code problem in two different</div><div class="gmail_default" style="font-family:monospace,monospace">programming languages, using an imperative style with hash tables,</div><div class="gmail_default" style="font-family:monospace,monospace">and modulo the usual hand-waving about hash-tables being "O(1)"</div><div class="gmail_default" style="font-family:monospace,monospace">-- which is fair enough in this case -- the code is obviously</div><div class="gmail_default" style="font-family:monospace,monospace">linear in the size of the two CSV files the program has to read.</div><div class="gmail_default" style="font-family:monospace,monospace"><br></div><div class="gmail_default" style="font-family:monospace,monospace">I'm reminded of my experience with XPath, XQuery, and XSLT, that</div><div class="gmail_default" style="font-family:monospace,monospace">sometimes it is simpler and more readable NOT to use a specialised</div><div class="gmail_default" style="font-family:monospace,monospace">query language but to use plain high level code in a language with</div><div class="gmail_default" style="font-family:monospace,monospace">decent facilities.  (Such as Mercury.)  Not coincidentally, XPath,</div><div class="gmail_default" style="font-family:monospace,monospace">XQuery, and XSLT hide practically everything you need to understand</div><div class="gmail_default" style="font-family:monospace,monospace">performance.</div><div class="gmail_default" style="font-family:monospace,monospace"><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, 31 Dec 2022 at 08:35, Zoltan Somogyi <<a href="mailto:zoltan.somogyi@runbox.com">zoltan.somogyi@runbox.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
2022-12-30 00:27 GMT+11:00 "Mark Clements" <<a href="mailto:mark.clements@ki.se" target="_blank">mark.clements@ki.se</a>>:<br>
> %% patient and visit are nondet predicates<br>
> main(!IO) :-<br>
>     print_line("{Id, RowNumber, Date, Score, CumScore}", !IO),<br>
>     aggregate((pred({Id,RowNumber,Datei,Scorei,CumScorei}::out) is nondet :-<br>
>                    patient(Id,_),<br>
>                    Combined = (pred(Date::out,Score::out) is nondet :- visit(Id,Date,Score)),<br>
>                    Combined(Datei,Scorei),<br>
>                    bag_cum_sum(Combined)(Datei,CumScorei),<br>
>                    Dates = (pred(Date::out) is nondet :- Combined(Date,_)),<br>
>                    bag_row_number(Dates)(Datei,RowNumber)),<br>
>               print_line,<br>
>               !IO).<br>
> <br>
> Third, I have sought to stay with nondet predicates, with the implementation internally using lists -- is there a better approach?<br>
<br>
I think that getting your data from nondet predicates is fundamentally a bad idea.<br>
The reason is simple: it bakes the data into the program. If you want to run<br>
the same task on a different data set, you have to modify the program and recompile it.<br>
This is much less convenient than a program that you can run on a different data set<br>
simply by invoking it with different file names.<br>
<br>
The attached code is my solution to the same task on <a href="http://rosettacode.org" rel="noreferrer" target="_blank">rosettacode.org</a>.<br>
You will note that its operative part is longer than your code above,<br>
but it is also much simpler, and therefore easier to read and to<br>
understand. It is also easier to reason about its performance.<br>
For example,<br>
<br>
-  the main operation loops over all visits,<br>
- the non-constant-time part of each iteration consists of lookup and update operations<br>
  on its main data structure, VisitDataMap,<br>
- VisitDataMap is a map, and is therefore implemented using balanced trees.<br>
<br>
This makes it clear that its complexity is O(N log N), where N is the number of visits.<br>
(Technically, it is O(N log M), where M is the number of unique patients, but the<br>
difference is negligible.) By contrast, I cannot tell anything about the performance<br>
of the aggregate-using code above, because all the relevant details are hidden<br>
behind abstraction boundaries, whose documentation is silent about performance.<br>
Note that in the SQL programs from which you draw your inspiration, selecting<br>
the right set of indexes for each relation is usually an important design concern.<br>
<br>
Zoltan._______________________________________________<br>
users mailing list<br>
<a href="mailto:users@lists.mercurylang.org" target="_blank">users@lists.mercurylang.org</a><br>
<a href="https://lists.mercurylang.org/listinfo/users" rel="noreferrer" target="_blank">https://lists.mercurylang.org/listinfo/users</a><br>
</blockquote></div>