SML,使用foldr定义列表的最小值

SML,使用foldr定义列表的最小值,sml,Sml,我需要使用foldr找到列表的最小值 以下是我编写的代码: fun minlist nil = nil | minlist (x::xs) = List.foldr (fn (y,z) => if y < z then y else z) x xs; fun minlist nil=nil |minlist(x::xs)=List.foldr(fn(y,z)=>如果y无法应用于'a list'类型的参数” 我被困了一段时间。感谢您的帮助。您的第一句话说空列表的最小值是一个列表。

我需要使用foldr找到列表的最小值

以下是我编写的代码:

fun minlist nil = nil
  | minlist (x::xs) = List.foldr (fn (y,z) => if y < z then y else z) x xs;
fun minlist nil=nil
|minlist(x::xs)=List.foldr(fn(y,z)=>如果y
但是,我得到一个错误:“重载>无法应用于'a list'类型的参数”


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

您的第一句话说空列表的最小值是一个列表。
因此,
(fn(y,z)=>如果y
生成一个列表,并且
y
z
也必须是列表

对于空列表,您无法生成合理的值,因此您应该删除该情况并接受编译警告,或者引发异常。

查找两个值中的最小值 表达式
如果y
具有内置名称
Int.min(y,z)

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

正如molbdnilo所说,您可以接受编译警告(如果您向函数提供空列表,则可能在运行时引发
Match
异常),或者在给定空列表时引发特定异常,例如
empty
。两者都不好,但后者至少能让问题变得清楚

在不使用foldr的情况下编写此文件可能看起来像:

fun minimum [] = raise Empty
  | minimum [x] = x
  | minimum (x::xs) = Int.min (x, minimum xs)
fun minimum [] = NONE
  | minimum [x] = SOME x
  | minimum (x::xs) =
    case minimum xs of
         NONE => SOME x
       | SOME y => SOME (Int.min (x, y))
将递归函数转换为折叠函数 给定某个递归函数
foo
,该函数依赖于某个函数
bar
和某个默认值
acc

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

  • acc
    x
    ,是一些最小值
  • bar
    Int.min
这是对
最小值
递归方案的一次尝试

给定函数foldr

fun foldr f e []      = e
  | foldr f e (x::xs) = f (x, foldr f e xs);
fun minimum [] = ...
  | minimum (x::xs) = foldr ...
您可能会注意到相同的相似之处:

  • f
    ,但被制成一个参数
  • e
    acc
    ,但被制成一个参数
minimum
中唯一不适合此通用递归方案的是处理空列表。因此,您仍然必须将其与
foldr
分开:

fun foldr f e []      = e
  | foldr f e (x::xs) = f (x, foldr f e xs);
fun minimum [] = ...
  | minimum (x::xs) = foldr ...
但其余的情况类似

错误感知返回类型 第三种选择是将函数的类型签名更改为

val minimum : int list -> int option
你目前的练习可能不允许这样做

在不使用foldr的情况下编写此文件可能看起来像:

fun minimum [] = raise Empty
  | minimum [x] = x
  | minimum (x::xs) = Int.min (x, minimum xs)
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 [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
   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
由于在递归调用返回之前无法计算outer
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]
~> 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
   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
而不是
foldr
来解决此练习,方法与上面的方法完全相同,并且您将有一个使用较少堆栈空间的尾部递归变量