首页 文章

ML多态函数

提问于
浏览
0

我试图在类型T上定义多态函数和,其中类型T可以是int,real或类型T的列表.int和real的情况的总和应该按预期工作 . 对于T列表的情况,它应该返回两个列表的相应元素的总和(列表的长度应该相同) .

例子:

sum (INT 2, INT 3) = INT 5

sum (REAL 2.3, REAL 3.4) = REAL 5.7

sum(L [2, 3, 4], L [3, 4, 5]) = L [5, 7, 9]

sum(L L([2, 3, 4], [2, 3, 4]), L ([3, 4, 5], [3, 4, 5]) = L ([5, 7, 9], [3, 4, 5])

我写的功能如下:

datatype T = INT of int | REAL of real | L of T list;


fun sum (x:T, x':T) = case (x, x') of

  (INT n, INT n') => INT (n + n')

| (REAL n, REAL n') => REAL (n + n')

| (L (x :: xs), L (y :: ys)) => L ((sum (x, y)) :: (sum (L xs, L 
                                                           ys))

| (_,_) => REAL (0.0);

但是对于上面的函数,我得到了错误:

构造函数应用于不正确的参数 .

expects: _ * [??? list]

but got: _ * [???]

in: :: (sum (x, y), sum (L xs, L ys))

unhandled exception: Fail: compilation aborted: parseAndElaborate reported errors

因此我通过添加nil来改变我的代码,如下所示 . 据我所知,错误的原因是cons运算符试图将T(INT或REAL)最终连接到T(INT或REAL)为(sum(x,y),sum(L xs) ,L ys))最终将通过递归调用INT或REAL来评估 . 因此,我最后通过添加nil(空列表)来更改我的代码

fun sum (x:T, x':T) = case (x, x') of

   (INT n, INT n') => INT (n + n')

 | (REAL n, REAL n') => REAL (n + n')

 | (L (x :: xs), L (y :: ys)) => L ((sum (x, y)) :: (sum (L xs, 
                                                 L ys)) :: nil)

 | (_,_) => REAL (0.0);

但是对于这种情况,它对INT和REAL的行为正确,但对于多态列表则不行 . 它对INT和REAL的行为正确(因为它们更容易实现) . 对于列表部分,我想cons运算符存在一些问题,我无法找出解决方案 . 我执行的测试用例及其输出如下:

sum (L([INT(1)]), L([INT(3)]));
val it = L [INT 4,L []] : T

sum (L([INT(1),INT(2)]), L([INT(3),INT(4)]));
val it = L [INT 4,L [INT #,L #]] : T

P.S:请忽略最后一种情况(,)=> REAL(0.0),因为我稍后会处理类型不匹配的情况 .

1 回答

  • 2
    • 这似乎是相互递归函数的一个很好的用例:
    datatype T = INT of int | REAL of real | L of T list
    
    fun sum (x, x') = case (x, x') of
       (INT  n, INT  n') => INT (n + n')
     | (REAL n, REAL n') => REAL (n + n')
     | (L   ns, L   ns') => L (sumLists (ns, ns'))
     | (_, _)            => ? (* mismatching types *)
    
    and sumLists (x::xs, y::ys) = sum (x, y) :: sumLists (xs, ys)
      | sumLists ([], []) = []
      | sumLists (_, _) = ? (* mismatching lengths *)
    
    • 由于类型不匹配导致 REAL 0.0 似乎是一个问题 .

    例如,为什么 sum (INT 2, L [INT 3]) 应该 REAL 0.0

    为什么 sum (INT 2, REAL 3.0) 应该 REAL 0.0

    如果对您的域有意义,可以考虑添加 INTREAL 的备用,或者更好的是,考虑将 sum 函数更改为可能返回一个总和,如果它可以在树的所有级别上有意义地计算,即 val sum : T * T -> T option . 它归结为错误处理 .

    • 编写描述角落案例预期行为的测试 . 特别是,当涉及对不具有相同类型的值求和时,以及对不匹配长度的列表求和 .

    您的示例看起来像测试:

    val test1 = sum (L [INT 1], L [INT 3])               = L [INT 4]
    val test2 = sum (L [INT 1, INT 2], L [INT 3, INT 4]) = L [INT 4, INT 6]
    

    除了 T 不是相等类型,因为它包含 real ,因此当您遇到 real 时,需要编写自己的使用epsilon test (nearlyEqual)的相等运算符,例如:

    fun eqT (INT x, INT y) = x = y
      | eqT (REAL x, REAL y) = nearlyEqual(x, y, someEps)
      | eqT (L (x::xs), L (y::ys)) = eqT (x, y) andalso eqT (L ys, L xs)
      | eqT (L [], L []) = true
      | eqT (_, _) = false
    

    你的一些角落情况可能看起来像

    val case1 = sum (INT 2, REAL 3.0)
    val case2 = sum (INT 2, L [])
    val case3 = sum (INT 2, L [INT 3])
    val case4 = sum (L [INT 1], L [INT 1, INT 2])
    val case5 = sum (L [INT 1], L [INT 1, REAL 2.0])
    val case6 = sum (L [], L [L []])
    

相关问题