为递归数据结构自动生成差异pp
Automatically Generate difference pp for recursive data structures
OUnit framework 有一个函数 assert_equal
可以(除其他外)接受参数 pp_diff
以更易读的方式格式化两个输入的差异。由于数据结构在现实世界的应用程序中变得相当大,这似乎非常有用。
然而,手动实现这一点似乎也很乏味(尤其是在开发过程中,数据结构可能经常更改)。所以想知道有没有办法(最好是基于ppx的)生成这样的函数?
如果 OCaml 中(还)没有这样的东西,那么在通常的嫌疑人(例如 Haskell、Lisp)中是否有任何相关的东西可以被移植甚至只是用于灵感(因为我没有甚至对如何开始这样的实施有丝毫的暗示)?换句话说:如何为相互递归的功能数据结构生成有意义的差异漂亮打印机?
将数据结构转换为人类可读形式(然后对该表示进行操作)的一种方法是使用 Core.Std
s-expressions;它们基本上是在 LISP s-expressions 之后建模的,并且 Core.Std
具有将数据与 s-expressions 相互转换的功能(以及一个语法扩展来自动化大部分无聊的部分)。您可以在 Real World OCaml 的第 17 章中找到很好的概述。
对于您的应用程序来说最重要的是,已经有计算 s-expressions 之间差异的功能(您也可以 pretty-print 它们,然后对它们使用普通的文本差异)。此功能可在 Core_extended.Std.Sexp.Diff
.
中找到
示例:
open Core.Std (* basic Sexp functionality. *)
module XSexp = Core_extended.Std.Sexp (* for Sexp diffs. *)
type example = {
a: string;
b: int;
c: float;
} with sexp (* syntax extension *)
let v1 = { a = "foo"; b = 1; c = 3.14 }
let v2 = { a = "bar"; b = 2; c = 3.14 }
let print_sexp s = print_endline (Sexp.to_string_hum s)
let sexp_diff s1 s2 =
match XSexp.Diff.of_sexps ~original:s1 ~updated:s2 with
None -> ""
| Some(diff) -> XSexp.Diff.to_string diff
let main () =
let s1 = sexp_of_example v1 in
let s2 = sexp_of_example v2 in
print_endline "=== first sexp ===";
print_sexp s1;
print_endline "=== second sexp ===";
print_sexp s2;
print_endline "=== print_diff output ===";
XSexp.print_diff ~oc:stdout ~original:s1 ~updated:s2 ();
print_endline "=== sexp_diff output ===";
print_endline (sexp_diff s1 s2)
let () = main ()
这里,print_diff
是一个预定义函数,用于将差异打印到通道,sexp_diff
是一个简单的自定义函数,使用 Sexp.Diff
到 return 差异作为一个字符串。使用 corebuild -pkg core_extended example.native
(或 .byte
,或使用带有必要参数的 ocamlbuild)构建程序后,运行 程序应产生以下输出:
=== first sexp ===
((a foo) (b 1) (c 3.14))
=== second sexp ===
((a bar) (b 2) (c 3.14))
=== print_diff output ===
a
- foo
+ bar
b
- 1
+ 2
=== sexp_diff output ===
a
- foo
+ bar
b
- 1
+ 2
OUnit framework 有一个函数 assert_equal
可以(除其他外)接受参数 pp_diff
以更易读的方式格式化两个输入的差异。由于数据结构在现实世界的应用程序中变得相当大,这似乎非常有用。
然而,手动实现这一点似乎也很乏味(尤其是在开发过程中,数据结构可能经常更改)。所以想知道有没有办法(最好是基于ppx的)生成这样的函数?
如果 OCaml 中(还)没有这样的东西,那么在通常的嫌疑人(例如 Haskell、Lisp)中是否有任何相关的东西可以被移植甚至只是用于灵感(因为我没有甚至对如何开始这样的实施有丝毫的暗示)?换句话说:如何为相互递归的功能数据结构生成有意义的差异漂亮打印机?
将数据结构转换为人类可读形式(然后对该表示进行操作)的一种方法是使用 Core.Std
s-expressions;它们基本上是在 LISP s-expressions 之后建模的,并且 Core.Std
具有将数据与 s-expressions 相互转换的功能(以及一个语法扩展来自动化大部分无聊的部分)。您可以在 Real World OCaml 的第 17 章中找到很好的概述。
对于您的应用程序来说最重要的是,已经有计算 s-expressions 之间差异的功能(您也可以 pretty-print 它们,然后对它们使用普通的文本差异)。此功能可在 Core_extended.Std.Sexp.Diff
.
示例:
open Core.Std (* basic Sexp functionality. *)
module XSexp = Core_extended.Std.Sexp (* for Sexp diffs. *)
type example = {
a: string;
b: int;
c: float;
} with sexp (* syntax extension *)
let v1 = { a = "foo"; b = 1; c = 3.14 }
let v2 = { a = "bar"; b = 2; c = 3.14 }
let print_sexp s = print_endline (Sexp.to_string_hum s)
let sexp_diff s1 s2 =
match XSexp.Diff.of_sexps ~original:s1 ~updated:s2 with
None -> ""
| Some(diff) -> XSexp.Diff.to_string diff
let main () =
let s1 = sexp_of_example v1 in
let s2 = sexp_of_example v2 in
print_endline "=== first sexp ===";
print_sexp s1;
print_endline "=== second sexp ===";
print_sexp s2;
print_endline "=== print_diff output ===";
XSexp.print_diff ~oc:stdout ~original:s1 ~updated:s2 ();
print_endline "=== sexp_diff output ===";
print_endline (sexp_diff s1 s2)
let () = main ()
这里,print_diff
是一个预定义函数,用于将差异打印到通道,sexp_diff
是一个简单的自定义函数,使用 Sexp.Diff
到 return 差异作为一个字符串。使用 corebuild -pkg core_extended example.native
(或 .byte
,或使用带有必要参数的 ocamlbuild)构建程序后,运行 程序应产生以下输出:
=== first sexp ===
((a foo) (b 1) (c 3.14))
=== second sexp ===
((a bar) (b 2) (c 3.14))
=== print_diff output ===
a
- foo
+ bar
b
- 1
+ 2
=== sexp_diff output ===
a
- foo
+ bar
b
- 1
+ 2