Recent

Author Topic: How to assign an anonymous function  (Read 2475 times)

avk

  • Sr. Member
  • ****
  • Posts: 417
    • my self-education project
Re: How to assign an anonymous function
« Reply #15 on: March 29, 2021, 05:41:45 am »
Did I get his last example right?

The author seems to believe this code is unreadable and will cause problems a few months down the road...

Well, yes, as usual hype, but sometimes it's funny.
For example, how would you solve such a problem?

Let Memo1 contain a list of words(just ASCII for simplicity) separated by tabs, spaces, commas, semicolons, or just line breaks.
When Button1 is pressed, it is required to split this list into words, convert these words to lower case and display in Memo1 non-repeating words consisting of three or more characters and which are palindromes(in lexicographic order, each word on a separate line), or display a message that there are no palindromes.

engkin

  • Hero Member
  • *****
  • Posts: 2819
Re: How to assign an anonymous function
« Reply #16 on: March 29, 2021, 06:04:04 am »
Code: Pascal  [Select][+][-]
  1.         if Gender <> 'M'  then continue;
  2.         if Age <= 18  then continue;
  3.         if Age > 65   then continue;
  4.         if Name = ''  then continue;

Why would you do the conditions this way? I would have expected something along:
Code: Pascal  [Select][+][-]
  1.      if (Gender = 'M') and
  2.         (Age > 18)  and
  3.         (Age <= 65)   and
  4.         (Name <> '')  then
  5.  

What did I miss?

egsuh

  • Hero Member
  • *****
  • Posts: 750
Re: How to assign an anonymous function
« Reply #17 on: March 29, 2021, 06:55:52 am »
@engkin

No difference. Just for cases when there are other codes between the condition checks.

avk

  • Sr. Member
  • ****
  • Posts: 417
    • my self-education project
Re: How to assign an anonymous function
« Reply #18 on: March 29, 2021, 12:59:44 pm »
Well, yes, as usual hype, but sometimes it's funny.
For example, how would you solve such a problem?
...

Okay, then I'll try to answer my question myself.
A fairly typical Pascal code for solving this problem might look like this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   Words: TStringArray;
  4.   Palindromes: TStringList;
  5.   I: Integer;
  6.   s: string;
  7. begin
  8.   Words := Memo1.Lines.Text.Split([#9, #10, #13, ' ', ',', ';'], TStringSplitOptions.ExcludeEmpty);
  9.   for I := 0 to High(Words) do
  10.     Words[I] := LowerCase(Words[I]);
  11.   Palindromes := TStringList.Create;
  12.   try
  13.     for I := 0 to High(Words) do
  14.       if (Words[I].Length > 2) and (Words[I] = ReverseString(Words[I])) then
  15.         Palindromes.Add(Words[I]);
  16.     if Palindromes.Count <> 0 then
  17.       begin
  18.         Palindromes.Sort;
  19.         I := 0;
  20.         while I < Palindromes.Count do
  21.           begin
  22.             s := Palindromes[I];
  23.             Memo1.Append(s);
  24.             repeat
  25.               Inc(I);
  26.             until (I >= Palindromes.Count) or (Palindromes[I] <> s);
  27.           end;
  28.       end
  29.     else
  30.       Memo1.Append('no palindromes');
  31.   finally
  32.     Palindromes.Free;
  33.   end;
  34. end;  
  35.  

But it could also look like this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2.   function ToLower(const s: string): string; begin ToLower := LowerCase(s) end;
  3.   function Fits(const s: string): Boolean; begin Fits := (s.Length > 2) and (s = ReverseString(s)) end;
  4.   function Less(const L, R: string): Boolean; begin Less := L < R end;
  5.   function Join(const L, R: string): string; begin Join := R + LineEnding + L end;
  6. begin
  7.   Memo1.Append(Memo1.Lines.Text
  8.     .Words([#9, #10, #13, ' ', ',', ';'])
  9.     .Map(@ToLower)
  10.     .Select(@Fits)
  11.     .Distinct(@Less)
  12.     .Fold(@Join)
  13.     .OrElse('no palindromes')
  14.   );
  15. end;
  16.  

And, of course, everyone can have their own opinion, which of the solutions is more readable and simpler.

engkin

  • Hero Member
  • *****
  • Posts: 2819
Re: How to assign an anonymous function
« Reply #19 on: March 29, 2021, 07:05:18 pm »
Sorry, did not mean not to answer your question. I missed your post. I think I have a solution probably based on a generic sorted set. Might customize its add function. But yes, I agree, the second solution looks beautiful. I did not say otherwise.

This line is remarkable:
Code: Pascal  [Select][+][-]
  1.     .OrElse('no palindromes')
« Last Edit: March 29, 2021, 08:40:49 pm by engkin »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7185
  • Debugger - SynEdit - and more
    • wiki
Re: How to assign an anonymous function
« Reply #20 on: March 29, 2021, 08:36:35 pm »
With the appropriate helpers, I think the original code boils down to (not tested):
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   &Word: String;
  4. begin
  5.   LastWord := '';
  6.   for &Word in
  7.      lowercase(Memo1.Lines.Text)
  8.      .Split([#9, #10, #13, ' ', ',', ';'], TStringSplitOptions.ExcludeEmpty)
  9.      .Sort // could be a normal function, "for word in sort(memo....split) do"
  10.   do
  11.   begin
  12.     If &Word = LastWord then continue;
  13.     LastWord := &Word;
  14.     if (&Word.Length <=2) or (&Word <> ReverseString(&Word))  then continue;
  15.  
  16.     Memo1.Append(&Word);
  17.   end;
  18.  

IMHO: Ugly, with all that is going into the for loop header....
« Last Edit: March 29, 2021, 08:47:13 pm by Martin_fr »

engkin

  • Hero Member
  • *****
  • Posts: 2819
Re: How to assign an anonymous function
« Reply #21 on: March 29, 2021, 09:11:55 pm »
The class:
Code: Pascal  [Select][+][-]
  1. uses
  2.   LazUTF8, StrUtils, Generics.Collections;
  3.  
  4. type
  5.   TWordSet = specialize TSortedHashSet<String>;
  6.  
  7.   TPalindromeSet = class(TWordSet)
  8.     function Add(constref AValue: String): Boolean; override;
  9.   end;
  10.  
  11. function TPalindromeSet.Add(constref AValue: String): Boolean;
  12. begin
  13.   if (UTF8Length(AValue)>2) and (AValue = UTF8ReverseString(AValue)) then
  14.     Result:=inherited Add(AValue)
  15.   else
  16.     Result:=False;
  17. end;
  18.  

Usage:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   Palindromes: TPalindromeSet;
  4.   Words: TStringArray;
  5.   AWord: string;
  6. begin
  7.   Words := Memo1.Lines.Text
  8.            .ToLower
  9.            .Split([#9, #10, #13, ' ', ',', ';'], TStringSplitOptions.ExcludeEmpty);
  10.  
  11.   Palindromes := TPalindromeSet.Create();
  12.   try
  13.     for AWord in Words do
  14.       Palindromes.Add(AWord);
  15.  
  16.     for AWord in Palindromes do
  17.       Memo1.Append(AWord);
  18.  
  19.     if Palindromes.Count=0 then
  20.       Memo1.Append('no palindromes');
  21.   finally
  22.     SetLength(Words, 0);
  23.     Palindromes.Free;
  24.   end;
  25. end;

Probably adding words could be part of the class, AddWords. But the idea is simple, the class has to deal with its duty, only adding words that are palindromes. It makes it possible to focus on the real problem. This includes adding words from other languages.

Test string with English, Hebrew, Arabic and Chinese words.
Quote
ולכשתשכלו;BB;AA;CCC;MMM;AAA;MeeM:ABA;AABB;ABBA;BAAB;MAN;NAM;MAM;NAN;FAT;BAAB;FAAF;باب;文言文
« Last Edit: March 29, 2021, 10:07:24 pm by engkin »

Pluto

  • New Member
  • *
  • Posts: 29
Re: How to assign an anonymous function
« Reply #22 on: March 30, 2021, 04:44:03 am »
Thank you.  Well done.

Blade

  • Full Member
  • ***
  • Posts: 105
Re: How to assign an anonymous function
« Reply #23 on: March 30, 2021, 06:41:20 am »
I'm waiting for that ah-ha moment when I can start to figure out really simple things.... Every piece of documentation I can find seems to say this should work, yet I get a compilation error.  I feel dumb for asking, but why doesn't this work?  Is there some compiler directive I have to give to let this thing do anonymous functions?

You might want to check out this thread.  Very interesting debate, workarounds, and examples.

https://forum.lazarus.freepascal.org/index.php?topic=45818.0
(Functional programming in Pascal)

Quote
SymbolicFrank
What is the problem that anonymous functions solve?

Code: Pascal  [Select][+][-]
  1. function IsNegative(SomeParam: Integer): Boolean;
  2. var
  3.   SomeVar: Integer;
  4.  
  5.   function Calculate: Boolean;
  6.   begin
  7.     Result := False;
  8.     if SomeVar < 0 then Result := True;
  9.   end;
  10.  
  11. begin
  12.   Result := Calculate;
  13. end;
  14.  
« Last Edit: March 30, 2021, 07:18:27 am by Blade »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8343
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: How to assign an anonymous function
« Reply #24 on: March 30, 2021, 09:15:44 am »
Code: Pascal  [Select][+][-]
  1. function IsNegative(SomeParam: Integer): Boolean;
  2. var
  3.   SomeVar: Integer;
  4.  
  5.   function Calculate: Boolean;
  6.   begin
  7.     Result := False;
  8.     if SomeVar < 0 then Result := True;
  9.   end;
  10.  
  11. begin
  12.   Result := Calculate;
  13. end;
  14.  
One deficiency I admit I hope it wasn't there in the first place. It's a rare condition, but once you're trapped in, you might not realized where the problem is.

PascalDragon

  • Hero Member
  • *****
  • Posts: 3075
  • Compiler Developer
Re: How to assign an anonymous function
« Reply #25 on: March 30, 2021, 09:34:04 am »
With the appropriate helpers, I think the original code boils down to (not tested):
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   &Word: String;
  4. begin
  5.   LastWord := '';
  6.   for &Word in
  7.      lowercase(Memo1.Lines.Text)
  8.      .Split([#9, #10, #13, ' ', ',', ';'], TStringSplitOptions.ExcludeEmpty)
  9.      .Sort // could be a normal function, "for word in sort(memo....split) do"
  10.   do
  11.   begin
  12.     If &Word = LastWord then continue;
  13.     LastWord := &Word;
  14.     if (&Word.Length <=2) or (&Word <> ReverseString(&Word))  then continue;
  15.  
  16.     Memo1.Append(&Word);
  17.   end;
  18.  

IMHO: Ugly, with all that is going into the for loop header....

Sidenote: you don't need to escape Word as it's a type, not a keyword.

OwlOfTime

  • Hero Member
  • *****
  • Posts: 3827
Re: How to assign an anonymous function
« Reply #26 on: March 30, 2021, 04:51:38 pm »
Lately I had to rewrite 'functional' programming because it was slower than a traditional for loop.

The main problem is if you have a big array (100k objects) and what is being done in each .something.something you concat is to create a new array!!! Of course array of pointers but nevermind, it was slow and I get the same result with more lines of code but faster.

So think twice when one liners are best or not... Measure their speed.
Just Why?

Leledumbo

  • Hero Member
  • *****
  • Posts: 8343
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: How to assign an anonymous function
« Reply #27 on: March 30, 2021, 11:57:58 pm »
Lately I had to rewrite 'functional' programming because it was slower than a traditional for loop.

The main problem is if you have a big array (100k objects) and what is being done in each .something.something you concat is to create a new array!!! Of course array of pointers but nevermind, it was slow and I get the same result with more lines of code but faster.

So think twice when one liners are best or not... Measure their speed.
That's actually one core principle of (pure) functional programming: immutable objects. That's why (almost?) every functional programming languages are garbage collected.

avk

  • Sr. Member
  • ****
  • Posts: 417
    • my self-education project
Re: How to assign an anonymous function
« Reply #28 on: March 31, 2021, 05:41:13 am »
Lately I had to rewrite 'functional' programming because it was slower than a traditional for loop.

The main problem is if you have a big array (100k objects) and what is being done in each .something.something you concat is to create a new array!!! Of course array of pointers but nevermind, it was slow and I get the same result with more lines of code but faster.

So think twice when one liners are best or not... Measure their speed.

At least in LGenerics, the TGEnumearble class is just a wrapper over an enumerator and usually doesn't create any additional arrays. The exceptions are the Sorted(), Distinct() and sometimes Reverse() methods, but I just don't know how to implement them differently.

 

TinyPortal © 2005-2018