Lazarus

Other Languages => Russian => Topic started by: wanderus on December 03, 2019, 10:04:13 am

Title: Почему не работает рекурсивный вызов вложенных функций без параметров?
Post by: wanderus on December 03, 2019, 10:04:13 am
Обнаружил неприятную проблемку. Не очень понимаю, это ошибка или так задумано.
Имеется код:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.btnRecursClick(Sender: TObject);
  2. var i:integer;
  3.     a:array[0..9] of integer;
  4.  
  5.   function TestRecurs:integer;
  6.   var mm: integer;
  7.   begin
  8.     if i>=9 then begin
  9.       result := -1;
  10.       Exit;
  11.     end;
  12.     Inc(i);
  13.     result := i;
  14.     Memo1.Lines.Add('TestRecurs '+IntToStr(i));
  15.     a[i] := TestRecurs;
  16.   end;
  17.  
  18.   function TestRecursDummy(dummy:integer):integer;
  19.   var mm: integer;
  20.   begin
  21.     if i>=9 then begin
  22.       result := -1;
  23.       Exit;
  24.     end;
  25.     Inc(i);
  26.     result := i;
  27.     Memo1.Lines.Add('TestRecursDummy '+IntToStr(i));
  28.     a[i] := TestRecursDummy(1);
  29.   end;
  30.  
  31. begin
  32.    i:=0;
  33.    ShowMessage(IntToStr(TestRecurs));
  34.    i:=0;
  35.    ShowMessage(IntToStr(TestRecursDummy(1)));
  36. end;
  37.  
Тут видно две идентичные вложенные функции TestRecurs и TestRecursDummy, которые отличаются только тем, что во второй функции есть фиктивный параметр.
Так вот, TestRecurs этим кодом вызывается всего 1 раз, а TestRecursDummy - 9 раз. То есть после выполнения Memo1 содержит:

TestRecurs 1
TestRecursDummy 1
TestRecursDummy 2
TestRecursDummy 3
TestRecursDummy 4
TestRecursDummy 5
TestRecursDummy 6
TestRecursDummy 7
TestRecursDummy 8
TestRecursDummy 9

Используется стандартный Lazarus 2.0.4 под Win32
В опциях проекта отключил оптимизацию - не помогает.
Title: Re: Почему не работает рекурсивный вызов вложенных функций без параметров?
Post by: wanderus on December 03, 2019, 10:08:47 am
P.S. Кажется, я догадываюсь, в чем проблема.
Вот эта строчка:
Code: Pascal  [Select][+][-]
  1. a[i] := TestRecurs;
  2.  
Если ее исправить на:
Code: Pascal  [Select][+][-]
  1. a[i] := TestRecurs();
  2.  
то рекурсия работает корректно.
Title: Re: Почему не работает рекурсивный вызов вложенных функций без параметров?
Post by: Zoran on December 03, 2019, 10:56:18 am
P.S. Кажется, я догадываюсь, в чем проблема.
Вот эта строчка:
Code: Pascal  [Select][+][-]
  1. a[i] := TestRecurs;
  2.  
Если ее исправить на:
Code: Pascal  [Select][+][-]
  1. a[i] := TestRecurs();
  2.  
то рекурсия работает корректно.

Ah, it is that the function implicitely creates "result variable", named the same as function name (see https://www.freepascal.org/docs-html/current/ref/refse90.html (https://www.freepascal.org/docs-html/current/ref/refse90.html)).
Therefore, when function have no parameters, there is ambiguity between this "result variable" and recursive call, which by default compiler resolves to "result variable". By adding parentheses, you tell the compiler explicitly that it is function call.
Title: Re: Почему не работает рекурсивный вызов вложенных функций без параметров?
Post by: wanderus on December 03, 2019, 02:01:33 pm
Thanks. I already remembered this feature.  It would be great to have warnings about such ambiguities.
It’s a good idea to always use parentheses for the future like C/C++ function calls
Title: Re: Почему не работает рекурсивный вызов вложенных функций без параметров?
Post by: ASerge on December 03, 2019, 07:35:30 pm
It would be great to have warnings about such ambiguities.
Agree. Especially since it is NOT compatible with Delphi.
In the documentation (https://www.freepascal.org/docs-html/current/ref/refse78.html#x141-16300012.2), however, it is said: Sometimes, the call is desired, for instance in recursion in that case, the call must be forced. This can be done by adding the parenthesis to the function name.
Title: Re: Почему не работает рекурсивный вызов вложенных функций без параметров?
Post by: 440bx on December 03, 2019, 08:10:21 pm
It’s a good idea to always use parentheses for the future like C/C++ function calls
Yes, it is.  Without the parentheses, in a assignment such as "a := somename;" it is not possible to know if "somename" is a function call or a variable just by reading that single statement.

I believe it would a very nice feature if FPC required the () for function calls (and procedures, for consistency's sake) depending on the setting of a compiler switch, e.g, {$EXPLICITCALLS ON}.


TinyPortal © 2005-2018