【问题标题】:Translating imperative to functional code将命令式转换为函数式代码
【发布时间】:2011-09-30 04:17:12
【问题描述】:

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

我想这可以通过在每个函数调用和返回时传递一个TheWorld 对象来完成,然后从那里优化,尝试从不使用它的函数中删除该参数等。但是有没有一种已知的更好的方法干吗?

【问题讨论】:

  • 出于兴趣,为什么需要这样做?
  • @Marcin,例如,它是静态分析的常用方法。顺便说一句,这是你的反对票吗?
  • 我要做的是自动代码分析、优化和并行化,为此我需要解决“这段代码计算什么函数”的问题,在我看来是等价的把它翻译成纯函数形式的问题。
  • @rwallace:似乎是一种合理的方法。这是否会以任何方式公开(因为我很想知道你的想法)?
  • 哦,对,如果超优化是您的目标,那么是的——我的目标只是映射,例如一个受限制的 Python 代码子集到 Haskell DSL,因此人们可以使用 Haskell 语义有效地编写,但使用 Python 的语法和一些更基本的习惯用法。

标签: functional-programming code-translation compiler-construction


【解决方案1】:

有多种方法可以有效地进行此类翻译。首先,值得做一个带有后续CPS 转换的SSA 转换:这样你就可以从带有变量和分支的命令式代码中得到一堆琐碎的相互递归函数。函数调用(甚至虚拟调用)也可以通过传递延续参数而不是依赖于隐式堆栈语义轻松地进行 CPS 编辑。

可以像处理变量一样处理数组,在 SSA 转换之前,所有数组访问都应替换为 getupdate 函数调用,它们应该具有隐式复制语义(但要注意在这个案例)。结构也一样。

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

【讨论】:

  • 赞成,似乎是一种合理的方法,虽然我不太明白一件事,是否有理由想要 CPS 一切?毕竟函数式编程通常依赖于隐式堆栈语义,不是吗?我以前没有亲自使用过 CPS,但我看到的参考文献通常表明它用于反向,将功能代码编译到命令式目标平台?
  • @rwallace,推理显式 CPS 而不是隐式堆栈要容易得多。并且 SSA 已经等同于 CPS,您只需推断值的活动范围即可从一个转换到另一个。
  • CPS 绝对比具有命令式编程语言所有机制的隐式堆栈更容易推理,但是 CPS 真的比纯粹的 lambda 术语更容易推理吗?如果是,为什么?
  • @rwallace,这主要是一个实际的观察。在任何时候都有一个明确的延续可以很容易地预测当前上下文中的哪些值被捕获并将被进一步使用。另一个原因是,如果您已经完成了 SSA 内存来注册升级,则需要再执行一个步骤来进行反向转换。
【解决方案2】:

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

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

两种语言由以下给出:

请参阅:“A Functional Perspective on SSA Optimisation Algorithms”,它提供了将 SSA 形式的程序自动转换为 ANF 的算法。

【讨论】:

    【解决方案3】:

    在许多函数式编程语言中,可以将一系列局部变量赋值替换为一系列 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;
    }
    

    Futhark 编程语言中的等效函数可以这样编写,使用记录数据结构来存储局部变量:

    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 中,这可以通过 replacing subterms 来完成:

    :- 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]
            ).
    

    还有几种方法可以使用单子或递归来implement while-loops

    【讨论】:

      猜你喜欢
      • 2012-07-02
      • 1970-01-01
      • 1970-01-01
      • 2016-01-08
      • 2012-10-20
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 2013-10-11
      相关资源
      最近更新 更多