【问题标题】:Returning a function in Ada在 Ada 中返回一个函数
【发布时间】:2017-08-21 20:25:54
【问题描述】:

有没有可能,一个函数可以在 Ada 中返回一个函数?我正在努力让currying 工作。

type Integer_Func_Type is access function (Y : Integer) return Integer;

function Add (X : Integer) return Integer_Func_Type is
   function Inner (Y : Integer) return Integer is
   begin
      return X + Y;
   end Inner;
begin
   return Inner'Access;
end;

最后,我不想一次提供一个函数的所有参数。例如:如果x是三元函数,ycurry(x),那么我可以使用以下函数调用:y(a,b,c)y(a,b)(c)y(a)(b,c)y(a)(b)(c)

编辑

我实施了“Jacob Sparre Andersen”的建议。但看起来柯里化并不容易实现。我必须提前实现我想使用的任何类型的所有可能变体。这是正确的吗?

with Ada.Text_IO;

with R;

procedure Hello is
   Add_Two : R.Test2 := (X => 2);
begin
   Ada.Text_IO.Put_Line(Add_Two.Add(3)'Img);
end Hello;

r.adb

package body R is

   function Add(A : Test2; Y : Integer) return Integer is
   begin
      return A.X + Y;
   end Add;

end R;

r.ads

package R is

   type Test is abstract tagged null record;

   function Add(A : Test; Y : Integer) return Integer is abstract;

   type Test2 is new Test with
      record
         X : Integer;
      end record;

   overriding
   function Add(A : Test2; Y : Integer) return Integer;

end R;

【问题讨论】:

  • 我认为内部函数的访问规则/范围会有问题。
  • @DaleStanbrough 我明白了。你认为,currying 是可能的吗?
  • Ada 不进行类型推断,所以是的,您必须将所需的每种函数(参数和返回类型)声明为单独的标记类型。
  • Chris Okasaki 在Functional Programming in ...Ada? 中解决了这个问题。
  • 不,简而言之,您不能真正将函数绑定到仅存在于本地的环境(LISP 意义上),因此返回函数并且这种环境不是可能在艾达。但是,一些(如果不是大多数)编译器会通过提供非 Ada 属性 'Unrestricted_Access 让您承担风险。所以,如果你能延长这个函数(指针)所引用的对象的生命周期超出语言的要求......

标签: function ada currying


【解决方案1】:

这是使用泛型的方法:

with Ada.Text_IO;

procedure Test is
   --  shorthand Ada 2012 syntax; can also use full body
   function Add (X, Y : Integer) return Integer is (X + Y);

   generic
      type A_Type (<>) is limited private;
      type B_Type (<>) is limited private;
      type Return_Type (<>) is limited private;
      with function Orig (A : A_Type; B : B_Type) return Return_Type;
      A : A_Type;
   function Curry_2_to_1 (B : B_Type) return Return_Type;

   function Curry_2_to_1 (B : B_Type) return Return_Type is
     (Orig (A, B));

   function Curried_Add is new Curry_2_to_1
     (Integer, Integer, Integer, Add, 3);
begin
   Ada.Text_IO.Put_Line (Integer'Image (Curried_Add (39)));
end Test;

如您所见,它非常冗长。此外,您需要为原始函数的每个参数 X 和生成函数的每个参数 Y 提供一个柯里化实现,因此您将拥有很多 Curry_X_to_Y 函数。这是必要的,因为 Ada 没有可变参数泛型。

很多冗长也来自 Ada 没有进行类型推断:您需要明确指定 A_TypeB_TypeReturn_Type,尽管理论上可以从给定的原始函数推断出它们(这就是一些函数式编程语言可以)。

最后,您需要一个来自柯里化函数的命名实例,因为 Ada 不支持泛型函数的匿名实例。

因此,原则上,柯里化确实有效,但它不像 Haskell 这样的语言那么优雅。如果您只想针对特定类型进行柯里化,代码会显着缩短,但也会失去灵活性。

【讨论】:

    【解决方案2】:

    你不能完全按照你的意愿去做,因为一旦Add 返回,Inner 就会停止存在。

    您可以使用标记类型对您描述的效果做一些事情。

    一种带有与您的函数类型匹配的原始操作的抽象标记类型。

    然后是一个以X 为属性的派生标记类型和匹配Inner 的函数的实现。

    【讨论】:

    • 谢谢!我实施了你的建议。但我认为,真正的柯里化是不可能的:-(
    • 我很确定您可以通过泛型(子程序)进行柯里化。 (编辑以消除确定性!)。
    【解决方案3】:

    许多答案似乎都涉及如何让子程序处理可变数量的参数。处理此问题的一种方法是使用一系列值。例如,

    type Integer_List is array (Positive range <>) of Integer;
    function Add (List : Integer_List) return Integer;
    

    可以被认为是一个接受任意数量的整数类型参数的函数。如果所有参数都具有相同的类型,这很简单。如果您处理一组可能的参数类型,它会更复杂,但仍然可能:

    type Number_ID is (Int, Flt, Dur);
    type Number (ID : Number_ID) is record
       case ID is
       when Int =>
          Int_Value : Integer;
       when Flt =>
          Flt_Value : Float;
       when Dur =>
          Dur_Value : Duration;
       end case;
    end record;
    type Number_List is array (Positive range <>) of Number;
    function Add (List : Number_List) return Number;
    

    如果您必须能够处理事先不知道的类型,则此技术不适合。

    【讨论】:

    • 检查了一个使用 数组聚合 的相关示例here
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多