元组访问:找不到固定的记录类型

tuple access: Can't find a fixed record type

我编写了一个应该接收元组列表的函数。我使用 # 访问元组的组件并且代码编译:

fun recheck ([], n) = []
  | recheck (h::t, n) = 
    if ((#1 h) *  (#1 h)) +  ((#2 h) * (#2 h)) = n then
    h::recheck(t, n)
    else
    recheck(t, n)

但是另一个基本上做同样事情的函数,即接收元组列表并访问它们,会导致错误。

fun validate ([]) = true
  | validate (h::t)  = 
    if 1 = (#1 h) then
    true
    else
    false

Can't find a fixed record type.   Found near #1

这里有什么区别,为什么后者会报错?

编辑

第一个函数实际上并不能自行编译。

但是整个代码段确实如此:

fun drop ([], n) = []
  | drop (h::t, 0) = h::t
  | drop (h::t, n) =
    drop(t, n-1) 

fun sts_linear (y, n) =
  if y < (Math.sqrt(n)+1.0) then
      let
      (* x^2 + y^2 = n => x = sqrt(n-y^2) *)
      val x = Math.sqrt(n - (y * y));
      val xr  = Real.realRound(x);
      in
  if (abs(x - xr) < 0.000000001)  then
          [(Real.trunc xr, Real.trunc y)]@sts_linear (y+1.0, n)
      else
          (
        []@sts_linear (y+1.0, n)
          )
      end
  else []

fun recheck ([], n) = []
  | recheck (h::t, n) =
    if ((#1 h) *  (#1 h)) +  ((#2 h) * (#2 h)) = n then
    h::recheck(t, n)
    else
    recheck(t, n)

fun sts (n) =
  (
    let
        val pairs = sts_linear(0.0, Real.fromInt n);
    in
        recheck(drop(pairs, Real.ceil( Real.fromInt (length(pairs))/2.0 ) ), n)
    end
  )

您的第一个代码 不会 编译,至少 SML/NJ:

如果你让它编译,那么它一定是在 SML 的非标准扩展中。

您的两个定义的问题在于,在 SML 中没有 tuple 任意元数的多态概念。您可以编写函数来处理对列表。您可以编写函数来处理三元组列表。但是——您不能编写同时处理成对列表和三元组列表的函数(至少如果您的函数尝试将这些 pairs/triples 作为元组处理)。

一个解决方案是摆脱 # 并使用模式匹配来提取组件:

fun validate [] = true
|   validate ((x,y)::t)  = 
        if x = 1 then
            true
        else
            false

但是,如果您真的想编写一个可以多态地应用于成对列表或三元组列表(或四元组,...)的函数,最简单的方法是表示成对、三元组、等作为列表而不是元组。包含未指定大小列表的列表在 SML 中不是问题。

尽量减少这种情况,因为我在 SML/NJ 中看到了以下工作 而且我不知道它实际上是一个编译器扩展

val p1 = {x=0, y=0};
val p2 = {x=1, y=1};
val p3 = {x=1, y=1, z=1};

从编译器错误的角度来看,存在一个笨拙的结构 没有多少语言有以这种方式工作的错误, 因为该函数有效,但会产生类型错误 除非存在对函数的调用来解决 'record' 的类型,因此要解决错误,必须添加更多代码。

fun getFoo(field) = fn record => field record;

没有实际调用 getX 编译器无法确定 record 的类型 其中ALL字段的完整类型信息 编译器必须知道记录的一部分,而不仅仅是 #x 字段。

let val getX = getFoo(#x);
    val x1 = getX(p1);
    val x2 = getX(p2);
    val x3 = getFoo(#x)(p3);
in () end;

而以下注释掉的片段会导致错误,因为 p1 和 p3 不同,因此对 getFoo 的调用不同 需要

(*
let val getX = getFoo(#x);
    val x1 = getX(p1);
    val x3 = getX(p3);
in () end;
*)

以下是不够的,因为它永远不会解析记录。

let val getX = getFoo(#x) in () end;