线性化表达式语法树

如何解决线性化表达式语法树

我定义了一个这样的递归类型

type t = | Param of int | Add of t * t

它允许我写这样的符号表达式

let x = Param(1)
let y = Param(2)
let z = Add(x,y)
# z;;
- : t = Add (Param 1,Param 2)

或者更复杂的东西

let rec fib n x y = match n with
  | 0 -> (x,y)
  | _ -> (fib (n - 1) y (Add(x,y)))      
let z5 = fib 5 x y
# z5;;
- : t * t =
(Add (Add (Param 2,Add (Param 1,Param 2)),Add (Add (Param 1,Param 2),Add (Param 2,Param 2)))),Add (Add (Add (Param 1,Param 2))),Add (Add (Param 2,Param 2))))))

现在我想把这个递归的东西转换成一个线性形式,其中每个唯一的 Param 和 Add wold 只出现一次——就像这样:

(Step 0,(Param 1))
(Step 1,(Param 2))
(Step 2,(Add (Step 0,Step 1))
(Step 3,(Add (Step 1,Step 2))
(Step 4,(Add (Step 2,Step 3))
...

这个转换叫什么?以及如何实施?

解决方法

我通常称其为展平或展开,其他一些常用名称(有时过于具体)是 three address codeA-normal form

使用变量赋值来表示步骤可能更自然,而不是使用 Step 概念,例如,

type var = int
type const = int
type stmt = Set var * const
type expr = Cst of int | Var of int | Add of expr * expr | ...

这样你的扁平化纤维就会看起来像

Set (0,(Add (Cst 1,Cst 2)))
Set (1,(Add (Cst 2,Var 0)))
Set (2,(Add (Var 0,Var 1)))
...

使用类型为 int -> expr -> stmt list -> int * expr * stmt list 的递归函数,例如,(未经测试),

let rec flatten v exprs stmts = match expr with
  | Cst _ | Var _ as expr -> v,expr,stmts
  | Add (x,y) -> 
    let v,x,stmts = flatten v x stmts in
    let v,y,stmts = flatten v x stmts in
    v+1,Var (v+1),(Set (v+1,Add(x,y)) :: stmts)

v 参数是变量名的新生成器(我们只用整数表示)。

此外,如果您想了解扁平化在现实世界中的 AST 是如何工作的,这里是 an example

实现散列约束和扁平化

首先,我们需要定义 AST 的哈希约束表示,例如,

type exp = Cst of int | Ref of int | Add of hexp * hexp
and hexp = {ref : int; exp : exp}

我们用唯一的序数索引每个散列约束的表达式,以便物理上相等的表达式将具有相同的 ref 编号。我们需要这样才能将表达式存储在有序的数据结构中,例如映射和集合(OCaml 不允许我们按物理地址对值进行排序,这是有道理的,因为 OCaml 使用分代 GC,因此值的地址会发生变化随着时间的推移)。

现在,让我们编写 hashcons 函数来对表达式进行散列运算,例如,

let hashes = Hashtbl.create 100
let hashcons exp =
  match Hashtbl.find_opt hashes exp with
  | None ->
    let ref = Hashtbl.length hashes + 1 in
    Hashtbl.add hashes exp ref;
    {ref; exp}
  | Some ref -> {ref; exp}

现在我们可以编写使用 hashconsed 表示的 fib 函数,

let rec fib n x y = match n with
  | 0 -> (x,y)
  | _ -> fib (n - 1) y (hashcons (Add (x,y)))

let x = hashcons@@Cst 1
let y = hashcons@@Cst 2
let _,z = fib 999 x y

没有大的变化。现在,让我们编写 flatten 函数。但在此之前,我们需要对程序进行一些表示,

module Program = Map.Make(Int)

let set id x = Program.update id (function
    | None -> Some x
    | x -> x)

let rec get ref prog = match Program.find_opt ref prog with
  | None -> None
  | Some _ -> Some {ref; exp = Ref ref}

let (++) exp prog = {ref=exp.ref; exp=Ref exp.ref},set exp.ref exp prog

我们的程序是从引用号(它既像变量名又像定义变量的地方——我们的表示中有一个内置的 SSA 属性)的映射,最后,我们的 flatten 函数是,>

let unify {ref} exp prog =
  {ref; exp=Ref ref},set ref {ref; exp} prog

let rec flatten input prog =
  match get input.ref prog with
  | Some exp -> exp,prog
  | None -> match input.exp with
    | Cst _ | Ref _ -> input ++ prog
    | Add (x,y) ->
      let x,prog = flatten x prog in
      let y,prog = flatten y prog in
      unify input (Add (x,y)) prog

它是线性的,因为当我们展平一个复杂的表达式时,我们使用 unify 并将该表达式的展平形式存储在其 id 下。以便下次我们看到相同的复杂表达式时,我们可以轻松地从程序中提取它已经扁平化的版本,使用整数键进行查找。

输出是,

# let p = snd@@flatten z Program.empty
val p : hexp Program.t = <abstr>
# print_program p;;
#1 := 1
#2 := 2
#3 := #1 + #2
#4 := #2 + #3
#5 := #3 + #4
#6 := #4 + #5
<snip>
#1001 := #999 + #1000
- : unit = ()

其中 print_program 函数定义为,

let rec pp_exp ppf {exp} = match exp with
  | Cst x -> Format.fprintf ppf "%d" x
  | Ref x -> Format.fprintf ppf "#%d" x
  | Add (x,y) -> Format.fprintf ppf "%a + %a" pp_exp x pp_exp y

let print_program prog =
  Program.to_seq prog |>
  Seq.iter @@ fun (id,exp) ->
  Format.printf "#%d := %a@\n" id pp_exp exp

扁平化功能的工作速度非常快,但一如既往它不是免费的。 fib 的哈希约束版本现在要慢得多,因为我们必须对每个表达式进行哈希约束,并且在此期间我们必须查找哈希表并在结构上比较大值。 fib 函数未利用表达式的哈希约束表示,因此可以编写更高效的 fib 函数。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res