Recent

Author Topic: Почему не работает рекурсивный вызов вложенных функций без параметров?  (Read 874 times)

wanderus

  • New Member
  • *
  • Posts: 10
Обнаружил неприятную проблемку. Не очень понимаю, это ошибка или так задумано.
Имеется код:
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
В опциях проекта отключил оптимизацию - не помогает.

wanderus

  • New Member
  • *
  • Posts: 10
P.S. Кажется, я догадываюсь, в чем проблема.
Вот эта строчка:
Code: Pascal  [Select]
  1. a[i] := TestRecurs;
  2.  
Если ее исправить на:
Code: Pascal  [Select]
  1. a[i] := TestRecurs();
  2.  
то рекурсия работает корректно.

Zoran

  • Hero Member
  • *****
  • Posts: 1468
    • http://wiki.lazarus.freepascal.org/User:Zoran
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).
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.
« Last Edit: December 03, 2019, 10:58:05 am by Zoran »

wanderus

  • New Member
  • *
  • Posts: 10
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

ASerge

  • Hero Member
  • *****
  • Posts: 1422
It would be great to have warnings about such ambiguities.
Agree. Especially since it is NOT compatible with Delphi.
In the documentation, 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.

440bx

  • Hero Member
  • *****
  • Posts: 1272
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}.


using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.