Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 连续传递样式中的中间值和返回值_C#_Lambda_Scheme_Continuations_Continuation Passing - Fatal编程技术网

C# 连续传递样式中的中间值和返回值

C# 连续传递样式中的中间值和返回值,c#,lambda,scheme,continuations,continuation-passing,C#,Lambda,Scheme,Continuations,Continuation Passing,我来自一个面向对象的、非功能性的背景,所以我很难完全直观地看到几个有关的在线示例。此外,Scheme之类的函数式语言不必指定参数类型或返回值,因此我不确定我的想法是否正确 由于C#支持lambdas,我从Wikipedia文章中选取了第一个示例,并尝试使用强类型将其移植到C#,以了解该模式将如何应用: // (Scheme) // direct function (define (pyth x y) (sqrt (+ (* x x) (* y y)))) // rewriten with

我来自一个面向对象的、非功能性的背景,所以我很难完全直观地看到几个有关的在线示例。此外,Scheme之类的函数式语言不必指定参数类型或返回值,因此我不确定我的想法是否正确

由于C#支持lambdas,我从Wikipedia文章中选取了第一个示例,并尝试使用强类型将其移植到C#,以了解该模式将如何应用:

// (Scheme)

// direct function
(define (pyth x y)
 (sqrt (+ (* x x) (* y y))))

// rewriten with CPS 
(define (pyth& x y k)
 (*& x x (lambda (x2)
          (*& y y (lambda (y2)
                   (+& x2 y2 (lambda (x2py2)
                              (sqrt& x2py2 k))))))))

// where *&, +& and sqrt& are defined to 
// calculate *, + and sqrt respectively and pass the result to k
(define (*& x y k)
 (k (* x y)))
因此,在C#中重写CPS
pyth&
版本导致:

// (C#6)

// continuation function signature
delegate double Cont(double a);

// *&, +& and sqrt& functions
static double MulCont(double a, double b, Cont k) => k(a * b);
static double AddCont(double a, double b, Cont k) => k(a + b);
static double SqrtCont(double a, Cont k) => k(Math.Sqrt(a));

// sqrt(x*x + y*y), cps style
static double PythCont(double x, double y, Cont k) =>
    MulCont(x, x, x2 =>
        MulCont(y, y, y2 =>
            AddCont(x2, y2, x2py2 =>
                SqrtCont(x2py2, k))));
我本可以使用泛型而不是double,但签名会更长。无论如何,我不确定的是:

  • 上面的
    Cont
    签名是否正确(即
    Func
    )?如果继续fn。接受参数,处理它,然后返回相同类型的值

  • 当我第一次开始阅读continuations时,我感觉这个continuation函数将在调用堆栈中的每个步骤中被调用,但在上面的示例中,它只传递给
    sqrt&
    ,所有其他调用都会得到lambda,而lambda并不会真正“传递”中间值给原始continuation。上面函数中的代码基本上类似于
    k(Math.Sqrt(x*x+y*y))
    ,这是否意味着我关于中间“挂钩”的假设是错误的

  • 是的,除非你想用最外层的延续来做任何非数值的事情,否则它是正确的。 只有当原始表达式包含更多类型时,才需要更多“Cont”,例如

    (定义(foo x)(如果(=x 0)1 0))

  • 在这种情况下,它可能看起来像这样(抱歉,为了简洁起见,我在scheme中写道):

    --现在,最外层的延续有一个数字(比如一个int)作为输入,而提供给“=&”的延续有bool->int类型

  • 您几乎是对的(直到二元性)——调用堆栈上的每一步现在都是对某个延续的调用。 一般来说,您可能会混淆第一类延续和CP——前者是一种语言特性(在scheme中,您可以使用call/cc运算符访问当前延续),后者是一种可以在任何地方使用的技术。 实际上,您可以将表达式转换为CP,而无需在您的语言中使用高阶函数(只需以某种方式表示它们)
  • 您询问的另一件事是cps与控制流的关系。请注意,在应用函数式语言(如scheme)中,您唯一指定的是,在应用程序中,首先计算操作数和运算符,然后将后者应用于前者。计算操作数的顺序无关紧要——可以从左到右、从右到左(或者以某种疯狂的方式)。但是如果您不使用纯函数样式,并且操作数会产生一些副作用,该怎么办?例如,它们可能会将某些内容打印到标准输出,然后返回一些值。在这种情况下,您希望控制订单。如果我记得清楚的话,使用gambit-C编译的程序从右到左计算参数,而使用gambit的解释器从左到右进行解释——因此问题确实存在;)。准确地说,cps可能会拯救你[实际上还有其他方法,但我们现在就在关注cps!]。 在您发布的方案示例中,强制从左到右计算“+”的参数。 你可以很容易地改变这一点:

    (define (pyth& x y k)
     (*& y y (lambda (y2)
              (*& x x (lambda (x2)
                       (+& x2 y2 (lambda (x2py2)
                                  (sqrt& x2py2 k))))))))
    
    这就是问题所在

    在一些进一步的应用程序中,正如guys在评论中所说的,转换到CPS会将每个应用程序移动到尾部位置,因此调用堆栈将被替换为lambdas,而且,如果将它们去功能化,会得到一个表示控制流的数据结构——一个要转换为的整洁表单,比如C,或者其他命令式语言。完全自动化! 或者,如果您想实现一些monad mumbo jumbo,比如说monad,在CPS中很容易,只需在每个延续lambda之前添加一个测试,测试接收的值是否“只是某个东西”(在这种情况下,执行作业并将结果推送到您的延续),或者“什么都没有”,在这种情况下,您只需推什么(到延续lambda)。 当然,要用另一个程序或宏,而不是手工,因为这可能会很乏味——cps最神奇的地方在于,它很容易自动转换为cps


    希望我没有让它变得不必要的复杂。

    我已经对延续单子做了一个非常全面的介绍,你可以在这里找到

    你也可以找到一把.Net小提琴

    我在这里概括地重复一遍 从初始函数开始

    int Square(int x ){return (x * x);}
    
  • 使用回调并删除返回类型
  • publicstaticvoidsquare(intx,动作回调)
    {
    回调(x*x);
    }
    
  • 讨好回拨
  • public static Action Square(int x)
    {
    return(callback)=>{callback(x*x);};
    }
    
  • 泛化返回的延续
  • public static Func Square(int x)
    {
    return(callback)=>{callback(x*x);};
    }
    
  • 提取延续结构,也称为单子的返回方法
  • 代表T Cont(函数f);
    公共静态继续(此U x)
    {
    返回(回调)=>回调(x);
    }
    正方形。继续()
    
  • 添加bind Monadic方法,从而完成Monad
  • publicstaticcontbind(
    本节,
    Func k,
    Func选择器)
    {
    返回(函数c)=>
    m(t=>k(t)(y=>c(选择器(t,y)));
    }
    
    我觉得很好。唯一的问题是它应该使用尾部调用。虽然这个例子很好,但是你的堆栈会随着更多的代码迅速膨胀。如果不知道你对钩子的直觉是什么,很难回答这个问题。使用CPS,被调用方可以多次调用延续,或者根本不调用,或者将其保存在某个地方以供以后调用。这个例子是fa
    int Square(int x ){return (x * x);}
    
        public static void Square(int x, Action<int> callback)
        {
        callback(x * x);
        }
    
        public static Action<Action<int>> Square(int x)
        {
         return (callback) => { callback(x * x); };
        }
    
        public static Func<Func<int,T>,T> Square<T>(int x)
        {
             return (callback) => { callback(x * x); };
        }
    
           delegate T Cont<U, T>(Func<U, T> f);
    
        public static Cont<U, T> ToContinuation<U, T>(this U x)
        {
           return (callback) => callback(x);
        }
    
        square.ToContinuation<Func<int, int>, int>()
    
        public static Cont<V, Answer> Bind<T, U, V, Answer>(
        this Cont<T, Answer> m,
        Func<T, Cont<U, Answer>> k, 
        Func<T, U, V> selector)
        {
         return (Func<V, Answer> c) => 
        m(t => k(t)(y => c(selector(t, y))));
        }