Метод цепочки в Аде

У многих языков есть шаблон, в котором вы делаете что-то вроде этого:

object = Create_Object().Set(1).Set(2).Set(3);

(Я считаю, что это произошло в Smalltalk.) Это работает, потому что Set() Метод возвращает ссылку на свой получатель.

Могу ли я сделать что-нибудь подобное в Аде?

Подходы, которые я пробовал, которые не работают, включают:

-- doesn't work because the returned object is a read-only copy of the receiver
function Set(self: in out Object) return Object;

-- doesn't work because I can't return an access to a local variable
function Set(self: in out Object) return access Object;

-- looks like it works until I realise that it's not a method, and isn't doing
-- dynamic dispatch [*]
function Set(self: access Object) return access Object;

Можно ли это сделать?

3 ответа

Я думаю, что функция с этим профилем сделает работу за вас:

function Set (Target   : in Instance;
              New_Item : in Integer) return Instance;

Написав полный пакет вокруг этого объявления функции, я могу написать:

Object := Set (1).Set (2).Set (3);
Object.Show;

и получить вывод:

{1, 2, 3}

Я отправил полные источники на http://repositories.jacob-sparre.dk/miscellaneous-ada-2005-examples/.

Ваше третье предложение - правильный подход, и вы фактически определяете примитивную операцию, когда вводите "доступ" для параметра. Вот пример, который использует два теговых типа, чтобы показать, где происходит диспетчеризация, и использует цепочку вызовов методов. Я также предоставил процедуру для удобства, так как в противном случае вам пришлось бы использовать временную переменную. Я использовал Create_Object, но явный вызов "new", но это, конечно, то же самое. Кроме того, я показал пример, когда Ада не будет выполнять динамическую диспетчеризацию, когда она статически знает задействованные типы. Это на самом деле хорошие функции (с точки зрения производительности), даже если это действительно требует осторожности (и даже эксперты время от времени кусают:=)

with Utils; use Utils;    
procedure Chain is
   O : access Object := new Object;
   C : access Child := new Child;
begin
   O.Add (1).Add (2);
   C.Add (3).Add (4);
end Chain;

package Utils is    
   type Object is tagged null record;
   function Add (Self : access Object; Val : Integer) return access Object;
   procedure Add (Self : access Object; Val : Integer);  --  for convenience

   type Child is new Object with null record;
   overriding function Add (Self : access Child; Val : Integer) return access Child;
   overriding procedure Add (Self : access Child; Val : Integer);  --  for convenience
end Utils;

with Ada.Text_IO; use Ada.Text_IO;    
package body Utils is    
   function Add (Self : access Object; Val : Integer) return access Object is
   begin
      Put_Line ("func Object.Add" & Val'Img);
      Self.Add (Val);   --  static call, not dynamic dispatch
      return Self;
   end Add;

   procedure Add (Self : access Object; Val : Integer) is
   begin
      Put_Line ("proc Object.add" & Val'Img);
   end Add;

   overriding function Add (Self : access Child; Val : Integer) return access Child is
   begin
      Put_Line ("Child.Add" & Val'Img);
      Self.Add (Val);   --  static call, not dynamic dispatch
      return Self;
   end Add;

   overriding procedure Add (Self : access Child; Val : Integer) is
   begin
      Put_Line ("proc Child.Add" & Val'Img);
   end Add;   
end Utils;

Выход этой программы:

func Object.Add 1
proc Object.add 1
proc Object.add 2
Child.Add 3
proc Child.Add 3
proc Child.Add 4

Редактировать: как обсуждалось в комментариях, рекомендуется всегда использовать переопределение, чтобы убедиться, что процедура действительно переопределяет, а не использовать другой профиль. Параметр доступа создает примитивную операцию.

Из параграфа 18 параметры функции [формальные] могут использовать только режим "in" (только для чтения). Таким образом, входной объект не изменяется.

18 {mode mode} Параметр mode формального параметра передает направление передачи информации с фактическим параметром: in, in out или out. Mode in используется по умолчанию и является режимом параметра, определенного в access_definition. Формальные параметры функции, если таковые имеются, должны иметь режим в. [Ada 2005]

Поэтому я не думаю, что язык поддерживает это, как показано на других языках.

Однако, если вы не возражаете против копирования, мы можем сохранить результат обратно в объекте, но это не то же самое, что другие языки, на которые вы ссылаетесь.

obj := obj.set("this").set(123).set(some_other_type);

Вы устанавливаете "this" для копии "obj" и возвращаете эту копию. Из этой копии мы создаем новую копию и устанавливаем 123 в этой копии, которая, в свою очередь, возвращается. Наконец, последний результат копируется, и мы присваиваем ему значение some_other_type. Эта последняя копия возвращается, и мы можем сохранить ее в obj. Обратите внимание, что хорошо оптимизированная реализация может избежать большинства, если не всех этих копий, если set() Функции достаточно малы и встроены, но я бы не смог на это.

Оглядываясь назад, используя процедуры:

obj.set("this");
obj.set(123);
obj.set(some_other_type);

не будет делать никаких копий вообще.

Другие вопросы по тегам