首页 > 解决方案 > 插入 let 绑定局部变量

问题描述

我正在阅读 Oleg Kiselyov 的教程Reconciling Abstraction with High Performance: A MetaOCaml approach。一个练习(练习 23)要求使用 let-insertion 将数组索引访问绑定到局部变量。question 的函数是vmult_ca,它生成用于将复数数组相乘的代码:

let vmult_ca : 
    (float_complex array -> float_complex array -> float_complex array -> unit)
    code =
 .<fun vout v1 v2 ->
    let n = Array.length vout in  
                                      (* vector representations *)
  .~(let vout = OVec (.<n>., fun i v -> 
       .<vout.(.~i) <- .~(of_code_complex v)>.) in
     let v1   = Vec  (.<n>., fun i -> 
       of_complex_code .<v1.(.~i)>.) in
     let v2   = Vec  (.<n>., fun i -> 
       of_complex_code .<v2.(.~i)>.) in
     let module V = VMULT(FloatCodeComplex)(VecDyn) in
     V.vmult vout v1 v2)
  >.
;;

vout存储结果的输出向量 在哪里。Vec (n, fun i -> v)是一个抽象向量,其中n是长度,并将fun i -> v每个索引映射到一个值。 OVec (n, fun i v -> body)是一个抽象的“输出向量”,其中n是长度并且fun i v -> body在每个索引i和相关输出元素v上运行iof_complex_code将一个complex code值转换为一个code complex值,例如.<{real=1.0, imag=0.0}>.转换为{real=.<1.0>., imag=.<0.0>.}. 该模块VMULT定义(逐点)向量乘法(有关详细信息,请参见此处的代码)。

运行时,vmult_ca生成以下代码:

val vmult_ca :
  (float_complex array -> float_complex array -> float_complex array -> unit)
  code = .<
  fun vout_4  ->
    fun v1_5  ->
      fun v2_6  ->
        let n_7 = Array.length vout_4  in
        for i_8 = 0 to n_7 - 1 do
          vout_4.(i_8) <-
            {
              Cmplx.im =
                (((v1_5.(i_8)).Cmplx.re *. (v2_6.(i_8)).Cmplx.im) +.
                   ((v1_5.(i_8)).Cmplx.im *. (v2_6.(i_8)).Cmplx.re));
              Cmplx.re =
                (((v1_5.(i_8)).Cmplx.re *. (v2_6.(i_8)).Cmplx.re) -.
                   ((v1_5.(i_8)).Cmplx.im *. (v2_6.(i_8)).Cmplx.im))
            }
        done>.

注意v1_5.(i_8)重复4次。let挑战是在某处插入一个vmult_ca绑定v1_5.(i_8)到局部变量以避免重复。genlet我可以通过简单地调用来“作弊” .<v1.(~i)>.,但我不知道在哪里插入letwithout genlet; 任何提示将不胜感激。

标签: ocamlmetaprogrammingmetaocaml

解决方案


Let-insertion 是BER 中的一个原始操作,它自动将传递的代码绑定到一个新生成的变量。

这是一个工作示例,假设您有返回数组元素平方的代码,

let power_elt xs i = xs.(i) * xs.(i)

我们想生成一个只有一个数组访问的优化代码

let power_elt xs i = let x = xs.(i) in x*x

在 MetaOCaml 样式中,我们可以使用genlet

let power_elt xs i =
  let x = genlet .<xs.(i)>. in 
  .<.~x * .~x>.

生成的代码

 let t = power_elt [|1;2;3|] 1;;

将会

 val t : int code = .<let lv_8 = Stdlib.Array.get (* CSP xs *) 1 in lv_8 * lv_8>.

推荐阅读