【问题标题】:Haskell: Convert an expression into a list of InstructionsHaskell:将表达式转换为指令列表
【发布时间】:2013-11-25 15:22:47
【问题描述】:

这是一个家庭作业问题。

我的目标是将“”形式的类型表达式转换为 CPU 指令列表。给定数据结构

    data Expr = Const Double | Var String | Add Expr Expr | Mult Expr Expr

    data Instr = LoadImmediate RegisterNumber -- put value here
                               RegisterValue -- the value
               | Addition RegisterNumber -- put result here 
                          RegisterNumber -- first thing to add 
                          RegisterNumber -- second thing to multiply
               | Multiply RegisterNumber -- put result here 
                          RegisterNumber -- first thing to multiply 
                          RegisterNumber -- second thing to multiply
    type RegisterNumber = Int 
    type RegisterValue = Double

类型表达式有四个主要功能

Const: 将多个 double 类型转换为类型表达式,让你使用它。
Var: 基本上将字符串(即“x”)转换为类型表达式,让你将其应用于常量
然后是 AddMult 命令,让您可以添加和相乘两个类型表达式

我们可以假设我们将看到的唯一变量是“x”,它已经在寄存器 2 中。结果将到达寄存器 1。总共有 4 个寄存器。

所以Add Const 1 (Mult (Var "x") (Const 2)) 在 [Instr] 类型中将是

[LoadImmediate 3 1,
LoadImmediate 4 2,
Multiply 1 4 2,
Addition 1 3 1]

编辑:对不起,我不得不提一下,因为这是一个初学者课程,我们只需要考虑形式的表达

(Const a) `Add` ((Var "x") `Mult` Expr)

其中 'Expr' 是一些表达式。或数学形式 a0+x(a1+x(a2+x...))

我稍微修正了我的代码,现在我得到的错误是“不在范围内:数据构造函数'RegNum'”

expr2Code :: Expr -> [Instr]
expr2Code expr = e2C 1 expr
e2C:: Int -> Expr -> Instr
e2C RegNum (Const y) = [LoadImmediate RegNum y]
e2C RegNum (Var "x") = []
e2C RegNum (Add expr1 expr2) = e2C 3 expr1 ++ e2C 4 expr2 ++ [Addition RegNum 3 4]
e2C RegNum (Mult expr1 expr2) = e2C 3 expr1 ++ e2C 4 expr2 ++ [Multiply RegNum 3 4]

抱歉,帖子太长了,我们将不胜感激。

【问题讨论】:

    标签: haskell expression cpu-registers


    【解决方案1】:

    好吧,我假设您有无限数量的寄存器。如果不是,您可以体验寄存器溢出的乐趣,但您需要更多指令来处理动态内存。

    有 3 种简单的方法可以做到这一点

    1. 表达式 -> SSA -> 指令
    2. 表达式 -> CPS -> 指令
    3. 表达式 -> 指令

    前 2 个提供了更容易优化寄存器使用的机会,但涉及中间语言。既然我们很懒,那就做3吧。

    import Control.Monad.State
    
    type Gensym  = State Int
    
    gensym :: Gensym RegisterNumber
    gensym = modify (+1) >> fmap (subtract 1) get
    

    现在我们有了一种唯一生成寄存器的方法,我们可以采用这种效率极低的方法。

    withRegister :: (RegisterNumber -> [Instr]) -> Gensym (RegisterNumber, [Instr])
    withRegister f = gensym >>= \r -> return (r, f r)
    
    compile :: Expr -> Gensym (RegisterNumber, [Instr])
    compile (Const d)    = withRegister $ \r -> [LoadImmediate r d]
    compile (Var   "x")  = return (2, [])
    compile (Add e1 e2)  = do
      (t1, c1) <- compile e1 -- Recursively compile
      (t2, c2) <- compile e2 -- Each subexpression like you were
      withRegister $ \r -> Addition t1 t2 r : (c1 ++ c2) -- Make sure we 
                                                         -- use a unique register
    compile (Mult e1 e2)  = do
      (t1, c1) <- compile e1
      (t2, c2) <- compile e2
      withRegister $ \r -> Multiply t1 t2 r : (c1 ++ c2)
    
    compileExpr :: Expr -> [Instr]
    compileExpr = reverse . snd . flip evalState 3 . compile
    

    这基本上递归地编译每个表达式,将它们的各种代码块连接在一起。这与您所拥有的相似,但有 3 个主要区别,

    1. 我正在使用 monad 为我处理寄存器。 您必须确保永远不会破坏您将需要的寄存器,通过使用 monad 我确保我使用的所有寄存器都是唯一的。这确实效率低下,但微不足道。

    2. 在处理 Var 时,我只加载寄存器 2 中的任何内容,因为 LoadImmediate 需要双精度,而您没有在表达式中实际绑定变量的机制。

    3. 因为您不处理表达式,所以每个计算块都必须明确地保存在寄存器中。你不能再做x + y * z了。这就是为什么如果您查看AddMult 的代码,每个子表达式都会被编译到一个新的寄存器中。

    您的示例生成

    [LoadImmediate 4 2.0,Multiply 2 4 5,LoadImmediate 3 1.0,Addition 3 5 6]
    

    哪个是正确的,它将 4 和 x 相乘,然后再加上 3。

    【讨论】:

      【解决方案2】:

      e2C _ (Var "x") = LoadImmediate 2 "x"

      如果x 已经在寄存器 2 中,则根本不需要加载它。 Var "x" 不会转化为任何加载操作。相反,它在某些other 操作(加法或乘法)中转换为2 的操作数。例如,(Add (Const 25) (Var "x")) 将转换为 [LoadImmediate 3 25, Addition 1 3 2]

      e2C _ (Mult x y) = Multiply 4 (e2C _ x) (e2C _ y)

      这当然行不通,因为Multiply 的操作数是寄存器,而不是指令。您必须翻译x 并注意结果到哪个寄存器rx;然后翻译y 并注意ry its 结果到哪个寄存器;确保rx != xy;最后,发出Multiply rz rx ry

      现在,如何确定rz,以及如何确定rx != ry?一个简单的策略是确保每个结果都进入自己的寄存器(假设它们的数量是无限的,这对于真实的机器架构当然不是真的)。

      顶级结果将进入寄存器 1。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-05
        • 2018-01-29
        • 2011-03-05
        • 1970-01-01
        • 1970-01-01
        • 2012-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多