SML,使用文件夹定义列表的最小值

时间:2019-03-05 20:49:38

标签: sml

我需要使用文件夹查找列表的最小值。

这是我编写的代码:

fun minlist nil = nil
  | minlist (x::xs) = List.foldr (fn (y,z) => if y < z then y else z) x xs;

但是我遇到一个错误:“重载>无法应用于类型为“列表”的参数”

我被困了一段时间。感谢您的帮助

2 个答案:

答案 0 :(得分:1)

您的第一个子句说空列表的最小值是一个列表。
因此,(fn (y,z) => if y < z then y else z)产生一个列表,yz也必须是列表。

对于空列表,您无法产生任何有意义的值,因此您应该删除这种情况并接受编译警告,或者引发异常。

答案 1 :(得分:0)

找到两个值中的最小值

您的表达式if y < z then y else z具有内置名称Int.min (y, z)

处理空列表

您将空列表处理为minlist nil = nil,这意味着“空int列表中最小的int是空列表”。但是空列表不是int,因此不能是int列表中的元素,也不能是否则返回最小int的函数的返回值。

就像molbdnilo所说的那样,您可以忍受编译警告(如果向函数提供空列表,则可能在运行时引发Match异常),或者引发特定的异常,例如{{ 1}}给出空列表时。两者都不是好事,但后者至少可以使问题清楚。

在不使用Empty的情况下编写此代码可能看起来像这样:

foldr

将递归函数转换为折叠

给出一些依赖于某些函数fun minimum [] = raise Empty | minimum [x] = x | minimum (x::xs) = Int.min (x, minimum xs) 和某些默认值foo的递归函数bar

acc

您可能会注意到fun foo [] = acc | foo (x::xs) = bar (x, foo xs) minimum之间的相似之处:

  • fooacc,是一些最小值
  • xbar

这是尝试概括Int.min的递归方案。

给出函数minimum

foldr

您可能会注意到相同的地方:

  • fun foldr f e [] = e | foldr f e (x::xs) = f (x, foldr f e xs); f,但被设置为参数
  • bare,但被设置为参数

acc中唯一不适合此一般递归方案的是处理空列表。因此,您仍然必须将其与minimum分开:

foldr

但是其余的都差不多。

可识别错误的返回类型

第三个选择是将函数的类型签名更改为

fun minimum [] = ...
  | minimum (x::xs) = foldr ...

您当前的练习可能不允许的

在不使用val minimum : int list -> int option 的情况下编写此代码可能看起来像这样:

foldr

或更妙的是:

fun minimum [] = NONE
  | minimum [x] = SOME x
  | minimum (x::xs) =
    case minimum xs of
         NONE => SOME x
       | SOME y => SOME (Int.min (x, y))

将此功能转换为使用fun minimum [] = NONE | minimum [x] = SOME x | minimum (x::xs) = Option.map (fn y => Int.min (x, y)) (minimum xs) 的过程是相同的,但是使用不同的foldr

尾递归

f函数不折叠(从上面重复):

minimum

有一个问题是它主要使用stack memory

可以通过手动评估功能来说明:

fun minimum [] = raise Empty
  | minimum [x] = x
  | minimum (x::xs) = Int.min (x, minimum xs)

由于无法在递归调用返回之前计算外部 minimum [1,2,3,4,5] ~> Int.min (1, minimum [2,3,4,5]) ~> Int.min (1, Int.min (2, minimum [3,4,5])) ~> Int.min (1, Int.min (2, Int.min (3, minimum [4,5]))) ~> Int.min (1, Int.min (2, Int.min (3, Int.min (4, minimum [5])))) ~> Int.min (1, Int.min (2, Int.min (3, Int.min (4, 5)))) ~> Int.min (1, Int.min (2, Int.min (3, 4))) ~> Int.min (1, Int.min (2, 3)) ~> Int.min (1, 2) ~> 1 ,因此用于计算函数的堆栈内存量与列表的长度成正比。

您可以通过使用累加参数来避免这种情况:

Int.min

手动评估此功能:

fun minimum [] = raise Empty
  | minimum (y::ys) =
    let fun helper [] acc = acc
          | helper (x::xs) acc = helper xs (Int.min (x, acc))
    in helper ys y end

由于 minimum [1,2,3,4,5] ~> helper [2,3,4,5] 1 ~> helper [3,4,5] (Int.min (2, 1)) ~> helper [3,4,5] 1 ~> helper [4,5] (Int.min (3, 1)) ~> helper [4,5] 1 ~> helper [5] (Int.min (4, 1)) ~> helper [5] 1 ~> helper [] (Int.min (5, 1)) ~> helper [] 1 ~> 1 是可交换的,因此您最好使用Int.min而不是foldl来解决此练习,方法与上面完全相同,并且会有一条尾巴-递归变量,使用较少的堆栈空间。