SML:删除列表的前 n 个元素

问题描述

所以,我知道有一个内置函数可以删除元素,但是如果您希望删除最后 n 个元素,它可以工作。 我想删除前 n 个元素。
一个例子:

  > drop([1,2,3,4],2)   
    > [1,2]

我写了这段代码

exception Subscription;

val counter = 0;

fun drop (l,0) = l 
| drop([],n) = raise Subscription
| drop(h::x,n) = if n<0 then raise Subscription
 else if(counter <n) then  
     let 
         val counter = counter +1
     in 
         (h:: drop(x,n))
     end    
 else nil
 ;    


drop([1,2);

它不会被编译。我得到的错误信息是:

   uncaught exception Subscription
    raised at: dropper.ml:6.23-6.35

感谢任何想法和帮助。

解决方法

您误解了 val 声明的含义。它确实 不声明一个您可以在之后更改其内容的变量 就像在命令式编程语言中一样。基本上:

  • 正如@kopec 所提到的,你的测试 if(counter < n) 毫无意义 因为它减少到 if(0 < n),因为 counter 被设置为 0,即, 放在后面的 val counter = counter + 1 对此没有影响 测试

  • 声明 val counter = counter + 1 没有用。它 基本上意味着“现在 counters 被设置为 counters + 1(所以 1)only in ... end 块后面”,它根本没有使用。

因此您的代码等效于:

exception Subscription;

val counter = 0;

fun drop (l,0) = l 
| drop([],n) = raise Subscription
| drop(h::x,n) = if n<0 then raise Subscription
 else if(0 <n) then (h:: drop(x,n))
 else nil
 ;    

drop([1,2,3,4],2);

因此,很明显,使用您的示例列表,您将继续执行 递归调用 ((h:: drop(x,n))) 直到到达第三个子句 引发异常的函数。

在这种情况下,解决问题的正确方法是 使用递归函数的参数:

exception Subscription;

fun drop(l,0) = l 
|   drop([],n) = raise Subscription
|   drop(h :: x,n) =
   if n < 0 then raise Subscription
   else (h :: drop(x,n - 1)) 
 ;

drop([1,2);

因此,如果 n 大于或等于输入列表的长度,您 最终会落在函数的第一个子句中。

(很高兴看到您开始熟悉模式匹配:))