Functional programming 将命令式转换为函数式代码

Functional programming 将命令式转换为函数式代码,functional-programming,code-translation,compiler-construction,Functional Programming,Code Translation,Compiler Construction,我需要编写一个程序,将命令式代码转换为纯函数式。我不担心I/O—我想到了一些解决方案—但我确实需要处理堆对象和局部变量 我想可以通过在每次函数调用和返回时传递一个TheWorld对象,然后从中进行优化,尝试从未使用该参数的函数中删除该参数等来实现。但是,有没有已知的更好的方法呢?有很多方法可以有效地进行这种转换。首先,值得做一个带有结果转换的SSA转换:通过这种方式,您可以从带有变量和分支的命令式代码中获得一堆简单的相互递归函数。通过传递延续参数而不是依赖隐式堆栈语义,函数调用(甚至虚拟调用)也

我需要编写一个程序,将命令式代码转换为纯函数式。我不担心I/O—我想到了一些解决方案—但我确实需要处理堆对象和局部变量


我想可以通过在每次函数调用和返回时传递一个
TheWorld
对象,然后从中进行优化,尝试从未使用该参数的函数中删除该参数等来实现。但是,有没有已知的更好的方法呢?

有很多方法可以有效地进行这种转换。首先,值得做一个带有结果转换的SSA转换:通过这种方式,您可以从带有变量和分支的命令式代码中获得一堆简单的相互递归函数。通过传递延续参数而不是依赖隐式堆栈语义,函数调用(甚至虚拟调用)也可以很容易地进行CPS

数组的处理方式与变量相同,在SSA转换之前,所有数组访问都应替换为
get
update
函数调用,函数调用应具有隐式复制语义(但在这种情况下要注意别名)。结构也一样


只有在无法维护复制语义的情况下,您才需要有这个
TheWorld
对象,它应该保留所有分配的对象,并且在每次修改其中一个对象时都应该进行完整的复制。

正如SK logic指出的,您可以用SSA形式表示您的命令式程序

但是,您不必应用CPS转换,而是可以直接将命令式SSA表示转换为“管理范式”中的等效纯函数程序——一种基于Zadarnowski等人发布的算法的受限函数语言

这两种语言由以下内容提供:


请参阅:“,它给出了将SSA形式的程序自动转换为ANF的算法。

在许多函数式编程语言中,可以用一系列的
let
表达式替换一系列局部变量赋值

例如,此C函数可以这样翻译:

int example(int a,int b){
    a += 1;
    b += 2;
    if(a == 1){
        b += 1;
    }
    else if(b == 1){
        a += 1;
    }
    return a + b;
}
编程语言中的等效函数可以这样编写,使用记录数据结构存储局部变量:

let example a b =
    let vars = {a,b} in
    let vars = vars with a = vars.a + 1 in
    let vars = vars with b = vars.b + 2 in
    let vars = (if vars.a == 1 then
        let vars = vars with b = vars.b + 1 in
        vars
    else if b == 1 then
        let vars = vars with a = vars.a + 1 in
        vars
    else
        vars)
    in vars.a + vars.b
在某些情况下,还可以将一系列命令语句转换为单个算术表达式。在Prolog中,这可以通过以下方式完成:


还有几种使用单子或递归的方法。

出于兴趣,为什么需要这样做?@Marcin,例如,这是一种常见的静态分析方法。顺便说一句,这是你的否决票吗?我试图做的是自动代码分析、优化和并行化,为此我需要解决“这段代码计算的是什么函数”的问题,在我看来,这相当于将其转换为纯函数形式的问题。@rwallace:似乎是一种合理的方法。这会以任何方式公开吗(因为我很想看看你的想法)?哦,对了,如果超优化是你的目标,那么是的-我的目标就是简单地将Python代码的受限子集映射到Haskell DSL,因此,人们可以有效地使用Haskell语义编写代码,但要使用Python的语法和一些更基本的习惯用法。升级似乎是一种合理的方法,尽管有一点我不太明白,但有没有理由希望对所有内容进行CPS?毕竟,函数式编程通常依赖于隐式堆栈语义,不是吗?我个人以前没有使用过CPS,但我看到的对它的引用通常表明它是用于反向的,将功能代码编译到命令式目标平台?@rwallace,对显式CPS而不是隐式堆栈进行推理要容易得多。SSA已经等同于CPS,你只需要推断活跃度范围的值就可以从一个转换到另一个。CPS肯定比一个包含命令式编程语言所有机制的隐式堆栈更容易推理,但CPS真的比纯lambda术语更容易推理吗?如果是,为什么?@rwallace,这主要是一个实际观察。在任何一点上都有一个显式的延续可以很容易地预测,从当前上下文中捕获哪些值并将进一步使用。另一个原因是,如果您已经完成了SSA内存注册升级,则需要执行另一个步骤来执行反向转换。
:- use_module(prolog_vars_list).
:- set_prolog_flag(double_quotes, chars).
:- initialization(main).

main :- 
    To_solve = (Z=11,
    Z=Z*2,
    A=1+A,
    A=A+2,
    A = Z+1,
    A = A * 2,
    A=A+3+Z+P),

    run_imperative(To_solve,B),
    
    %print the input
    writeln(To_solve),
    
    %now print the output
    writeln(B).

run_imperative(A,B) :- imperative_to_declarative(A,_=B).

imperative_to_declarative((A,B,C),D1) :- 
imperative_to_declarative((B,C),D),imperative_to_declarative((A,D),D1).

imperative_to_declarative((A=A1,B=B1),(_=C)) :-
    replace(A,A1,B1,C).

replace(Subterm0, Subterm, Term0, Term) :-
        (   Term0 == Subterm0 -> Term = Subterm
        ;   var(Term0) -> Term = Term0
        ;   Term0 =.. [F|Args0],
            maplist(replace(Subterm0,Subterm), Args0, Args),
            Term =.. [F|Args]
        ).