Okay, I start with this:

Okay, I start with this:

procedure PrintSquares1(aInteger: integer);
var
i: Integer;
begin
for i := 1 to aInteger do
begin
WriteLn(i, ' - ', i * i);
end;
end;

Then I go to this:

procedure PrintSquares2(aInteger: integer);
begin
if aInteger > 0 then
begin
PrintSquares1(aInteger - 1);
WriteLn(aInteger, ' - ', ainteger * aInteger);
end;
end;

And finally this (using Spring4D Collections):

procedure PrintSquares3(aLength: integer);
var
List: IList;
i: integer;
begin
List := TCollections.CreateList;
for i := 1 to aLength do
begin
List.Add(i);
end;
List.ForEach(procedure(const aInt: integer) begin WriteLn(aInt, ' - ', aInt * aInt) end);
end;

But I feel like there should be a better way to fill that collection.

Thoughts?

Comments

  1. What exactly is the task? Because if its to print x numbers and their squares everything after version 1 is overkill.

    ReplyDelete
  2. Is the second piece of code correct? It calls PrintSquares1

    ReplyDelete
  3. You mean better than looping over Add to fill the list?

    ReplyDelete
  4. Nicholas Ring Oops, you right. Thanks.
    David Millington Yes.
    Stefan Glienke Overkill in the interest of example. :-)

    ReplyDelete
  5. Delphi libraries have almost always been very 'dynamic' in its APIs, indicating that the implementation would do a huge number of memory allocations, and then have some clever implementations tjat were not as bad as predicted. If you would use a programming language like R for this, the code would be like (Delphi-zed):

    var a,b:TList ;
    a:=CreateVector (n,1);
    b:=IntToStr(a)+'-'+IntToStr (a*a);
    Writeln (b);

    All three lines would deal with vectors, even the + operator would concatenate many strings in parallel, knowing exactly the number of items to handle, before concatenating the first string. Memory allocation count is significantly reduced compared to Delphi, code is easier to read (imo), and it is just faster code. But it would require libraries that Delphi currently has not. Maybe next Delphi version?

    ReplyDelete
  6. When code is short, it is IMHO generally preferable to use literal code rather than library functions, like in your first case.

    When a library function can only save a handful lines, you have to balance it against the loss of readability and "obviousness" of the code: library functions come by the millions, with side-effects, specifics, and documentations, meaning you are more likely to NOT know exactly what they do, rather than the opposite.

    This is IME especially true nowadays, as developers have to jump between languages and frameworks.
    So KISS: let simple literal code stay literal.

    ReplyDelete
  7. I do not believe that loops increase readability.

    ReplyDelete
  8. Lars Dybdahl Very hard to imagine anything more readable than a for loop from 1 to N

    ReplyDelete
  9. Lars Dybdahl David Heffernan ...on the other hand interfaces + ForEach + anonymous method will obfuscate a stack trace so that the Enemy will have a harder time debugging your crash reports. So there is that.

    ReplyDelete
  10. David Heffernan I think there is a difference with explicitly writing a loop and stating that you want to perform an operation on a range.

    In Scala you can for example write this (doing that in Delphi unfortunately of course leads to massive bloat and performance impact):

    1 to 10 foreach { i => println(i + " - " + i*i) }

    You can argue that this is also a loop - yes but not explicitly written. I define a range and then perform an operation on it. (1 to 10) is an entity of its own in Scala (range), a for-to or for-in loop in Delphi is not.

    ReplyDelete
  11. Stefan Glienke  I don't disagree. I just don't think that loops are hard to read as Lars Dybdahl appeared to suggest.

    ReplyDelete
  12. David Heffernan Not in this particular case, agreed. But they can add quite some unneeded ceremony.

    ReplyDelete
  13. David Heffernan Some people prefer foreach() for the same reason, that for...to is more errorprone. But even foreach() risks doing things in a slow way, for instance, if the loop contains .Add() statements. When using vectors, you know the size of the data that you are going to create, so as long as strings and objects are not involved, you can allocate all the data in one step. The data types that one needs, depend on the purpose of ones software. If the purpose is to do a lot of calculations on table-like data in memory, then loops quickly become tedious. That is why I make my statistical analysis in R and not in Delphi... it is much easier to write and maintain in R. I just don't see any reason why Delphi should not be able to do the same as what R does, maybe in a slightly more compiler-friendly way. Until Delphi can do it, I keep using R for advanced reports for my Delphi apps.

    ReplyDelete
  14. Stefan Glienke your Scala example is actually an explicit-implicit loop, implicit as in "not stated in the code", yet explicit as if such a foreach printed something like

    10 - 100
    1 - 1
    3 - 9
    ...

    many would be complaining, yet that would still be a semantically correct interpretation of the code, since if you are only specifying an operation per item, the execution order should be a detail left to the implementation.

    A "foreach that is not a loop" should not be making any promises about ordering, otherwise it's just syntactic sugar in my book (which is not bad per se, but not enough to qualify as more than a loop)

    ReplyDelete
  15. +Lars That's an entirely different topic. An interesting one. R is notorious for its appalling performance. The apply, lapply, sapply, and so on are a means to keep code away from the R interpreter and its terrible performance. The resulting code is frequently unintelligible in my view.

    Even then the perf is frequently very poor because of all params are copied when passed to functions.

    All I said was that a simple for loop is very clear and easy to read.

    ReplyDelete
  16. Getting back to Stefan Glienke answer --- Thank you:

    procedure PrintSquares3(aInteger: integer);
    var
    List: IEnumerable;
    begin
    List := TEnumerable.Range(1, aInteger);
    List.ForEach(procedure(const aInt: integer) begin WriteLn(aInt, ' - ', aInt * aInt) end);
    end;

    Never noticed all those class functions on TEnumerable before.  Nice.

    ReplyDelete
  17. Lars Dybdahl It should be possible to write a series of range classes, though I don't know if generics is powerful enough. Ideally it would be nice to support primitives like IntToStr but over an entirely generic (unknown) type... I can't think of a clean way in Delphi without too many specialisations.

    In C++ years ago I wrote a custom function evaluation engine which operated over massive amounts of data. Version one went datapoint by datapoint and was very slow. Version one-point-one was vector-ised: all types were vectors and operations were done on entire vectors at once. And it was much faster.

    ReplyDelete
  18. David Heffernan I agree, even the functional programming language Haskell goes imperative when speed is required. However, just because somebody implemented it poorly, parts of the concepts can still be used. There are many Delphi functions that work with TList and TStringList, for instance, so we are almost there, already. I recently looked at some Delphi source code, that created 1 object, and discovered that the relatively simple object creation actually created hundreds of objects. Some of them were subobjects of library class objects, that were used. Once we get 100-1000 times more object creations, than we actually thought we had, things start to become slower than necessary. Instead, if you set the capacity of a TList before adding items, you reduce the amount of allocations to 1. And that is basically what I advocate, could be supported automatically. It is already supported when copying lists, and could also be supported in other functions or operators. It only becomes slower, when there are a significant number of sub-results that are allocated as vectors, or if the .Add call can be eliminated automatically by the compiler in a foreach loop.

    R is primarily unreadable, because it is patchy and inconsistent.

    ReplyDelete
  19. Nick, so I take you watched that Robert C. Martin video John tweeted about? :)

    https://t.co/gk8i4Nh5ga

    ReplyDelete
  20. Hallvard Vassbotn Yep.  Exactly.  :-)

    ReplyDelete

Post a Comment