### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

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

#### avk

• Sr. Member
• Posts: 411
##### 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: 2721
##### 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: 726
##### 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: 411
##### 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
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: 2721
##### 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

• Hero Member
• Posts: 7098
• Debugger - SynEdit - and more
##### 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: 2721
##### 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
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
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.

• Jr. Member
• Posts: 99
##### 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: 8328
• 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: 2988
• 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.

#### lainz

• Hero Member
• Posts: 4044
• Leandro Diaz
##### 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.

#### Leledumbo

• Hero Member
• Posts: 8328
• 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: 411
##### 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.